Uncommited changes synced
[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         var box = {
10386             tag: 'div',
10387             cn: [
10388                 {
10389                     tag: 'input',
10390                     type : 'hidden',
10391                     cls: 'form-hidden-field'
10392                 },
10393                 inputblock
10394             ]
10395             
10396         };
10397         
10398         if(this.multiple){
10399             box = {
10400                 tag: 'div',
10401                 cn: [
10402                     {
10403                         tag: 'input',
10404                         type : 'hidden',
10405                         cls: 'form-hidden-field'
10406                     },
10407                     {
10408                         tag: 'ul',
10409                         cls: 'roo-select2-choices',
10410                         cn:[
10411                             {
10412                                 tag: 'li',
10413                                 cls: 'roo-select2-search-field',
10414                                 cn: [
10415
10416                                     inputblock
10417                                 ]
10418                             }
10419                         ]
10420                     }
10421                 ]
10422             }
10423         };
10424         
10425         var combobox = {
10426             cls: 'roo-select2-container input-group',
10427             cn: [
10428                 box
10429 //                {
10430 //                    tag: 'ul',
10431 //                    cls: 'typeahead typeahead-long dropdown-menu',
10432 //                    style: 'display:none'
10433 //                }
10434             ]
10435         };
10436         
10437         if(!this.multiple && this.showToggleBtn){
10438             
10439             var caret = {
10440                         tag: 'span',
10441                         cls: 'caret'
10442              };
10443             if (this.caret != false) {
10444                 caret = {
10445                      tag: 'i',
10446                      cls: 'fa fa-' + this.caret
10447                 };
10448                 
10449             }
10450             
10451             combobox.cn.push({
10452                 tag :'span',
10453                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10454                 cn : [
10455                     caret,
10456                     {
10457                         tag: 'span',
10458                         cls: 'combobox-clear',
10459                         cn  : [
10460                             {
10461                                 tag : 'i',
10462                                 cls: 'icon-remove'
10463                             }
10464                         ]
10465                     }
10466                 ]
10467
10468             })
10469         }
10470         
10471         if(this.multiple){
10472             combobox.cls += ' roo-select2-container-multi';
10473         }
10474          var indicator = {
10475             tag : 'i',
10476             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10477             tooltip : 'This field is required'
10478         };
10479         if (Roo.bootstrap.version == 4) {
10480             indicator = {
10481                 tag : 'i',
10482                 style : 'display:none'
10483             };
10484         }
10485         
10486         
10487         if (align ==='left' && this.fieldLabel.length) {
10488             
10489             cfg.cls += ' roo-form-group-label-left row';
10490
10491             cfg.cn = [
10492                 indicator,
10493                 {
10494                     tag: 'label',
10495                     'for' :  id,
10496                     cls : 'control-label',
10497                     html : this.fieldLabel
10498
10499                 },
10500                 {
10501                     cls : "", 
10502                     cn: [
10503                         combobox
10504                     ]
10505                 }
10506
10507             ];
10508             
10509             var labelCfg = cfg.cn[1];
10510             var contentCfg = cfg.cn[2];
10511             
10512             if(this.indicatorpos == 'right'){
10513                 cfg.cn = [
10514                     {
10515                         tag: 'label',
10516                         'for' :  id,
10517                         cls : 'control-label',
10518                         cn : [
10519                             {
10520                                 tag : 'span',
10521                                 html : this.fieldLabel
10522                             },
10523                             indicator
10524                         ]
10525                     },
10526                     {
10527                         cls : "", 
10528                         cn: [
10529                             combobox
10530                         ]
10531                     }
10532
10533                 ];
10534                 
10535                 labelCfg = cfg.cn[0];
10536                 contentCfg = cfg.cn[1];
10537             }
10538             
10539             if(this.labelWidth > 12){
10540                 labelCfg.style = "width: " + this.labelWidth + 'px';
10541             }
10542             
10543             if(this.labelWidth < 13 && this.labelmd == 0){
10544                 this.labelmd = this.labelWidth;
10545             }
10546             
10547             if(this.labellg > 0){
10548                 labelCfg.cls += ' col-lg-' + this.labellg;
10549                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10550             }
10551             
10552             if(this.labelmd > 0){
10553                 labelCfg.cls += ' col-md-' + this.labelmd;
10554                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10555             }
10556             
10557             if(this.labelsm > 0){
10558                 labelCfg.cls += ' col-sm-' + this.labelsm;
10559                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10560             }
10561             
10562             if(this.labelxs > 0){
10563                 labelCfg.cls += ' col-xs-' + this.labelxs;
10564                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10565             }
10566             
10567         } else if ( this.fieldLabel.length) {
10568 //                Roo.log(" label");
10569             cfg.cn = [
10570                 indicator,
10571                {
10572                    tag: 'label',
10573                    //cls : 'input-group-addon',
10574                    html : this.fieldLabel
10575
10576                },
10577
10578                combobox
10579
10580             ];
10581             
10582             if(this.indicatorpos == 'right'){
10583                 
10584                 cfg.cn = [
10585                     {
10586                        tag: 'label',
10587                        cn : [
10588                            {
10589                                tag : 'span',
10590                                html : this.fieldLabel
10591                            },
10592                            indicator
10593                        ]
10594
10595                     },
10596                     combobox
10597
10598                 ];
10599
10600             }
10601
10602         } else {
10603             
10604 //                Roo.log(" no label && no align");
10605                 cfg = combobox
10606                      
10607                 
10608         }
10609         
10610         var settings=this;
10611         ['xs','sm','md','lg'].map(function(size){
10612             if (settings[size]) {
10613                 cfg.cls += ' col-' + size + '-' + settings[size];
10614             }
10615         });
10616         
10617         return cfg;
10618         
10619     },
10620     
10621     
10622     
10623     // private
10624     onResize : function(w, h){
10625 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10626 //        if(typeof w == 'number'){
10627 //            var x = w - this.trigger.getWidth();
10628 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10629 //            this.trigger.setStyle('left', x+'px');
10630 //        }
10631     },
10632
10633     // private
10634     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10635
10636     // private
10637     getResizeEl : function(){
10638         return this.inputEl();
10639     },
10640
10641     // private
10642     getPositionEl : function(){
10643         return this.inputEl();
10644     },
10645
10646     // private
10647     alignErrorIcon : function(){
10648         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10649     },
10650
10651     // private
10652     initEvents : function(){
10653         
10654         this.createList();
10655         
10656         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10657         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10658         if(!this.multiple && this.showToggleBtn){
10659             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10660             if(this.hideTrigger){
10661                 this.trigger.setDisplayed(false);
10662             }
10663             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10664         }
10665         
10666         if(this.multiple){
10667             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10668         }
10669         
10670         if(this.removable && !this.editable && !this.tickable){
10671             var close = this.closeTriggerEl();
10672             
10673             if(close){
10674                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10675                 close.on('click', this.removeBtnClick, this, close);
10676             }
10677         }
10678         
10679         //this.trigger.addClassOnOver('x-form-trigger-over');
10680         //this.trigger.addClassOnClick('x-form-trigger-click');
10681         
10682         //if(!this.width){
10683         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10684         //}
10685     },
10686     
10687     closeTriggerEl : function()
10688     {
10689         var close = this.el.select('.roo-combo-removable-btn', true).first();
10690         return close ? close : false;
10691     },
10692     
10693     removeBtnClick : function(e, h, el)
10694     {
10695         e.preventDefault();
10696         
10697         if(this.fireEvent("remove", this) !== false){
10698             this.reset();
10699             this.fireEvent("afterremove", this)
10700         }
10701     },
10702     
10703     createList : function()
10704     {
10705         this.list = Roo.get(document.body).createChild({
10706             tag: 'ul',
10707             cls: 'typeahead typeahead-long dropdown-menu',
10708             style: 'display:none'
10709         });
10710         
10711         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10712         
10713     },
10714
10715     // private
10716     initTrigger : function(){
10717        
10718     },
10719
10720     // private
10721     onDestroy : function(){
10722         if(this.trigger){
10723             this.trigger.removeAllListeners();
10724           //  this.trigger.remove();
10725         }
10726         //if(this.wrap){
10727         //    this.wrap.remove();
10728         //}
10729         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10730     },
10731
10732     // private
10733     onFocus : function(){
10734         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10735         /*
10736         if(!this.mimicing){
10737             this.wrap.addClass('x-trigger-wrap-focus');
10738             this.mimicing = true;
10739             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10740             if(this.monitorTab){
10741                 this.el.on("keydown", this.checkTab, this);
10742             }
10743         }
10744         */
10745     },
10746
10747     // private
10748     checkTab : function(e){
10749         if(e.getKey() == e.TAB){
10750             this.triggerBlur();
10751         }
10752     },
10753
10754     // private
10755     onBlur : function(){
10756         // do nothing
10757     },
10758
10759     // private
10760     mimicBlur : function(e, t){
10761         /*
10762         if(!this.wrap.contains(t) && this.validateBlur()){
10763             this.triggerBlur();
10764         }
10765         */
10766     },
10767
10768     // private
10769     triggerBlur : function(){
10770         this.mimicing = false;
10771         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10772         if(this.monitorTab){
10773             this.el.un("keydown", this.checkTab, this);
10774         }
10775         //this.wrap.removeClass('x-trigger-wrap-focus');
10776         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10777     },
10778
10779     // private
10780     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10781     validateBlur : function(e, t){
10782         return true;
10783     },
10784
10785     // private
10786     onDisable : function(){
10787         this.inputEl().dom.disabled = true;
10788         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10789         //if(this.wrap){
10790         //    this.wrap.addClass('x-item-disabled');
10791         //}
10792     },
10793
10794     // private
10795     onEnable : function(){
10796         this.inputEl().dom.disabled = false;
10797         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10798         //if(this.wrap){
10799         //    this.el.removeClass('x-item-disabled');
10800         //}
10801     },
10802
10803     // private
10804     onShow : function(){
10805         var ae = this.getActionEl();
10806         
10807         if(ae){
10808             ae.dom.style.display = '';
10809             ae.dom.style.visibility = 'visible';
10810         }
10811     },
10812
10813     // private
10814     
10815     onHide : function(){
10816         var ae = this.getActionEl();
10817         ae.dom.style.display = 'none';
10818     },
10819
10820     /**
10821      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10822      * by an implementing function.
10823      * @method
10824      * @param {EventObject} e
10825      */
10826     onTriggerClick : Roo.emptyFn
10827 });
10828  /*
10829  * Based on:
10830  * Ext JS Library 1.1.1
10831  * Copyright(c) 2006-2007, Ext JS, LLC.
10832  *
10833  * Originally Released Under LGPL - original licence link has changed is not relivant.
10834  *
10835  * Fork - LGPL
10836  * <script type="text/javascript">
10837  */
10838
10839
10840 /**
10841  * @class Roo.data.SortTypes
10842  * @singleton
10843  * Defines the default sorting (casting?) comparison functions used when sorting data.
10844  */
10845 Roo.data.SortTypes = {
10846     /**
10847      * Default sort that does nothing
10848      * @param {Mixed} s The value being converted
10849      * @return {Mixed} The comparison value
10850      */
10851     none : function(s){
10852         return s;
10853     },
10854     
10855     /**
10856      * The regular expression used to strip tags
10857      * @type {RegExp}
10858      * @property
10859      */
10860     stripTagsRE : /<\/?[^>]+>/gi,
10861     
10862     /**
10863      * Strips all HTML tags to sort on text only
10864      * @param {Mixed} s The value being converted
10865      * @return {String} The comparison value
10866      */
10867     asText : function(s){
10868         return String(s).replace(this.stripTagsRE, "");
10869     },
10870     
10871     /**
10872      * Strips all HTML tags to sort on text only - Case insensitive
10873      * @param {Mixed} s The value being converted
10874      * @return {String} The comparison value
10875      */
10876     asUCText : function(s){
10877         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10878     },
10879     
10880     /**
10881      * Case insensitive string
10882      * @param {Mixed} s The value being converted
10883      * @return {String} The comparison value
10884      */
10885     asUCString : function(s) {
10886         return String(s).toUpperCase();
10887     },
10888     
10889     /**
10890      * Date sorting
10891      * @param {Mixed} s The value being converted
10892      * @return {Number} The comparison value
10893      */
10894     asDate : function(s) {
10895         if(!s){
10896             return 0;
10897         }
10898         if(s instanceof Date){
10899             return s.getTime();
10900         }
10901         return Date.parse(String(s));
10902     },
10903     
10904     /**
10905      * Float sorting
10906      * @param {Mixed} s The value being converted
10907      * @return {Float} The comparison value
10908      */
10909     asFloat : function(s) {
10910         var val = parseFloat(String(s).replace(/,/g, ""));
10911         if(isNaN(val)) {
10912             val = 0;
10913         }
10914         return val;
10915     },
10916     
10917     /**
10918      * Integer sorting
10919      * @param {Mixed} s The value being converted
10920      * @return {Number} The comparison value
10921      */
10922     asInt : function(s) {
10923         var val = parseInt(String(s).replace(/,/g, ""));
10924         if(isNaN(val)) {
10925             val = 0;
10926         }
10927         return val;
10928     }
10929 };/*
10930  * Based on:
10931  * Ext JS Library 1.1.1
10932  * Copyright(c) 2006-2007, Ext JS, LLC.
10933  *
10934  * Originally Released Under LGPL - original licence link has changed is not relivant.
10935  *
10936  * Fork - LGPL
10937  * <script type="text/javascript">
10938  */
10939
10940 /**
10941 * @class Roo.data.Record
10942  * Instances of this class encapsulate both record <em>definition</em> information, and record
10943  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10944  * to access Records cached in an {@link Roo.data.Store} object.<br>
10945  * <p>
10946  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10947  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10948  * objects.<br>
10949  * <p>
10950  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10951  * @constructor
10952  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10953  * {@link #create}. The parameters are the same.
10954  * @param {Array} data An associative Array of data values keyed by the field name.
10955  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10956  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10957  * not specified an integer id is generated.
10958  */
10959 Roo.data.Record = function(data, id){
10960     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10961     this.data = data;
10962 };
10963
10964 /**
10965  * Generate a constructor for a specific record layout.
10966  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10967  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10968  * Each field definition object may contain the following properties: <ul>
10969  * <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,
10970  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10971  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10972  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10973  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10974  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10975  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10976  * this may be omitted.</p></li>
10977  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10978  * <ul><li>auto (Default, implies no conversion)</li>
10979  * <li>string</li>
10980  * <li>int</li>
10981  * <li>float</li>
10982  * <li>boolean</li>
10983  * <li>date</li></ul></p></li>
10984  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10985  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10986  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10987  * by the Reader into an object that will be stored in the Record. It is passed the
10988  * following parameters:<ul>
10989  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10990  * </ul></p></li>
10991  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10992  * </ul>
10993  * <br>usage:<br><pre><code>
10994 var TopicRecord = Roo.data.Record.create(
10995     {name: 'title', mapping: 'topic_title'},
10996     {name: 'author', mapping: 'username'},
10997     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10998     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10999     {name: 'lastPoster', mapping: 'user2'},
11000     {name: 'excerpt', mapping: 'post_text'}
11001 );
11002
11003 var myNewRecord = new TopicRecord({
11004     title: 'Do my job please',
11005     author: 'noobie',
11006     totalPosts: 1,
11007     lastPost: new Date(),
11008     lastPoster: 'Animal',
11009     excerpt: 'No way dude!'
11010 });
11011 myStore.add(myNewRecord);
11012 </code></pre>
11013  * @method create
11014  * @static
11015  */
11016 Roo.data.Record.create = function(o){
11017     var f = function(){
11018         f.superclass.constructor.apply(this, arguments);
11019     };
11020     Roo.extend(f, Roo.data.Record);
11021     var p = f.prototype;
11022     p.fields = new Roo.util.MixedCollection(false, function(field){
11023         return field.name;
11024     });
11025     for(var i = 0, len = o.length; i < len; i++){
11026         p.fields.add(new Roo.data.Field(o[i]));
11027     }
11028     f.getField = function(name){
11029         return p.fields.get(name);  
11030     };
11031     return f;
11032 };
11033
11034 Roo.data.Record.AUTO_ID = 1000;
11035 Roo.data.Record.EDIT = 'edit';
11036 Roo.data.Record.REJECT = 'reject';
11037 Roo.data.Record.COMMIT = 'commit';
11038
11039 Roo.data.Record.prototype = {
11040     /**
11041      * Readonly flag - true if this record has been modified.
11042      * @type Boolean
11043      */
11044     dirty : false,
11045     editing : false,
11046     error: null,
11047     modified: null,
11048
11049     // private
11050     join : function(store){
11051         this.store = store;
11052     },
11053
11054     /**
11055      * Set the named field to the specified value.
11056      * @param {String} name The name of the field to set.
11057      * @param {Object} value The value to set the field to.
11058      */
11059     set : function(name, value){
11060         if(this.data[name] == value){
11061             return;
11062         }
11063         this.dirty = true;
11064         if(!this.modified){
11065             this.modified = {};
11066         }
11067         if(typeof this.modified[name] == 'undefined'){
11068             this.modified[name] = this.data[name];
11069         }
11070         this.data[name] = value;
11071         if(!this.editing && this.store){
11072             this.store.afterEdit(this);
11073         }       
11074     },
11075
11076     /**
11077      * Get the value of the named field.
11078      * @param {String} name The name of the field to get the value of.
11079      * @return {Object} The value of the field.
11080      */
11081     get : function(name){
11082         return this.data[name]; 
11083     },
11084
11085     // private
11086     beginEdit : function(){
11087         this.editing = true;
11088         this.modified = {}; 
11089     },
11090
11091     // private
11092     cancelEdit : function(){
11093         this.editing = false;
11094         delete this.modified;
11095     },
11096
11097     // private
11098     endEdit : function(){
11099         this.editing = false;
11100         if(this.dirty && this.store){
11101             this.store.afterEdit(this);
11102         }
11103     },
11104
11105     /**
11106      * Usually called by the {@link Roo.data.Store} which owns the Record.
11107      * Rejects all changes made to the Record since either creation, or the last commit operation.
11108      * Modified fields are reverted to their original values.
11109      * <p>
11110      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11111      * of reject operations.
11112      */
11113     reject : function(){
11114         var m = this.modified;
11115         for(var n in m){
11116             if(typeof m[n] != "function"){
11117                 this.data[n] = m[n];
11118             }
11119         }
11120         this.dirty = false;
11121         delete this.modified;
11122         this.editing = false;
11123         if(this.store){
11124             this.store.afterReject(this);
11125         }
11126     },
11127
11128     /**
11129      * Usually called by the {@link Roo.data.Store} which owns the Record.
11130      * Commits all changes made to the Record since either creation, or the last commit operation.
11131      * <p>
11132      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11133      * of commit operations.
11134      */
11135     commit : function(){
11136         this.dirty = false;
11137         delete this.modified;
11138         this.editing = false;
11139         if(this.store){
11140             this.store.afterCommit(this);
11141         }
11142     },
11143
11144     // private
11145     hasError : function(){
11146         return this.error != null;
11147     },
11148
11149     // private
11150     clearError : function(){
11151         this.error = null;
11152     },
11153
11154     /**
11155      * Creates a copy of this record.
11156      * @param {String} id (optional) A new record id if you don't want to use this record's id
11157      * @return {Record}
11158      */
11159     copy : function(newId) {
11160         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11161     }
11162 };/*
11163  * Based on:
11164  * Ext JS Library 1.1.1
11165  * Copyright(c) 2006-2007, Ext JS, LLC.
11166  *
11167  * Originally Released Under LGPL - original licence link has changed is not relivant.
11168  *
11169  * Fork - LGPL
11170  * <script type="text/javascript">
11171  */
11172
11173
11174
11175 /**
11176  * @class Roo.data.Store
11177  * @extends Roo.util.Observable
11178  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11179  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11180  * <p>
11181  * 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
11182  * has no knowledge of the format of the data returned by the Proxy.<br>
11183  * <p>
11184  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11185  * instances from the data object. These records are cached and made available through accessor functions.
11186  * @constructor
11187  * Creates a new Store.
11188  * @param {Object} config A config object containing the objects needed for the Store to access data,
11189  * and read the data into Records.
11190  */
11191 Roo.data.Store = function(config){
11192     this.data = new Roo.util.MixedCollection(false);
11193     this.data.getKey = function(o){
11194         return o.id;
11195     };
11196     this.baseParams = {};
11197     // private
11198     this.paramNames = {
11199         "start" : "start",
11200         "limit" : "limit",
11201         "sort" : "sort",
11202         "dir" : "dir",
11203         "multisort" : "_multisort"
11204     };
11205
11206     if(config && config.data){
11207         this.inlineData = config.data;
11208         delete config.data;
11209     }
11210
11211     Roo.apply(this, config);
11212     
11213     if(this.reader){ // reader passed
11214         this.reader = Roo.factory(this.reader, Roo.data);
11215         this.reader.xmodule = this.xmodule || false;
11216         if(!this.recordType){
11217             this.recordType = this.reader.recordType;
11218         }
11219         if(this.reader.onMetaChange){
11220             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11221         }
11222     }
11223
11224     if(this.recordType){
11225         this.fields = this.recordType.prototype.fields;
11226     }
11227     this.modified = [];
11228
11229     this.addEvents({
11230         /**
11231          * @event datachanged
11232          * Fires when the data cache has changed, and a widget which is using this Store
11233          * as a Record cache should refresh its view.
11234          * @param {Store} this
11235          */
11236         datachanged : true,
11237         /**
11238          * @event metachange
11239          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11240          * @param {Store} this
11241          * @param {Object} meta The JSON metadata
11242          */
11243         metachange : true,
11244         /**
11245          * @event add
11246          * Fires when Records have been added to the Store
11247          * @param {Store} this
11248          * @param {Roo.data.Record[]} records The array of Records added
11249          * @param {Number} index The index at which the record(s) were added
11250          */
11251         add : true,
11252         /**
11253          * @event remove
11254          * Fires when a Record has been removed from the Store
11255          * @param {Store} this
11256          * @param {Roo.data.Record} record The Record that was removed
11257          * @param {Number} index The index at which the record was removed
11258          */
11259         remove : true,
11260         /**
11261          * @event update
11262          * Fires when a Record has been updated
11263          * @param {Store} this
11264          * @param {Roo.data.Record} record The Record that was updated
11265          * @param {String} operation The update operation being performed.  Value may be one of:
11266          * <pre><code>
11267  Roo.data.Record.EDIT
11268  Roo.data.Record.REJECT
11269  Roo.data.Record.COMMIT
11270          * </code></pre>
11271          */
11272         update : true,
11273         /**
11274          * @event clear
11275          * Fires when the data cache has been cleared.
11276          * @param {Store} this
11277          */
11278         clear : true,
11279         /**
11280          * @event beforeload
11281          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11282          * the load action will be canceled.
11283          * @param {Store} this
11284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11285          */
11286         beforeload : true,
11287         /**
11288          * @event beforeloadadd
11289          * Fires after a new set of Records has been loaded.
11290          * @param {Store} this
11291          * @param {Roo.data.Record[]} records The Records that were loaded
11292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11293          */
11294         beforeloadadd : true,
11295         /**
11296          * @event load
11297          * Fires after a new set of Records has been loaded, before they are added to the store.
11298          * @param {Store} this
11299          * @param {Roo.data.Record[]} records The Records that were loaded
11300          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11301          * @params {Object} return from reader
11302          */
11303         load : true,
11304         /**
11305          * @event loadexception
11306          * Fires if an exception occurs in the Proxy during loading.
11307          * Called with the signature of the Proxy's "loadexception" event.
11308          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11309          * 
11310          * @param {Proxy} 
11311          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11312          * @param {Object} load options 
11313          * @param {Object} jsonData from your request (normally this contains the Exception)
11314          */
11315         loadexception : true
11316     });
11317     
11318     if(this.proxy){
11319         this.proxy = Roo.factory(this.proxy, Roo.data);
11320         this.proxy.xmodule = this.xmodule || false;
11321         this.relayEvents(this.proxy,  ["loadexception"]);
11322     }
11323     this.sortToggle = {};
11324     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11325
11326     Roo.data.Store.superclass.constructor.call(this);
11327
11328     if(this.inlineData){
11329         this.loadData(this.inlineData);
11330         delete this.inlineData;
11331     }
11332 };
11333
11334 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11335      /**
11336     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11337     * without a remote query - used by combo/forms at present.
11338     */
11339     
11340     /**
11341     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11342     */
11343     /**
11344     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11345     */
11346     /**
11347     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11348     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11349     */
11350     /**
11351     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11352     * on any HTTP request
11353     */
11354     /**
11355     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11356     */
11357     /**
11358     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11359     */
11360     multiSort: false,
11361     /**
11362     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11363     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11364     */
11365     remoteSort : false,
11366
11367     /**
11368     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11369      * loaded or when a record is removed. (defaults to false).
11370     */
11371     pruneModifiedRecords : false,
11372
11373     // private
11374     lastOptions : null,
11375
11376     /**
11377      * Add Records to the Store and fires the add event.
11378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11379      */
11380     add : function(records){
11381         records = [].concat(records);
11382         for(var i = 0, len = records.length; i < len; i++){
11383             records[i].join(this);
11384         }
11385         var index = this.data.length;
11386         this.data.addAll(records);
11387         this.fireEvent("add", this, records, index);
11388     },
11389
11390     /**
11391      * Remove a Record from the Store and fires the remove event.
11392      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11393      */
11394     remove : function(record){
11395         var index = this.data.indexOf(record);
11396         this.data.removeAt(index);
11397  
11398         if(this.pruneModifiedRecords){
11399             this.modified.remove(record);
11400         }
11401         this.fireEvent("remove", this, record, index);
11402     },
11403
11404     /**
11405      * Remove all Records from the Store and fires the clear event.
11406      */
11407     removeAll : function(){
11408         this.data.clear();
11409         if(this.pruneModifiedRecords){
11410             this.modified = [];
11411         }
11412         this.fireEvent("clear", this);
11413     },
11414
11415     /**
11416      * Inserts Records to the Store at the given index and fires the add event.
11417      * @param {Number} index The start index at which to insert the passed Records.
11418      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11419      */
11420     insert : function(index, records){
11421         records = [].concat(records);
11422         for(var i = 0, len = records.length; i < len; i++){
11423             this.data.insert(index, records[i]);
11424             records[i].join(this);
11425         }
11426         this.fireEvent("add", this, records, index);
11427     },
11428
11429     /**
11430      * Get the index within the cache of the passed Record.
11431      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11432      * @return {Number} The index of the passed Record. Returns -1 if not found.
11433      */
11434     indexOf : function(record){
11435         return this.data.indexOf(record);
11436     },
11437
11438     /**
11439      * Get the index within the cache of the Record with the passed id.
11440      * @param {String} id The id of the Record to find.
11441      * @return {Number} The index of the Record. Returns -1 if not found.
11442      */
11443     indexOfId : function(id){
11444         return this.data.indexOfKey(id);
11445     },
11446
11447     /**
11448      * Get the Record with the specified id.
11449      * @param {String} id The id of the Record to find.
11450      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11451      */
11452     getById : function(id){
11453         return this.data.key(id);
11454     },
11455
11456     /**
11457      * Get the Record at the specified index.
11458      * @param {Number} index The index of the Record to find.
11459      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11460      */
11461     getAt : function(index){
11462         return this.data.itemAt(index);
11463     },
11464
11465     /**
11466      * Returns a range of Records between specified indices.
11467      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11468      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11469      * @return {Roo.data.Record[]} An array of Records
11470      */
11471     getRange : function(start, end){
11472         return this.data.getRange(start, end);
11473     },
11474
11475     // private
11476     storeOptions : function(o){
11477         o = Roo.apply({}, o);
11478         delete o.callback;
11479         delete o.scope;
11480         this.lastOptions = o;
11481     },
11482
11483     /**
11484      * Loads the Record cache from the configured Proxy using the configured Reader.
11485      * <p>
11486      * If using remote paging, then the first load call must specify the <em>start</em>
11487      * and <em>limit</em> properties in the options.params property to establish the initial
11488      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11489      * <p>
11490      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11491      * and this call will return before the new data has been loaded. Perform any post-processing
11492      * in a callback function, or in a "load" event handler.</strong>
11493      * <p>
11494      * @param {Object} options An object containing properties which control loading options:<ul>
11495      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11496      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11497      * passed the following arguments:<ul>
11498      * <li>r : Roo.data.Record[]</li>
11499      * <li>options: Options object from the load call</li>
11500      * <li>success: Boolean success indicator</li></ul></li>
11501      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11502      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11503      * </ul>
11504      */
11505     load : function(options){
11506         options = options || {};
11507         if(this.fireEvent("beforeload", this, options) !== false){
11508             this.storeOptions(options);
11509             var p = Roo.apply(options.params || {}, this.baseParams);
11510             // if meta was not loaded from remote source.. try requesting it.
11511             if (!this.reader.metaFromRemote) {
11512                 p._requestMeta = 1;
11513             }
11514             if(this.sortInfo && this.remoteSort){
11515                 var pn = this.paramNames;
11516                 p[pn["sort"]] = this.sortInfo.field;
11517                 p[pn["dir"]] = this.sortInfo.direction;
11518             }
11519             if (this.multiSort) {
11520                 var pn = this.paramNames;
11521                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11522             }
11523             
11524             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11525         }
11526     },
11527
11528     /**
11529      * Reloads the Record cache from the configured Proxy using the configured Reader and
11530      * the options from the last load operation performed.
11531      * @param {Object} options (optional) An object containing properties which may override the options
11532      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11533      * the most recently used options are reused).
11534      */
11535     reload : function(options){
11536         this.load(Roo.applyIf(options||{}, this.lastOptions));
11537     },
11538
11539     // private
11540     // Called as a callback by the Reader during a load operation.
11541     loadRecords : function(o, options, success){
11542         if(!o || success === false){
11543             if(success !== false){
11544                 this.fireEvent("load", this, [], options, o);
11545             }
11546             if(options.callback){
11547                 options.callback.call(options.scope || this, [], options, false);
11548             }
11549             return;
11550         }
11551         // if data returned failure - throw an exception.
11552         if (o.success === false) {
11553             // show a message if no listener is registered.
11554             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11555                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11556             }
11557             // loadmask wil be hooked into this..
11558             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11559             return;
11560         }
11561         var r = o.records, t = o.totalRecords || r.length;
11562         
11563         this.fireEvent("beforeloadadd", this, r, options, o);
11564         
11565         if(!options || options.add !== true){
11566             if(this.pruneModifiedRecords){
11567                 this.modified = [];
11568             }
11569             for(var i = 0, len = r.length; i < len; i++){
11570                 r[i].join(this);
11571             }
11572             if(this.snapshot){
11573                 this.data = this.snapshot;
11574                 delete this.snapshot;
11575             }
11576             this.data.clear();
11577             this.data.addAll(r);
11578             this.totalLength = t;
11579             this.applySort();
11580             this.fireEvent("datachanged", this);
11581         }else{
11582             this.totalLength = Math.max(t, this.data.length+r.length);
11583             this.add(r);
11584         }
11585         
11586         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11587                 
11588             var e = new Roo.data.Record({});
11589
11590             e.set(this.parent.displayField, this.parent.emptyTitle);
11591             e.set(this.parent.valueField, '');
11592
11593             this.insert(0, e);
11594         }
11595             
11596         this.fireEvent("load", this, r, options, o);
11597         if(options.callback){
11598             options.callback.call(options.scope || this, r, options, true);
11599         }
11600     },
11601
11602
11603     /**
11604      * Loads data from a passed data block. A Reader which understands the format of the data
11605      * must have been configured in the constructor.
11606      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11607      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11608      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11609      */
11610     loadData : function(o, append){
11611         var r = this.reader.readRecords(o);
11612         this.loadRecords(r, {add: append}, true);
11613     },
11614
11615     /**
11616      * Gets the number of cached records.
11617      * <p>
11618      * <em>If using paging, this may not be the total size of the dataset. If the data object
11619      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11620      * the data set size</em>
11621      */
11622     getCount : function(){
11623         return this.data.length || 0;
11624     },
11625
11626     /**
11627      * Gets the total number of records in the dataset as returned by the server.
11628      * <p>
11629      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11630      * the dataset size</em>
11631      */
11632     getTotalCount : function(){
11633         return this.totalLength || 0;
11634     },
11635
11636     /**
11637      * Returns the sort state of the Store as an object with two properties:
11638      * <pre><code>
11639  field {String} The name of the field by which the Records are sorted
11640  direction {String} The sort order, "ASC" or "DESC"
11641      * </code></pre>
11642      */
11643     getSortState : function(){
11644         return this.sortInfo;
11645     },
11646
11647     // private
11648     applySort : function(){
11649         if(this.sortInfo && !this.remoteSort){
11650             var s = this.sortInfo, f = s.field;
11651             var st = this.fields.get(f).sortType;
11652             var fn = function(r1, r2){
11653                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11654                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11655             };
11656             this.data.sort(s.direction, fn);
11657             if(this.snapshot && this.snapshot != this.data){
11658                 this.snapshot.sort(s.direction, fn);
11659             }
11660         }
11661     },
11662
11663     /**
11664      * Sets the default sort column and order to be used by the next load operation.
11665      * @param {String} fieldName The name of the field to sort by.
11666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11667      */
11668     setDefaultSort : function(field, dir){
11669         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11670     },
11671
11672     /**
11673      * Sort the Records.
11674      * If remote sorting is used, the sort is performed on the server, and the cache is
11675      * reloaded. If local sorting is used, the cache is sorted internally.
11676      * @param {String} fieldName The name of the field to sort by.
11677      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11678      */
11679     sort : function(fieldName, dir){
11680         var f = this.fields.get(fieldName);
11681         if(!dir){
11682             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11683             
11684             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11685                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11686             }else{
11687                 dir = f.sortDir;
11688             }
11689         }
11690         this.sortToggle[f.name] = dir;
11691         this.sortInfo = {field: f.name, direction: dir};
11692         if(!this.remoteSort){
11693             this.applySort();
11694             this.fireEvent("datachanged", this);
11695         }else{
11696             this.load(this.lastOptions);
11697         }
11698     },
11699
11700     /**
11701      * Calls the specified function for each of the Records in the cache.
11702      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11703      * Returning <em>false</em> aborts and exits the iteration.
11704      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11705      */
11706     each : function(fn, scope){
11707         this.data.each(fn, scope);
11708     },
11709
11710     /**
11711      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11712      * (e.g., during paging).
11713      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11714      */
11715     getModifiedRecords : function(){
11716         return this.modified;
11717     },
11718
11719     // private
11720     createFilterFn : function(property, value, anyMatch){
11721         if(!value.exec){ // not a regex
11722             value = String(value);
11723             if(value.length == 0){
11724                 return false;
11725             }
11726             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11727         }
11728         return function(r){
11729             return value.test(r.data[property]);
11730         };
11731     },
11732
11733     /**
11734      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11735      * @param {String} property A field on your records
11736      * @param {Number} start The record index to start at (defaults to 0)
11737      * @param {Number} end The last record index to include (defaults to length - 1)
11738      * @return {Number} The sum
11739      */
11740     sum : function(property, start, end){
11741         var rs = this.data.items, v = 0;
11742         start = start || 0;
11743         end = (end || end === 0) ? end : rs.length-1;
11744
11745         for(var i = start; i <= end; i++){
11746             v += (rs[i].data[property] || 0);
11747         }
11748         return v;
11749     },
11750
11751     /**
11752      * Filter the records by a specified property.
11753      * @param {String} field A field on your records
11754      * @param {String/RegExp} value Either a string that the field
11755      * should start with or a RegExp to test against the field
11756      * @param {Boolean} anyMatch True to match any part not just the beginning
11757      */
11758     filter : function(property, value, anyMatch){
11759         var fn = this.createFilterFn(property, value, anyMatch);
11760         return fn ? this.filterBy(fn) : this.clearFilter();
11761     },
11762
11763     /**
11764      * Filter by a function. The specified function will be called with each
11765      * record in this data source. If the function returns true the record is included,
11766      * otherwise it is filtered.
11767      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11768      * @param {Object} scope (optional) The scope of the function (defaults to this)
11769      */
11770     filterBy : function(fn, scope){
11771         this.snapshot = this.snapshot || this.data;
11772         this.data = this.queryBy(fn, scope||this);
11773         this.fireEvent("datachanged", this);
11774     },
11775
11776     /**
11777      * Query the records by a specified property.
11778      * @param {String} field A field on your records
11779      * @param {String/RegExp} value Either a string that the field
11780      * should start with or a RegExp to test against the field
11781      * @param {Boolean} anyMatch True to match any part not just the beginning
11782      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11783      */
11784     query : function(property, value, anyMatch){
11785         var fn = this.createFilterFn(property, value, anyMatch);
11786         return fn ? this.queryBy(fn) : this.data.clone();
11787     },
11788
11789     /**
11790      * Query by a function. The specified function will be called with each
11791      * record in this data source. If the function returns true the record is included
11792      * in the results.
11793      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11794      * @param {Object} scope (optional) The scope of the function (defaults to this)
11795       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11796      **/
11797     queryBy : function(fn, scope){
11798         var data = this.snapshot || this.data;
11799         return data.filterBy(fn, scope||this);
11800     },
11801
11802     /**
11803      * Collects unique values for a particular dataIndex from this store.
11804      * @param {String} dataIndex The property to collect
11805      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11806      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11807      * @return {Array} An array of the unique values
11808      **/
11809     collect : function(dataIndex, allowNull, bypassFilter){
11810         var d = (bypassFilter === true && this.snapshot) ?
11811                 this.snapshot.items : this.data.items;
11812         var v, sv, r = [], l = {};
11813         for(var i = 0, len = d.length; i < len; i++){
11814             v = d[i].data[dataIndex];
11815             sv = String(v);
11816             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11817                 l[sv] = true;
11818                 r[r.length] = v;
11819             }
11820         }
11821         return r;
11822     },
11823
11824     /**
11825      * Revert to a view of the Record cache with no filtering applied.
11826      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11827      */
11828     clearFilter : function(suppressEvent){
11829         if(this.snapshot && this.snapshot != this.data){
11830             this.data = this.snapshot;
11831             delete this.snapshot;
11832             if(suppressEvent !== true){
11833                 this.fireEvent("datachanged", this);
11834             }
11835         }
11836     },
11837
11838     // private
11839     afterEdit : function(record){
11840         if(this.modified.indexOf(record) == -1){
11841             this.modified.push(record);
11842         }
11843         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11844     },
11845     
11846     // private
11847     afterReject : function(record){
11848         this.modified.remove(record);
11849         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11850     },
11851
11852     // private
11853     afterCommit : function(record){
11854         this.modified.remove(record);
11855         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11856     },
11857
11858     /**
11859      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11860      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11861      */
11862     commitChanges : function(){
11863         var m = this.modified.slice(0);
11864         this.modified = [];
11865         for(var i = 0, len = m.length; i < len; i++){
11866             m[i].commit();
11867         }
11868     },
11869
11870     /**
11871      * Cancel outstanding changes on all changed records.
11872      */
11873     rejectChanges : function(){
11874         var m = this.modified.slice(0);
11875         this.modified = [];
11876         for(var i = 0, len = m.length; i < len; i++){
11877             m[i].reject();
11878         }
11879     },
11880
11881     onMetaChange : function(meta, rtype, o){
11882         this.recordType = rtype;
11883         this.fields = rtype.prototype.fields;
11884         delete this.snapshot;
11885         this.sortInfo = meta.sortInfo || this.sortInfo;
11886         this.modified = [];
11887         this.fireEvent('metachange', this, this.reader.meta);
11888     },
11889     
11890     moveIndex : function(data, type)
11891     {
11892         var index = this.indexOf(data);
11893         
11894         var newIndex = index + type;
11895         
11896         this.remove(data);
11897         
11898         this.insert(newIndex, data);
11899         
11900     }
11901 });/*
11902  * Based on:
11903  * Ext JS Library 1.1.1
11904  * Copyright(c) 2006-2007, Ext JS, LLC.
11905  *
11906  * Originally Released Under LGPL - original licence link has changed is not relivant.
11907  *
11908  * Fork - LGPL
11909  * <script type="text/javascript">
11910  */
11911
11912 /**
11913  * @class Roo.data.SimpleStore
11914  * @extends Roo.data.Store
11915  * Small helper class to make creating Stores from Array data easier.
11916  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11917  * @cfg {Array} fields An array of field definition objects, or field name strings.
11918  * @cfg {Array} data The multi-dimensional array of data
11919  * @constructor
11920  * @param {Object} config
11921  */
11922 Roo.data.SimpleStore = function(config){
11923     Roo.data.SimpleStore.superclass.constructor.call(this, {
11924         isLocal : true,
11925         reader: new Roo.data.ArrayReader({
11926                 id: config.id
11927             },
11928             Roo.data.Record.create(config.fields)
11929         ),
11930         proxy : new Roo.data.MemoryProxy(config.data)
11931     });
11932     this.load();
11933 };
11934 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11935  * Based on:
11936  * Ext JS Library 1.1.1
11937  * Copyright(c) 2006-2007, Ext JS, LLC.
11938  *
11939  * Originally Released Under LGPL - original licence link has changed is not relivant.
11940  *
11941  * Fork - LGPL
11942  * <script type="text/javascript">
11943  */
11944
11945 /**
11946 /**
11947  * @extends Roo.data.Store
11948  * @class Roo.data.JsonStore
11949  * Small helper class to make creating Stores for JSON data easier. <br/>
11950 <pre><code>
11951 var store = new Roo.data.JsonStore({
11952     url: 'get-images.php',
11953     root: 'images',
11954     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11955 });
11956 </code></pre>
11957  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11958  * JsonReader and HttpProxy (unless inline data is provided).</b>
11959  * @cfg {Array} fields An array of field definition objects, or field name strings.
11960  * @constructor
11961  * @param {Object} config
11962  */
11963 Roo.data.JsonStore = function(c){
11964     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11965         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11966         reader: new Roo.data.JsonReader(c, c.fields)
11967     }));
11968 };
11969 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11970  * Based on:
11971  * Ext JS Library 1.1.1
11972  * Copyright(c) 2006-2007, Ext JS, LLC.
11973  *
11974  * Originally Released Under LGPL - original licence link has changed is not relivant.
11975  *
11976  * Fork - LGPL
11977  * <script type="text/javascript">
11978  */
11979
11980  
11981 Roo.data.Field = function(config){
11982     if(typeof config == "string"){
11983         config = {name: config};
11984     }
11985     Roo.apply(this, config);
11986     
11987     if(!this.type){
11988         this.type = "auto";
11989     }
11990     
11991     var st = Roo.data.SortTypes;
11992     // named sortTypes are supported, here we look them up
11993     if(typeof this.sortType == "string"){
11994         this.sortType = st[this.sortType];
11995     }
11996     
11997     // set default sortType for strings and dates
11998     if(!this.sortType){
11999         switch(this.type){
12000             case "string":
12001                 this.sortType = st.asUCString;
12002                 break;
12003             case "date":
12004                 this.sortType = st.asDate;
12005                 break;
12006             default:
12007                 this.sortType = st.none;
12008         }
12009     }
12010
12011     // define once
12012     var stripRe = /[\$,%]/g;
12013
12014     // prebuilt conversion function for this field, instead of
12015     // switching every time we're reading a value
12016     if(!this.convert){
12017         var cv, dateFormat = this.dateFormat;
12018         switch(this.type){
12019             case "":
12020             case "auto":
12021             case undefined:
12022                 cv = function(v){ return v; };
12023                 break;
12024             case "string":
12025                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12026                 break;
12027             case "int":
12028                 cv = function(v){
12029                     return v !== undefined && v !== null && v !== '' ?
12030                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12031                     };
12032                 break;
12033             case "float":
12034                 cv = function(v){
12035                     return v !== undefined && v !== null && v !== '' ?
12036                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12037                     };
12038                 break;
12039             case "bool":
12040             case "boolean":
12041                 cv = function(v){ return v === true || v === "true" || v == 1; };
12042                 break;
12043             case "date":
12044                 cv = function(v){
12045                     if(!v){
12046                         return '';
12047                     }
12048                     if(v instanceof Date){
12049                         return v;
12050                     }
12051                     if(dateFormat){
12052                         if(dateFormat == "timestamp"){
12053                             return new Date(v*1000);
12054                         }
12055                         return Date.parseDate(v, dateFormat);
12056                     }
12057                     var parsed = Date.parse(v);
12058                     return parsed ? new Date(parsed) : null;
12059                 };
12060              break;
12061             
12062         }
12063         this.convert = cv;
12064     }
12065 };
12066
12067 Roo.data.Field.prototype = {
12068     dateFormat: null,
12069     defaultValue: "",
12070     mapping: null,
12071     sortType : null,
12072     sortDir : "ASC"
12073 };/*
12074  * Based on:
12075  * Ext JS Library 1.1.1
12076  * Copyright(c) 2006-2007, Ext JS, LLC.
12077  *
12078  * Originally Released Under LGPL - original licence link has changed is not relivant.
12079  *
12080  * Fork - LGPL
12081  * <script type="text/javascript">
12082  */
12083  
12084 // Base class for reading structured data from a data source.  This class is intended to be
12085 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12086
12087 /**
12088  * @class Roo.data.DataReader
12089  * Base class for reading structured data from a data source.  This class is intended to be
12090  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12091  */
12092
12093 Roo.data.DataReader = function(meta, recordType){
12094     
12095     this.meta = meta;
12096     
12097     this.recordType = recordType instanceof Array ? 
12098         Roo.data.Record.create(recordType) : recordType;
12099 };
12100
12101 Roo.data.DataReader.prototype = {
12102      /**
12103      * Create an empty record
12104      * @param {Object} data (optional) - overlay some values
12105      * @return {Roo.data.Record} record created.
12106      */
12107     newRow :  function(d) {
12108         var da =  {};
12109         this.recordType.prototype.fields.each(function(c) {
12110             switch( c.type) {
12111                 case 'int' : da[c.name] = 0; break;
12112                 case 'date' : da[c.name] = new Date(); break;
12113                 case 'float' : da[c.name] = 0.0; break;
12114                 case 'boolean' : da[c.name] = false; break;
12115                 default : da[c.name] = ""; break;
12116             }
12117             
12118         });
12119         return new this.recordType(Roo.apply(da, d));
12120     }
12121     
12122 };/*
12123  * Based on:
12124  * Ext JS Library 1.1.1
12125  * Copyright(c) 2006-2007, Ext JS, LLC.
12126  *
12127  * Originally Released Under LGPL - original licence link has changed is not relivant.
12128  *
12129  * Fork - LGPL
12130  * <script type="text/javascript">
12131  */
12132
12133 /**
12134  * @class Roo.data.DataProxy
12135  * @extends Roo.data.Observable
12136  * This class is an abstract base class for implementations which provide retrieval of
12137  * unformatted data objects.<br>
12138  * <p>
12139  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12140  * (of the appropriate type which knows how to parse the data object) to provide a block of
12141  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12142  * <p>
12143  * Custom implementations must implement the load method as described in
12144  * {@link Roo.data.HttpProxy#load}.
12145  */
12146 Roo.data.DataProxy = function(){
12147     this.addEvents({
12148         /**
12149          * @event beforeload
12150          * Fires before a network request is made to retrieve a data object.
12151          * @param {Object} This DataProxy object.
12152          * @param {Object} params The params parameter to the load function.
12153          */
12154         beforeload : true,
12155         /**
12156          * @event load
12157          * Fires before the load method's callback is called.
12158          * @param {Object} This DataProxy object.
12159          * @param {Object} o The data object.
12160          * @param {Object} arg The callback argument object passed to the load function.
12161          */
12162         load : true,
12163         /**
12164          * @event loadexception
12165          * Fires if an Exception occurs during data retrieval.
12166          * @param {Object} This DataProxy object.
12167          * @param {Object} o The data object.
12168          * @param {Object} arg The callback argument object passed to the load function.
12169          * @param {Object} e The Exception.
12170          */
12171         loadexception : true
12172     });
12173     Roo.data.DataProxy.superclass.constructor.call(this);
12174 };
12175
12176 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12177
12178     /**
12179      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12180      */
12181 /*
12182  * Based on:
12183  * Ext JS Library 1.1.1
12184  * Copyright(c) 2006-2007, Ext JS, LLC.
12185  *
12186  * Originally Released Under LGPL - original licence link has changed is not relivant.
12187  *
12188  * Fork - LGPL
12189  * <script type="text/javascript">
12190  */
12191 /**
12192  * @class Roo.data.MemoryProxy
12193  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12194  * to the Reader when its load method is called.
12195  * @constructor
12196  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12197  */
12198 Roo.data.MemoryProxy = function(data){
12199     if (data.data) {
12200         data = data.data;
12201     }
12202     Roo.data.MemoryProxy.superclass.constructor.call(this);
12203     this.data = data;
12204 };
12205
12206 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12207     
12208     /**
12209      * Load data from the requested source (in this case an in-memory
12210      * data object passed to the constructor), read the data object into
12211      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12212      * process that block using the passed callback.
12213      * @param {Object} params This parameter is not used by the MemoryProxy class.
12214      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12215      * object into a block of Roo.data.Records.
12216      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12217      * The function must be passed <ul>
12218      * <li>The Record block object</li>
12219      * <li>The "arg" argument from the load function</li>
12220      * <li>A boolean success indicator</li>
12221      * </ul>
12222      * @param {Object} scope The scope in which to call the callback
12223      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12224      */
12225     load : function(params, reader, callback, scope, arg){
12226         params = params || {};
12227         var result;
12228         try {
12229             result = reader.readRecords(this.data);
12230         }catch(e){
12231             this.fireEvent("loadexception", this, arg, null, e);
12232             callback.call(scope, null, arg, false);
12233             return;
12234         }
12235         callback.call(scope, result, arg, true);
12236     },
12237     
12238     // private
12239     update : function(params, records){
12240         
12241     }
12242 });/*
12243  * Based on:
12244  * Ext JS Library 1.1.1
12245  * Copyright(c) 2006-2007, Ext JS, LLC.
12246  *
12247  * Originally Released Under LGPL - original licence link has changed is not relivant.
12248  *
12249  * Fork - LGPL
12250  * <script type="text/javascript">
12251  */
12252 /**
12253  * @class Roo.data.HttpProxy
12254  * @extends Roo.data.DataProxy
12255  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12256  * configured to reference a certain URL.<br><br>
12257  * <p>
12258  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12259  * from which the running page was served.<br><br>
12260  * <p>
12261  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12262  * <p>
12263  * Be aware that to enable the browser to parse an XML document, the server must set
12264  * the Content-Type header in the HTTP response to "text/xml".
12265  * @constructor
12266  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12267  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12268  * will be used to make the request.
12269  */
12270 Roo.data.HttpProxy = function(conn){
12271     Roo.data.HttpProxy.superclass.constructor.call(this);
12272     // is conn a conn config or a real conn?
12273     this.conn = conn;
12274     this.useAjax = !conn || !conn.events;
12275   
12276 };
12277
12278 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12279     // thse are take from connection...
12280     
12281     /**
12282      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12283      */
12284     /**
12285      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12286      * extra parameters to each request made by this object. (defaults to undefined)
12287      */
12288     /**
12289      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12290      *  to each request made by this object. (defaults to undefined)
12291      */
12292     /**
12293      * @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)
12294      */
12295     /**
12296      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12297      */
12298      /**
12299      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12300      * @type Boolean
12301      */
12302   
12303
12304     /**
12305      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12306      * @type Boolean
12307      */
12308     /**
12309      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12310      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12311      * a finer-grained basis than the DataProxy events.
12312      */
12313     getConnection : function(){
12314         return this.useAjax ? Roo.Ajax : this.conn;
12315     },
12316
12317     /**
12318      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12319      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12320      * process that block using the passed callback.
12321      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12322      * for the request to the remote server.
12323      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12324      * object into a block of Roo.data.Records.
12325      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12326      * The function must be passed <ul>
12327      * <li>The Record block object</li>
12328      * <li>The "arg" argument from the load function</li>
12329      * <li>A boolean success indicator</li>
12330      * </ul>
12331      * @param {Object} scope The scope in which to call the callback
12332      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12333      */
12334     load : function(params, reader, callback, scope, arg){
12335         if(this.fireEvent("beforeload", this, params) !== false){
12336             var  o = {
12337                 params : params || {},
12338                 request: {
12339                     callback : callback,
12340                     scope : scope,
12341                     arg : arg
12342                 },
12343                 reader: reader,
12344                 callback : this.loadResponse,
12345                 scope: this
12346             };
12347             if(this.useAjax){
12348                 Roo.applyIf(o, this.conn);
12349                 if(this.activeRequest){
12350                     Roo.Ajax.abort(this.activeRequest);
12351                 }
12352                 this.activeRequest = Roo.Ajax.request(o);
12353             }else{
12354                 this.conn.request(o);
12355             }
12356         }else{
12357             callback.call(scope||this, null, arg, false);
12358         }
12359     },
12360
12361     // private
12362     loadResponse : function(o, success, response){
12363         delete this.activeRequest;
12364         if(!success){
12365             this.fireEvent("loadexception", this, o, response);
12366             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12367             return;
12368         }
12369         var result;
12370         try {
12371             result = o.reader.read(response);
12372         }catch(e){
12373             this.fireEvent("loadexception", this, o, response, e);
12374             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12375             return;
12376         }
12377         
12378         this.fireEvent("load", this, o, o.request.arg);
12379         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12380     },
12381
12382     // private
12383     update : function(dataSet){
12384
12385     },
12386
12387     // private
12388     updateResponse : function(dataSet){
12389
12390     }
12391 });/*
12392  * Based on:
12393  * Ext JS Library 1.1.1
12394  * Copyright(c) 2006-2007, Ext JS, LLC.
12395  *
12396  * Originally Released Under LGPL - original licence link has changed is not relivant.
12397  *
12398  * Fork - LGPL
12399  * <script type="text/javascript">
12400  */
12401
12402 /**
12403  * @class Roo.data.ScriptTagProxy
12404  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12405  * other than the originating domain of the running page.<br><br>
12406  * <p>
12407  * <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
12408  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12409  * <p>
12410  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12411  * source code that is used as the source inside a &lt;script> tag.<br><br>
12412  * <p>
12413  * In order for the browser to process the returned data, the server must wrap the data object
12414  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12415  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12416  * depending on whether the callback name was passed:
12417  * <p>
12418  * <pre><code>
12419 boolean scriptTag = false;
12420 String cb = request.getParameter("callback");
12421 if (cb != null) {
12422     scriptTag = true;
12423     response.setContentType("text/javascript");
12424 } else {
12425     response.setContentType("application/x-json");
12426 }
12427 Writer out = response.getWriter();
12428 if (scriptTag) {
12429     out.write(cb + "(");
12430 }
12431 out.print(dataBlock.toJsonString());
12432 if (scriptTag) {
12433     out.write(");");
12434 }
12435 </pre></code>
12436  *
12437  * @constructor
12438  * @param {Object} config A configuration object.
12439  */
12440 Roo.data.ScriptTagProxy = function(config){
12441     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12442     Roo.apply(this, config);
12443     this.head = document.getElementsByTagName("head")[0];
12444 };
12445
12446 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12447
12448 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12449     /**
12450      * @cfg {String} url The URL from which to request the data object.
12451      */
12452     /**
12453      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12454      */
12455     timeout : 30000,
12456     /**
12457      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12458      * the server the name of the callback function set up by the load call to process the returned data object.
12459      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12460      * javascript output which calls this named function passing the data object as its only parameter.
12461      */
12462     callbackParam : "callback",
12463     /**
12464      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12465      * name to the request.
12466      */
12467     nocache : true,
12468
12469     /**
12470      * Load data from the configured URL, read the data object into
12471      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12472      * process that block using the passed callback.
12473      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12474      * for the request to the remote server.
12475      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12476      * object into a block of Roo.data.Records.
12477      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12478      * The function must be passed <ul>
12479      * <li>The Record block object</li>
12480      * <li>The "arg" argument from the load function</li>
12481      * <li>A boolean success indicator</li>
12482      * </ul>
12483      * @param {Object} scope The scope in which to call the callback
12484      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12485      */
12486     load : function(params, reader, callback, scope, arg){
12487         if(this.fireEvent("beforeload", this, params) !== false){
12488
12489             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12490
12491             var url = this.url;
12492             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12493             if(this.nocache){
12494                 url += "&_dc=" + (new Date().getTime());
12495             }
12496             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12497             var trans = {
12498                 id : transId,
12499                 cb : "stcCallback"+transId,
12500                 scriptId : "stcScript"+transId,
12501                 params : params,
12502                 arg : arg,
12503                 url : url,
12504                 callback : callback,
12505                 scope : scope,
12506                 reader : reader
12507             };
12508             var conn = this;
12509
12510             window[trans.cb] = function(o){
12511                 conn.handleResponse(o, trans);
12512             };
12513
12514             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12515
12516             if(this.autoAbort !== false){
12517                 this.abort();
12518             }
12519
12520             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12521
12522             var script = document.createElement("script");
12523             script.setAttribute("src", url);
12524             script.setAttribute("type", "text/javascript");
12525             script.setAttribute("id", trans.scriptId);
12526             this.head.appendChild(script);
12527
12528             this.trans = trans;
12529         }else{
12530             callback.call(scope||this, null, arg, false);
12531         }
12532     },
12533
12534     // private
12535     isLoading : function(){
12536         return this.trans ? true : false;
12537     },
12538
12539     /**
12540      * Abort the current server request.
12541      */
12542     abort : function(){
12543         if(this.isLoading()){
12544             this.destroyTrans(this.trans);
12545         }
12546     },
12547
12548     // private
12549     destroyTrans : function(trans, isLoaded){
12550         this.head.removeChild(document.getElementById(trans.scriptId));
12551         clearTimeout(trans.timeoutId);
12552         if(isLoaded){
12553             window[trans.cb] = undefined;
12554             try{
12555                 delete window[trans.cb];
12556             }catch(e){}
12557         }else{
12558             // if hasn't been loaded, wait for load to remove it to prevent script error
12559             window[trans.cb] = function(){
12560                 window[trans.cb] = undefined;
12561                 try{
12562                     delete window[trans.cb];
12563                 }catch(e){}
12564             };
12565         }
12566     },
12567
12568     // private
12569     handleResponse : function(o, trans){
12570         this.trans = false;
12571         this.destroyTrans(trans, true);
12572         var result;
12573         try {
12574             result = trans.reader.readRecords(o);
12575         }catch(e){
12576             this.fireEvent("loadexception", this, o, trans.arg, e);
12577             trans.callback.call(trans.scope||window, null, trans.arg, false);
12578             return;
12579         }
12580         this.fireEvent("load", this, o, trans.arg);
12581         trans.callback.call(trans.scope||window, result, trans.arg, true);
12582     },
12583
12584     // private
12585     handleFailure : function(trans){
12586         this.trans = false;
12587         this.destroyTrans(trans, false);
12588         this.fireEvent("loadexception", this, null, trans.arg);
12589         trans.callback.call(trans.scope||window, null, trans.arg, false);
12590     }
12591 });/*
12592  * Based on:
12593  * Ext JS Library 1.1.1
12594  * Copyright(c) 2006-2007, Ext JS, LLC.
12595  *
12596  * Originally Released Under LGPL - original licence link has changed is not relivant.
12597  *
12598  * Fork - LGPL
12599  * <script type="text/javascript">
12600  */
12601
12602 /**
12603  * @class Roo.data.JsonReader
12604  * @extends Roo.data.DataReader
12605  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12606  * based on mappings in a provided Roo.data.Record constructor.
12607  * 
12608  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12609  * in the reply previously. 
12610  * 
12611  * <p>
12612  * Example code:
12613  * <pre><code>
12614 var RecordDef = Roo.data.Record.create([
12615     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12616     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12617 ]);
12618 var myReader = new Roo.data.JsonReader({
12619     totalProperty: "results",    // The property which contains the total dataset size (optional)
12620     root: "rows",                // The property which contains an Array of row objects
12621     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12622 }, RecordDef);
12623 </code></pre>
12624  * <p>
12625  * This would consume a JSON file like this:
12626  * <pre><code>
12627 { 'results': 2, 'rows': [
12628     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12629     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12630 }
12631 </code></pre>
12632  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12633  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12634  * paged from the remote server.
12635  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12636  * @cfg {String} root name of the property which contains the Array of row objects.
12637  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12638  * @cfg {Array} fields Array of field definition objects
12639  * @constructor
12640  * Create a new JsonReader
12641  * @param {Object} meta Metadata configuration options
12642  * @param {Object} recordType Either an Array of field definition objects,
12643  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12644  */
12645 Roo.data.JsonReader = function(meta, recordType){
12646     
12647     meta = meta || {};
12648     // set some defaults:
12649     Roo.applyIf(meta, {
12650         totalProperty: 'total',
12651         successProperty : 'success',
12652         root : 'data',
12653         id : 'id'
12654     });
12655     
12656     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12657 };
12658 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12659     
12660     /**
12661      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12662      * Used by Store query builder to append _requestMeta to params.
12663      * 
12664      */
12665     metaFromRemote : false,
12666     /**
12667      * This method is only used by a DataProxy which has retrieved data from a remote server.
12668      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12669      * @return {Object} data A data block which is used by an Roo.data.Store object as
12670      * a cache of Roo.data.Records.
12671      */
12672     read : function(response){
12673         var json = response.responseText;
12674        
12675         var o = /* eval:var:o */ eval("("+json+")");
12676         if(!o) {
12677             throw {message: "JsonReader.read: Json object not found"};
12678         }
12679         
12680         if(o.metaData){
12681             
12682             delete this.ef;
12683             this.metaFromRemote = true;
12684             this.meta = o.metaData;
12685             this.recordType = Roo.data.Record.create(o.metaData.fields);
12686             this.onMetaChange(this.meta, this.recordType, o);
12687         }
12688         return this.readRecords(o);
12689     },
12690
12691     // private function a store will implement
12692     onMetaChange : function(meta, recordType, o){
12693
12694     },
12695
12696     /**
12697          * @ignore
12698          */
12699     simpleAccess: function(obj, subsc) {
12700         return obj[subsc];
12701     },
12702
12703         /**
12704          * @ignore
12705          */
12706     getJsonAccessor: function(){
12707         var re = /[\[\.]/;
12708         return function(expr) {
12709             try {
12710                 return(re.test(expr))
12711                     ? new Function("obj", "return obj." + expr)
12712                     : function(obj){
12713                         return obj[expr];
12714                     };
12715             } catch(e){}
12716             return Roo.emptyFn;
12717         };
12718     }(),
12719
12720     /**
12721      * Create a data block containing Roo.data.Records from an XML document.
12722      * @param {Object} o An object which contains an Array of row objects in the property specified
12723      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12724      * which contains the total size of the dataset.
12725      * @return {Object} data A data block which is used by an Roo.data.Store object as
12726      * a cache of Roo.data.Records.
12727      */
12728     readRecords : function(o){
12729         /**
12730          * After any data loads, the raw JSON data is available for further custom processing.
12731          * @type Object
12732          */
12733         this.o = o;
12734         var s = this.meta, Record = this.recordType,
12735             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12736
12737 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12738         if (!this.ef) {
12739             if(s.totalProperty) {
12740                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12741                 }
12742                 if(s.successProperty) {
12743                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12744                 }
12745                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12746                 if (s.id) {
12747                         var g = this.getJsonAccessor(s.id);
12748                         this.getId = function(rec) {
12749                                 var r = g(rec);  
12750                                 return (r === undefined || r === "") ? null : r;
12751                         };
12752                 } else {
12753                         this.getId = function(){return null;};
12754                 }
12755             this.ef = [];
12756             for(var jj = 0; jj < fl; jj++){
12757                 f = fi[jj];
12758                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12759                 this.ef[jj] = this.getJsonAccessor(map);
12760             }
12761         }
12762
12763         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12764         if(s.totalProperty){
12765             var vt = parseInt(this.getTotal(o), 10);
12766             if(!isNaN(vt)){
12767                 totalRecords = vt;
12768             }
12769         }
12770         if(s.successProperty){
12771             var vs = this.getSuccess(o);
12772             if(vs === false || vs === 'false'){
12773                 success = false;
12774             }
12775         }
12776         var records = [];
12777         for(var i = 0; i < c; i++){
12778                 var n = root[i];
12779             var values = {};
12780             var id = this.getId(n);
12781             for(var j = 0; j < fl; j++){
12782                 f = fi[j];
12783             var v = this.ef[j](n);
12784             if (!f.convert) {
12785                 Roo.log('missing convert for ' + f.name);
12786                 Roo.log(f);
12787                 continue;
12788             }
12789             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12790             }
12791             var record = new Record(values, id);
12792             record.json = n;
12793             records[i] = record;
12794         }
12795         return {
12796             raw : o,
12797             success : success,
12798             records : records,
12799             totalRecords : totalRecords
12800         };
12801     }
12802 });/*
12803  * Based on:
12804  * Ext JS Library 1.1.1
12805  * Copyright(c) 2006-2007, Ext JS, LLC.
12806  *
12807  * Originally Released Under LGPL - original licence link has changed is not relivant.
12808  *
12809  * Fork - LGPL
12810  * <script type="text/javascript">
12811  */
12812
12813 /**
12814  * @class Roo.data.ArrayReader
12815  * @extends Roo.data.DataReader
12816  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12817  * Each element of that Array represents a row of data fields. The
12818  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12819  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12820  * <p>
12821  * Example code:.
12822  * <pre><code>
12823 var RecordDef = Roo.data.Record.create([
12824     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12825     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12826 ]);
12827 var myReader = new Roo.data.ArrayReader({
12828     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12829 }, RecordDef);
12830 </code></pre>
12831  * <p>
12832  * This would consume an Array like this:
12833  * <pre><code>
12834 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12835   </code></pre>
12836  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12837  * @constructor
12838  * Create a new JsonReader
12839  * @param {Object} meta Metadata configuration options.
12840  * @param {Object} recordType Either an Array of field definition objects
12841  * as specified to {@link Roo.data.Record#create},
12842  * or an {@link Roo.data.Record} object
12843  * created using {@link Roo.data.Record#create}.
12844  */
12845 Roo.data.ArrayReader = function(meta, recordType){
12846     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12847 };
12848
12849 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12850     /**
12851      * Create a data block containing Roo.data.Records from an XML document.
12852      * @param {Object} o An Array of row objects which represents the dataset.
12853      * @return {Object} data A data block which is used by an Roo.data.Store object as
12854      * a cache of Roo.data.Records.
12855      */
12856     readRecords : function(o){
12857         var sid = this.meta ? this.meta.id : null;
12858         var recordType = this.recordType, fields = recordType.prototype.fields;
12859         var records = [];
12860         var root = o;
12861             for(var i = 0; i < root.length; i++){
12862                     var n = root[i];
12863                 var values = {};
12864                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12865                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12866                 var f = fields.items[j];
12867                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12868                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12869                 v = f.convert(v);
12870                 values[f.name] = v;
12871             }
12872                 var record = new recordType(values, id);
12873                 record.json = n;
12874                 records[records.length] = record;
12875             }
12876             return {
12877                 records : records,
12878                 totalRecords : records.length
12879             };
12880     }
12881 });/*
12882  * - LGPL
12883  * * 
12884  */
12885
12886 /**
12887  * @class Roo.bootstrap.ComboBox
12888  * @extends Roo.bootstrap.TriggerField
12889  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12890  * @cfg {Boolean} append (true|false) default false
12891  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12892  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12893  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12894  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12895  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12896  * @cfg {Boolean} animate default true
12897  * @cfg {Boolean} emptyResultText only for touch device
12898  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12899  * @cfg {String} emptyTitle default ''
12900  * @constructor
12901  * Create a new ComboBox.
12902  * @param {Object} config Configuration options
12903  */
12904 Roo.bootstrap.ComboBox = function(config){
12905     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12906     this.addEvents({
12907         /**
12908          * @event expand
12909          * Fires when the dropdown list is expanded
12910         * @param {Roo.bootstrap.ComboBox} combo This combo box
12911         */
12912         'expand' : true,
12913         /**
12914          * @event collapse
12915          * Fires when the dropdown list is collapsed
12916         * @param {Roo.bootstrap.ComboBox} combo This combo box
12917         */
12918         'collapse' : true,
12919         /**
12920          * @event beforeselect
12921          * Fires before a list item is selected. Return false to cancel the selection.
12922         * @param {Roo.bootstrap.ComboBox} combo This combo box
12923         * @param {Roo.data.Record} record The data record returned from the underlying store
12924         * @param {Number} index The index of the selected item in the dropdown list
12925         */
12926         'beforeselect' : true,
12927         /**
12928          * @event select
12929          * Fires when a list item is selected
12930         * @param {Roo.bootstrap.ComboBox} combo This combo box
12931         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12932         * @param {Number} index The index of the selected item in the dropdown list
12933         */
12934         'select' : true,
12935         /**
12936          * @event beforequery
12937          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12938          * The event object passed has these properties:
12939         * @param {Roo.bootstrap.ComboBox} combo This combo box
12940         * @param {String} query The query
12941         * @param {Boolean} forceAll true to force "all" query
12942         * @param {Boolean} cancel true to cancel the query
12943         * @param {Object} e The query event object
12944         */
12945         'beforequery': true,
12946          /**
12947          * @event add
12948          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12949         * @param {Roo.bootstrap.ComboBox} combo This combo box
12950         */
12951         'add' : true,
12952         /**
12953          * @event edit
12954          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12955         * @param {Roo.bootstrap.ComboBox} combo This combo box
12956         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12957         */
12958         'edit' : true,
12959         /**
12960          * @event remove
12961          * Fires when the remove value from the combobox array
12962         * @param {Roo.bootstrap.ComboBox} combo This combo box
12963         */
12964         'remove' : true,
12965         /**
12966          * @event afterremove
12967          * Fires when the remove value from the combobox array
12968         * @param {Roo.bootstrap.ComboBox} combo This combo box
12969         */
12970         'afterremove' : true,
12971         /**
12972          * @event specialfilter
12973          * Fires when specialfilter
12974             * @param {Roo.bootstrap.ComboBox} combo This combo box
12975             */
12976         'specialfilter' : true,
12977         /**
12978          * @event tick
12979          * Fires when tick the element
12980             * @param {Roo.bootstrap.ComboBox} combo This combo box
12981             */
12982         'tick' : true,
12983         /**
12984          * @event touchviewdisplay
12985          * Fires when touch view require special display (default is using displayField)
12986             * @param {Roo.bootstrap.ComboBox} combo This combo box
12987             * @param {Object} cfg set html .
12988             */
12989         'touchviewdisplay' : true
12990         
12991     });
12992     
12993     this.item = [];
12994     this.tickItems = [];
12995     
12996     this.selectedIndex = -1;
12997     if(this.mode == 'local'){
12998         if(config.queryDelay === undefined){
12999             this.queryDelay = 10;
13000         }
13001         if(config.minChars === undefined){
13002             this.minChars = 0;
13003         }
13004     }
13005 };
13006
13007 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13008      
13009     /**
13010      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13011      * rendering into an Roo.Editor, defaults to false)
13012      */
13013     /**
13014      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13015      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13016      */
13017     /**
13018      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13019      */
13020     /**
13021      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13022      * the dropdown list (defaults to undefined, with no header element)
13023      */
13024
13025      /**
13026      * @cfg {String/Roo.Template} tpl The template to use to render the output
13027      */
13028      
13029      /**
13030      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13031      */
13032     listWidth: undefined,
13033     /**
13034      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13035      * mode = 'remote' or 'text' if mode = 'local')
13036      */
13037     displayField: undefined,
13038     
13039     /**
13040      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13041      * mode = 'remote' or 'value' if mode = 'local'). 
13042      * Note: use of a valueField requires the user make a selection
13043      * in order for a value to be mapped.
13044      */
13045     valueField: undefined,
13046     /**
13047      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13048      */
13049     modalTitle : '',
13050     
13051     /**
13052      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13053      * field's data value (defaults to the underlying DOM element's name)
13054      */
13055     hiddenName: undefined,
13056     /**
13057      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13058      */
13059     listClass: '',
13060     /**
13061      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13062      */
13063     selectedClass: 'active',
13064     
13065     /**
13066      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13067      */
13068     shadow:'sides',
13069     /**
13070      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13071      * anchor positions (defaults to 'tl-bl')
13072      */
13073     listAlign: 'tl-bl?',
13074     /**
13075      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13076      */
13077     maxHeight: 300,
13078     /**
13079      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13080      * query specified by the allQuery config option (defaults to 'query')
13081      */
13082     triggerAction: 'query',
13083     /**
13084      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13085      * (defaults to 4, does not apply if editable = false)
13086      */
13087     minChars : 4,
13088     /**
13089      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13090      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13091      */
13092     typeAhead: false,
13093     /**
13094      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13095      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13096      */
13097     queryDelay: 500,
13098     /**
13099      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13100      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13101      */
13102     pageSize: 0,
13103     /**
13104      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13105      * when editable = true (defaults to false)
13106      */
13107     selectOnFocus:false,
13108     /**
13109      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13110      */
13111     queryParam: 'query',
13112     /**
13113      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13114      * when mode = 'remote' (defaults to 'Loading...')
13115      */
13116     loadingText: 'Loading...',
13117     /**
13118      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13119      */
13120     resizable: false,
13121     /**
13122      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13123      */
13124     handleHeight : 8,
13125     /**
13126      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13127      * traditional select (defaults to true)
13128      */
13129     editable: true,
13130     /**
13131      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13132      */
13133     allQuery: '',
13134     /**
13135      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13136      */
13137     mode: 'remote',
13138     /**
13139      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13140      * listWidth has a higher value)
13141      */
13142     minListWidth : 70,
13143     /**
13144      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13145      * allow the user to set arbitrary text into the field (defaults to false)
13146      */
13147     forceSelection:false,
13148     /**
13149      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13150      * if typeAhead = true (defaults to 250)
13151      */
13152     typeAheadDelay : 250,
13153     /**
13154      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13155      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13156      */
13157     valueNotFoundText : undefined,
13158     /**
13159      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13160      */
13161     blockFocus : false,
13162     
13163     /**
13164      * @cfg {Boolean} disableClear Disable showing of clear button.
13165      */
13166     disableClear : false,
13167     /**
13168      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13169      */
13170     alwaysQuery : false,
13171     
13172     /**
13173      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13174      */
13175     multiple : false,
13176     
13177     /**
13178      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13179      */
13180     invalidClass : "has-warning",
13181     
13182     /**
13183      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13184      */
13185     validClass : "has-success",
13186     
13187     /**
13188      * @cfg {Boolean} specialFilter (true|false) special filter default false
13189      */
13190     specialFilter : false,
13191     
13192     /**
13193      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13194      */
13195     mobileTouchView : true,
13196     
13197     /**
13198      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13199      */
13200     useNativeIOS : false,
13201     
13202     /**
13203      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13204      */
13205     mobile_restrict_height : false,
13206     
13207     ios_options : false,
13208     
13209     //private
13210     addicon : false,
13211     editicon: false,
13212     
13213     page: 0,
13214     hasQuery: false,
13215     append: false,
13216     loadNext: false,
13217     autoFocus : true,
13218     tickable : false,
13219     btnPosition : 'right',
13220     triggerList : true,
13221     showToggleBtn : true,
13222     animate : true,
13223     emptyResultText: 'Empty',
13224     triggerText : 'Select',
13225     emptyTitle : '',
13226     
13227     // element that contains real text value.. (when hidden is used..)
13228     
13229     getAutoCreate : function()
13230     {   
13231         var cfg = false;
13232         //render
13233         /*
13234          * Render classic select for iso
13235          */
13236         
13237         if(Roo.isIOS && this.useNativeIOS){
13238             cfg = this.getAutoCreateNativeIOS();
13239             return cfg;
13240         }
13241         
13242         /*
13243          * Touch Devices
13244          */
13245         
13246         if(Roo.isTouch && this.mobileTouchView){
13247             cfg = this.getAutoCreateTouchView();
13248             return cfg;;
13249         }
13250         
13251         /*
13252          *  Normal ComboBox
13253          */
13254         if(!this.tickable){
13255             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13256             return cfg;
13257         }
13258         
13259         /*
13260          *  ComboBox with tickable selections
13261          */
13262              
13263         var align = this.labelAlign || this.parentLabelAlign();
13264         
13265         cfg = {
13266             cls : 'form-group roo-combobox-tickable' //input-group
13267         };
13268         
13269         var btn_text_select = '';
13270         var btn_text_done = '';
13271         var btn_text_cancel = '';
13272         
13273         if (this.btn_text_show) {
13274             btn_text_select = 'Select';
13275             btn_text_done = 'Done';
13276             btn_text_cancel = 'Cancel'; 
13277         }
13278         
13279         var buttons = {
13280             tag : 'div',
13281             cls : 'tickable-buttons',
13282             cn : [
13283                 {
13284                     tag : 'button',
13285                     type : 'button',
13286                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13287                     //html : this.triggerText
13288                     html: btn_text_select
13289                 },
13290                 {
13291                     tag : 'button',
13292                     type : 'button',
13293                     name : 'ok',
13294                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13295                     //html : 'Done'
13296                     html: btn_text_done
13297                 },
13298                 {
13299                     tag : 'button',
13300                     type : 'button',
13301                     name : 'cancel',
13302                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13303                     //html : 'Cancel'
13304                     html: btn_text_cancel
13305                 }
13306             ]
13307         };
13308         
13309         if(this.editable){
13310             buttons.cn.unshift({
13311                 tag: 'input',
13312                 cls: 'roo-select2-search-field-input'
13313             });
13314         }
13315         
13316         var _this = this;
13317         
13318         Roo.each(buttons.cn, function(c){
13319             if (_this.size) {
13320                 c.cls += ' btn-' + _this.size;
13321             }
13322
13323             if (_this.disabled) {
13324                 c.disabled = true;
13325             }
13326         });
13327         
13328         var box = {
13329             tag: 'div',
13330             cn: [
13331                 {
13332                     tag: 'input',
13333                     type : 'hidden',
13334                     cls: 'form-hidden-field'
13335                 },
13336                 {
13337                     tag: 'ul',
13338                     cls: 'roo-select2-choices',
13339                     cn:[
13340                         {
13341                             tag: 'li',
13342                             cls: 'roo-select2-search-field',
13343                             cn: [
13344                                 buttons
13345                             ]
13346                         }
13347                     ]
13348                 }
13349             ]
13350         };
13351         
13352         var combobox = {
13353             cls: 'roo-select2-container input-group roo-select2-container-multi',
13354             cn: [
13355                 box
13356 //                {
13357 //                    tag: 'ul',
13358 //                    cls: 'typeahead typeahead-long dropdown-menu',
13359 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13360 //                }
13361             ]
13362         };
13363         
13364         if(this.hasFeedback && !this.allowBlank){
13365             
13366             var feedback = {
13367                 tag: 'span',
13368                 cls: 'glyphicon form-control-feedback'
13369             };
13370
13371             combobox.cn.push(feedback);
13372         }
13373         
13374         var indicator = {
13375             tag : 'i',
13376             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13377             tooltip : 'This field is required'
13378         };
13379         if (Roo.bootstrap.version == 4) {
13380             indicator = {
13381                 tag : 'i',
13382                 style : 'display:none'
13383             };
13384         }
13385         if (align ==='left' && this.fieldLabel.length) {
13386             
13387             cfg.cls += ' roo-form-group-label-left row';
13388             
13389             cfg.cn = [
13390                 indicator,
13391                 {
13392                     tag: 'label',
13393                     'for' :  id,
13394                     cls : 'control-label col-form-label',
13395                     html : this.fieldLabel
13396
13397                 },
13398                 {
13399                     cls : "", 
13400                     cn: [
13401                         combobox
13402                     ]
13403                 }
13404
13405             ];
13406             
13407             var labelCfg = cfg.cn[1];
13408             var contentCfg = cfg.cn[2];
13409             
13410
13411             if(this.indicatorpos == 'right'){
13412                 
13413                 cfg.cn = [
13414                     {
13415                         tag: 'label',
13416                         'for' :  id,
13417                         cls : 'control-label col-form-label',
13418                         cn : [
13419                             {
13420                                 tag : 'span',
13421                                 html : this.fieldLabel
13422                             },
13423                             indicator
13424                         ]
13425                     },
13426                     {
13427                         cls : "",
13428                         cn: [
13429                             combobox
13430                         ]
13431                     }
13432
13433                 ];
13434                 
13435                 
13436                 
13437                 labelCfg = cfg.cn[0];
13438                 contentCfg = cfg.cn[1];
13439             
13440             }
13441             
13442             if(this.labelWidth > 12){
13443                 labelCfg.style = "width: " + this.labelWidth + 'px';
13444             }
13445             
13446             if(this.labelWidth < 13 && this.labelmd == 0){
13447                 this.labelmd = this.labelWidth;
13448             }
13449             
13450             if(this.labellg > 0){
13451                 labelCfg.cls += ' col-lg-' + this.labellg;
13452                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13453             }
13454             
13455             if(this.labelmd > 0){
13456                 labelCfg.cls += ' col-md-' + this.labelmd;
13457                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13458             }
13459             
13460             if(this.labelsm > 0){
13461                 labelCfg.cls += ' col-sm-' + this.labelsm;
13462                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13463             }
13464             
13465             if(this.labelxs > 0){
13466                 labelCfg.cls += ' col-xs-' + this.labelxs;
13467                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13468             }
13469                 
13470                 
13471         } else if ( this.fieldLabel.length) {
13472 //                Roo.log(" label");
13473                  cfg.cn = [
13474                    indicator,
13475                     {
13476                         tag: 'label',
13477                         //cls : 'input-group-addon',
13478                         html : this.fieldLabel
13479                     },
13480                     combobox
13481                 ];
13482                 
13483                 if(this.indicatorpos == 'right'){
13484                     cfg.cn = [
13485                         {
13486                             tag: 'label',
13487                             //cls : 'input-group-addon',
13488                             html : this.fieldLabel
13489                         },
13490                         indicator,
13491                         combobox
13492                     ];
13493                     
13494                 }
13495
13496         } else {
13497             
13498 //                Roo.log(" no label && no align");
13499                 cfg = combobox
13500                      
13501                 
13502         }
13503          
13504         var settings=this;
13505         ['xs','sm','md','lg'].map(function(size){
13506             if (settings[size]) {
13507                 cfg.cls += ' col-' + size + '-' + settings[size];
13508             }
13509         });
13510         
13511         return cfg;
13512         
13513     },
13514     
13515     _initEventsCalled : false,
13516     
13517     // private
13518     initEvents: function()
13519     {   
13520         if (this._initEventsCalled) { // as we call render... prevent looping...
13521             return;
13522         }
13523         this._initEventsCalled = true;
13524         
13525         if (!this.store) {
13526             throw "can not find store for combo";
13527         }
13528         
13529         this.indicator = this.indicatorEl();
13530         
13531         this.store = Roo.factory(this.store, Roo.data);
13532         this.store.parent = this;
13533         
13534         // if we are building from html. then this element is so complex, that we can not really
13535         // use the rendered HTML.
13536         // so we have to trash and replace the previous code.
13537         if (Roo.XComponent.build_from_html) {
13538             // remove this element....
13539             var e = this.el.dom, k=0;
13540             while (e ) { e = e.previousSibling;  ++k;}
13541
13542             this.el.remove();
13543             
13544             this.el=false;
13545             this.rendered = false;
13546             
13547             this.render(this.parent().getChildContainer(true), k);
13548         }
13549         
13550         if(Roo.isIOS && this.useNativeIOS){
13551             this.initIOSView();
13552             return;
13553         }
13554         
13555         /*
13556          * Touch Devices
13557          */
13558         
13559         if(Roo.isTouch && this.mobileTouchView){
13560             this.initTouchView();
13561             return;
13562         }
13563         
13564         if(this.tickable){
13565             this.initTickableEvents();
13566             return;
13567         }
13568         
13569         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13570         
13571         if(this.hiddenName){
13572             
13573             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13574             
13575             this.hiddenField.dom.value =
13576                 this.hiddenValue !== undefined ? this.hiddenValue :
13577                 this.value !== undefined ? this.value : '';
13578
13579             // prevent input submission
13580             this.el.dom.removeAttribute('name');
13581             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13582              
13583              
13584         }
13585         //if(Roo.isGecko){
13586         //    this.el.dom.setAttribute('autocomplete', 'off');
13587         //}
13588         
13589         var cls = 'x-combo-list';
13590         
13591         //this.list = new Roo.Layer({
13592         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13593         //});
13594         
13595         var _this = this;
13596         
13597         (function(){
13598             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13599             _this.list.setWidth(lw);
13600         }).defer(100);
13601         
13602         this.list.on('mouseover', this.onViewOver, this);
13603         this.list.on('mousemove', this.onViewMove, this);
13604         this.list.on('scroll', this.onViewScroll, this);
13605         
13606         /*
13607         this.list.swallowEvent('mousewheel');
13608         this.assetHeight = 0;
13609
13610         if(this.title){
13611             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13612             this.assetHeight += this.header.getHeight();
13613         }
13614
13615         this.innerList = this.list.createChild({cls:cls+'-inner'});
13616         this.innerList.on('mouseover', this.onViewOver, this);
13617         this.innerList.on('mousemove', this.onViewMove, this);
13618         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13619         
13620         if(this.allowBlank && !this.pageSize && !this.disableClear){
13621             this.footer = this.list.createChild({cls:cls+'-ft'});
13622             this.pageTb = new Roo.Toolbar(this.footer);
13623            
13624         }
13625         if(this.pageSize){
13626             this.footer = this.list.createChild({cls:cls+'-ft'});
13627             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13628                     {pageSize: this.pageSize});
13629             
13630         }
13631         
13632         if (this.pageTb && this.allowBlank && !this.disableClear) {
13633             var _this = this;
13634             this.pageTb.add(new Roo.Toolbar.Fill(), {
13635                 cls: 'x-btn-icon x-btn-clear',
13636                 text: '&#160;',
13637                 handler: function()
13638                 {
13639                     _this.collapse();
13640                     _this.clearValue();
13641                     _this.onSelect(false, -1);
13642                 }
13643             });
13644         }
13645         if (this.footer) {
13646             this.assetHeight += this.footer.getHeight();
13647         }
13648         */
13649             
13650         if(!this.tpl){
13651             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13652         }
13653
13654         this.view = new Roo.View(this.list, this.tpl, {
13655             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13656         });
13657         //this.view.wrapEl.setDisplayed(false);
13658         this.view.on('click', this.onViewClick, this);
13659         
13660         
13661         this.store.on('beforeload', this.onBeforeLoad, this);
13662         this.store.on('load', this.onLoad, this);
13663         this.store.on('loadexception', this.onLoadException, this);
13664         /*
13665         if(this.resizable){
13666             this.resizer = new Roo.Resizable(this.list,  {
13667                pinned:true, handles:'se'
13668             });
13669             this.resizer.on('resize', function(r, w, h){
13670                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13671                 this.listWidth = w;
13672                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13673                 this.restrictHeight();
13674             }, this);
13675             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13676         }
13677         */
13678         if(!this.editable){
13679             this.editable = true;
13680             this.setEditable(false);
13681         }
13682         
13683         /*
13684         
13685         if (typeof(this.events.add.listeners) != 'undefined') {
13686             
13687             this.addicon = this.wrap.createChild(
13688                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13689        
13690             this.addicon.on('click', function(e) {
13691                 this.fireEvent('add', this);
13692             }, this);
13693         }
13694         if (typeof(this.events.edit.listeners) != 'undefined') {
13695             
13696             this.editicon = this.wrap.createChild(
13697                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13698             if (this.addicon) {
13699                 this.editicon.setStyle('margin-left', '40px');
13700             }
13701             this.editicon.on('click', function(e) {
13702                 
13703                 // we fire even  if inothing is selected..
13704                 this.fireEvent('edit', this, this.lastData );
13705                 
13706             }, this);
13707         }
13708         */
13709         
13710         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13711             "up" : function(e){
13712                 this.inKeyMode = true;
13713                 this.selectPrev();
13714             },
13715
13716             "down" : function(e){
13717                 if(!this.isExpanded()){
13718                     this.onTriggerClick();
13719                 }else{
13720                     this.inKeyMode = true;
13721                     this.selectNext();
13722                 }
13723             },
13724
13725             "enter" : function(e){
13726 //                this.onViewClick();
13727                 //return true;
13728                 this.collapse();
13729                 
13730                 if(this.fireEvent("specialkey", this, e)){
13731                     this.onViewClick(false);
13732                 }
13733                 
13734                 return true;
13735             },
13736
13737             "esc" : function(e){
13738                 this.collapse();
13739             },
13740
13741             "tab" : function(e){
13742                 this.collapse();
13743                 
13744                 if(this.fireEvent("specialkey", this, e)){
13745                     this.onViewClick(false);
13746                 }
13747                 
13748                 return true;
13749             },
13750
13751             scope : this,
13752
13753             doRelay : function(foo, bar, hname){
13754                 if(hname == 'down' || this.scope.isExpanded()){
13755                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13756                 }
13757                 return true;
13758             },
13759
13760             forceKeyDown: true
13761         });
13762         
13763         
13764         this.queryDelay = Math.max(this.queryDelay || 10,
13765                 this.mode == 'local' ? 10 : 250);
13766         
13767         
13768         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13769         
13770         if(this.typeAhead){
13771             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13772         }
13773         if(this.editable !== false){
13774             this.inputEl().on("keyup", this.onKeyUp, this);
13775         }
13776         if(this.forceSelection){
13777             this.inputEl().on('blur', this.doForce, this);
13778         }
13779         
13780         if(this.multiple){
13781             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13782             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13783         }
13784     },
13785     
13786     initTickableEvents: function()
13787     {   
13788         this.createList();
13789         
13790         if(this.hiddenName){
13791             
13792             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13793             
13794             this.hiddenField.dom.value =
13795                 this.hiddenValue !== undefined ? this.hiddenValue :
13796                 this.value !== undefined ? this.value : '';
13797
13798             // prevent input submission
13799             this.el.dom.removeAttribute('name');
13800             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13801              
13802              
13803         }
13804         
13805 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13806         
13807         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13808         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13809         if(this.triggerList){
13810             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13811         }
13812          
13813         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13814         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13815         
13816         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13817         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13818         
13819         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13820         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13821         
13822         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13823         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13824         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13825         
13826         this.okBtn.hide();
13827         this.cancelBtn.hide();
13828         
13829         var _this = this;
13830         
13831         (function(){
13832             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13833             _this.list.setWidth(lw);
13834         }).defer(100);
13835         
13836         this.list.on('mouseover', this.onViewOver, this);
13837         this.list.on('mousemove', this.onViewMove, this);
13838         
13839         this.list.on('scroll', this.onViewScroll, this);
13840         
13841         if(!this.tpl){
13842             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13843                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13844         }
13845
13846         this.view = new Roo.View(this.list, this.tpl, {
13847             singleSelect:true,
13848             tickable:true,
13849             parent:this,
13850             store: this.store,
13851             selectedClass: this.selectedClass
13852         });
13853         
13854         //this.view.wrapEl.setDisplayed(false);
13855         this.view.on('click', this.onViewClick, this);
13856         
13857         
13858         
13859         this.store.on('beforeload', this.onBeforeLoad, this);
13860         this.store.on('load', this.onLoad, this);
13861         this.store.on('loadexception', this.onLoadException, this);
13862         
13863         if(this.editable){
13864             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13865                 "up" : function(e){
13866                     this.inKeyMode = true;
13867                     this.selectPrev();
13868                 },
13869
13870                 "down" : function(e){
13871                     this.inKeyMode = true;
13872                     this.selectNext();
13873                 },
13874
13875                 "enter" : function(e){
13876                     if(this.fireEvent("specialkey", this, e)){
13877                         this.onViewClick(false);
13878                     }
13879                     
13880                     return true;
13881                 },
13882
13883                 "esc" : function(e){
13884                     this.onTickableFooterButtonClick(e, false, false);
13885                 },
13886
13887                 "tab" : function(e){
13888                     this.fireEvent("specialkey", this, e);
13889                     
13890                     this.onTickableFooterButtonClick(e, false, false);
13891                     
13892                     return true;
13893                 },
13894
13895                 scope : this,
13896
13897                 doRelay : function(e, fn, key){
13898                     if(this.scope.isExpanded()){
13899                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13900                     }
13901                     return true;
13902                 },
13903
13904                 forceKeyDown: true
13905             });
13906         }
13907         
13908         this.queryDelay = Math.max(this.queryDelay || 10,
13909                 this.mode == 'local' ? 10 : 250);
13910         
13911         
13912         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13913         
13914         if(this.typeAhead){
13915             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13916         }
13917         
13918         if(this.editable !== false){
13919             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13920         }
13921         
13922         this.indicator = this.indicatorEl();
13923         
13924         if(this.indicator){
13925             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13926             this.indicator.hide();
13927         }
13928         
13929     },
13930
13931     onDestroy : function(){
13932         if(this.view){
13933             this.view.setStore(null);
13934             this.view.el.removeAllListeners();
13935             this.view.el.remove();
13936             this.view.purgeListeners();
13937         }
13938         if(this.list){
13939             this.list.dom.innerHTML  = '';
13940         }
13941         
13942         if(this.store){
13943             this.store.un('beforeload', this.onBeforeLoad, this);
13944             this.store.un('load', this.onLoad, this);
13945             this.store.un('loadexception', this.onLoadException, this);
13946         }
13947         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13948     },
13949
13950     // private
13951     fireKey : function(e){
13952         if(e.isNavKeyPress() && !this.list.isVisible()){
13953             this.fireEvent("specialkey", this, e);
13954         }
13955     },
13956
13957     // private
13958     onResize: function(w, h){
13959 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13960 //        
13961 //        if(typeof w != 'number'){
13962 //            // we do not handle it!?!?
13963 //            return;
13964 //        }
13965 //        var tw = this.trigger.getWidth();
13966 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13967 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13968 //        var x = w - tw;
13969 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13970 //            
13971 //        //this.trigger.setStyle('left', x+'px');
13972 //        
13973 //        if(this.list && this.listWidth === undefined){
13974 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13975 //            this.list.setWidth(lw);
13976 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13977 //        }
13978         
13979     
13980         
13981     },
13982
13983     /**
13984      * Allow or prevent the user from directly editing the field text.  If false is passed,
13985      * the user will only be able to select from the items defined in the dropdown list.  This method
13986      * is the runtime equivalent of setting the 'editable' config option at config time.
13987      * @param {Boolean} value True to allow the user to directly edit the field text
13988      */
13989     setEditable : function(value){
13990         if(value == this.editable){
13991             return;
13992         }
13993         this.editable = value;
13994         if(!value){
13995             this.inputEl().dom.setAttribute('readOnly', true);
13996             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13997             this.inputEl().addClass('x-combo-noedit');
13998         }else{
13999             this.inputEl().dom.setAttribute('readOnly', false);
14000             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14001             this.inputEl().removeClass('x-combo-noedit');
14002         }
14003     },
14004
14005     // private
14006     
14007     onBeforeLoad : function(combo,opts){
14008         if(!this.hasFocus){
14009             return;
14010         }
14011          if (!opts.add) {
14012             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14013          }
14014         this.restrictHeight();
14015         this.selectedIndex = -1;
14016     },
14017
14018     // private
14019     onLoad : function(){
14020         
14021         this.hasQuery = false;
14022         
14023         if(!this.hasFocus){
14024             return;
14025         }
14026         
14027         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14028             this.loading.hide();
14029         }
14030         
14031         if(this.store.getCount() > 0){
14032             
14033             this.expand();
14034             this.restrictHeight();
14035             if(this.lastQuery == this.allQuery){
14036                 if(this.editable && !this.tickable){
14037                     this.inputEl().dom.select();
14038                 }
14039                 
14040                 if(
14041                     !this.selectByValue(this.value, true) &&
14042                     this.autoFocus && 
14043                     (
14044                         !this.store.lastOptions ||
14045                         typeof(this.store.lastOptions.add) == 'undefined' || 
14046                         this.store.lastOptions.add != true
14047                     )
14048                 ){
14049                     this.select(0, true);
14050                 }
14051             }else{
14052                 if(this.autoFocus){
14053                     this.selectNext();
14054                 }
14055                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14056                     this.taTask.delay(this.typeAheadDelay);
14057                 }
14058             }
14059         }else{
14060             this.onEmptyResults();
14061         }
14062         
14063         //this.el.focus();
14064     },
14065     // private
14066     onLoadException : function()
14067     {
14068         this.hasQuery = false;
14069         
14070         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14071             this.loading.hide();
14072         }
14073         
14074         if(this.tickable && this.editable){
14075             return;
14076         }
14077         
14078         this.collapse();
14079         // only causes errors at present
14080         //Roo.log(this.store.reader.jsonData);
14081         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14082             // fixme
14083             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14084         //}
14085         
14086         
14087     },
14088     // private
14089     onTypeAhead : function(){
14090         if(this.store.getCount() > 0){
14091             var r = this.store.getAt(0);
14092             var newValue = r.data[this.displayField];
14093             var len = newValue.length;
14094             var selStart = this.getRawValue().length;
14095             
14096             if(selStart != len){
14097                 this.setRawValue(newValue);
14098                 this.selectText(selStart, newValue.length);
14099             }
14100         }
14101     },
14102
14103     // private
14104     onSelect : function(record, index){
14105         
14106         if(this.fireEvent('beforeselect', this, record, index) !== false){
14107         
14108             this.setFromData(index > -1 ? record.data : false);
14109             
14110             this.collapse();
14111             this.fireEvent('select', this, record, index);
14112         }
14113     },
14114
14115     /**
14116      * Returns the currently selected field value or empty string if no value is set.
14117      * @return {String} value The selected value
14118      */
14119     getValue : function()
14120     {
14121         if(Roo.isIOS && this.useNativeIOS){
14122             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14123         }
14124         
14125         if(this.multiple){
14126             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14127         }
14128         
14129         if(this.valueField){
14130             return typeof this.value != 'undefined' ? this.value : '';
14131         }else{
14132             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14133         }
14134     },
14135     
14136     getRawValue : function()
14137     {
14138         if(Roo.isIOS && this.useNativeIOS){
14139             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14140         }
14141         
14142         var v = this.inputEl().getValue();
14143         
14144         return v;
14145     },
14146
14147     /**
14148      * Clears any text/value currently set in the field
14149      */
14150     clearValue : function(){
14151         
14152         if(this.hiddenField){
14153             this.hiddenField.dom.value = '';
14154         }
14155         this.value = '';
14156         this.setRawValue('');
14157         this.lastSelectionText = '';
14158         this.lastData = false;
14159         
14160         var close = this.closeTriggerEl();
14161         
14162         if(close){
14163             close.hide();
14164         }
14165         
14166         this.validate();
14167         
14168     },
14169
14170     /**
14171      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14172      * will be displayed in the field.  If the value does not match the data value of an existing item,
14173      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14174      * Otherwise the field will be blank (although the value will still be set).
14175      * @param {String} value The value to match
14176      */
14177     setValue : function(v)
14178     {
14179         if(Roo.isIOS && this.useNativeIOS){
14180             this.setIOSValue(v);
14181             return;
14182         }
14183         
14184         if(this.multiple){
14185             this.syncValue();
14186             return;
14187         }
14188         
14189         var text = v;
14190         if(this.valueField){
14191             var r = this.findRecord(this.valueField, v);
14192             if(r){
14193                 text = r.data[this.displayField];
14194             }else if(this.valueNotFoundText !== undefined){
14195                 text = this.valueNotFoundText;
14196             }
14197         }
14198         this.lastSelectionText = text;
14199         if(this.hiddenField){
14200             this.hiddenField.dom.value = v;
14201         }
14202         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14203         this.value = v;
14204         
14205         var close = this.closeTriggerEl();
14206         
14207         if(close){
14208             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14209         }
14210         
14211         this.validate();
14212     },
14213     /**
14214      * @property {Object} the last set data for the element
14215      */
14216     
14217     lastData : false,
14218     /**
14219      * Sets the value of the field based on a object which is related to the record format for the store.
14220      * @param {Object} value the value to set as. or false on reset?
14221      */
14222     setFromData : function(o){
14223         
14224         if(this.multiple){
14225             this.addItem(o);
14226             return;
14227         }
14228             
14229         var dv = ''; // display value
14230         var vv = ''; // value value..
14231         this.lastData = o;
14232         if (this.displayField) {
14233             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14234         } else {
14235             // this is an error condition!!!
14236             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14237         }
14238         
14239         if(this.valueField){
14240             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14241         }
14242         
14243         var close = this.closeTriggerEl();
14244         
14245         if(close){
14246             if(dv.length || vv * 1 > 0){
14247                 close.show() ;
14248                 this.blockFocus=true;
14249             } else {
14250                 close.hide();
14251             }             
14252         }
14253         
14254         if(this.hiddenField){
14255             this.hiddenField.dom.value = vv;
14256             
14257             this.lastSelectionText = dv;
14258             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14259             this.value = vv;
14260             return;
14261         }
14262         // no hidden field.. - we store the value in 'value', but still display
14263         // display field!!!!
14264         this.lastSelectionText = dv;
14265         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14266         this.value = vv;
14267         
14268         
14269         
14270     },
14271     // private
14272     reset : function(){
14273         // overridden so that last data is reset..
14274         
14275         if(this.multiple){
14276             this.clearItem();
14277             return;
14278         }
14279         
14280         this.setValue(this.originalValue);
14281         //this.clearInvalid();
14282         this.lastData = false;
14283         if (this.view) {
14284             this.view.clearSelections();
14285         }
14286         
14287         this.validate();
14288     },
14289     // private
14290     findRecord : function(prop, value){
14291         var record;
14292         if(this.store.getCount() > 0){
14293             this.store.each(function(r){
14294                 if(r.data[prop] == value){
14295                     record = r;
14296                     return false;
14297                 }
14298                 return true;
14299             });
14300         }
14301         return record;
14302     },
14303     
14304     getName: function()
14305     {
14306         // returns hidden if it's set..
14307         if (!this.rendered) {return ''};
14308         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14309         
14310     },
14311     // private
14312     onViewMove : function(e, t){
14313         this.inKeyMode = false;
14314     },
14315
14316     // private
14317     onViewOver : function(e, t){
14318         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14319             return;
14320         }
14321         var item = this.view.findItemFromChild(t);
14322         
14323         if(item){
14324             var index = this.view.indexOf(item);
14325             this.select(index, false);
14326         }
14327     },
14328
14329     // private
14330     onViewClick : function(view, doFocus, el, e)
14331     {
14332         var index = this.view.getSelectedIndexes()[0];
14333         
14334         var r = this.store.getAt(index);
14335         
14336         if(this.tickable){
14337             
14338             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14339                 return;
14340             }
14341             
14342             var rm = false;
14343             var _this = this;
14344             
14345             Roo.each(this.tickItems, function(v,k){
14346                 
14347                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14348                     Roo.log(v);
14349                     _this.tickItems.splice(k, 1);
14350                     
14351                     if(typeof(e) == 'undefined' && view == false){
14352                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14353                     }
14354                     
14355                     rm = true;
14356                     return;
14357                 }
14358             });
14359             
14360             if(rm){
14361                 return;
14362             }
14363             
14364             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14365                 this.tickItems.push(r.data);
14366             }
14367             
14368             if(typeof(e) == 'undefined' && view == false){
14369                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14370             }
14371                     
14372             return;
14373         }
14374         
14375         if(r){
14376             this.onSelect(r, index);
14377         }
14378         if(doFocus !== false && !this.blockFocus){
14379             this.inputEl().focus();
14380         }
14381     },
14382
14383     // private
14384     restrictHeight : function(){
14385         //this.innerList.dom.style.height = '';
14386         //var inner = this.innerList.dom;
14387         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14388         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14389         //this.list.beginUpdate();
14390         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14391         this.list.alignTo(this.inputEl(), this.listAlign);
14392         this.list.alignTo(this.inputEl(), this.listAlign);
14393         //this.list.endUpdate();
14394     },
14395
14396     // private
14397     onEmptyResults : function(){
14398         
14399         if(this.tickable && this.editable){
14400             this.hasFocus = false;
14401             this.restrictHeight();
14402             return;
14403         }
14404         
14405         this.collapse();
14406     },
14407
14408     /**
14409      * Returns true if the dropdown list is expanded, else false.
14410      */
14411     isExpanded : function(){
14412         return this.list.isVisible();
14413     },
14414
14415     /**
14416      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14417      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14418      * @param {String} value The data value of the item to select
14419      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14420      * selected item if it is not currently in view (defaults to true)
14421      * @return {Boolean} True if the value matched an item in the list, else false
14422      */
14423     selectByValue : function(v, scrollIntoView){
14424         if(v !== undefined && v !== null){
14425             var r = this.findRecord(this.valueField || this.displayField, v);
14426             if(r){
14427                 this.select(this.store.indexOf(r), scrollIntoView);
14428                 return true;
14429             }
14430         }
14431         return false;
14432     },
14433
14434     /**
14435      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14436      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14437      * @param {Number} index The zero-based index of the list item to select
14438      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14439      * selected item if it is not currently in view (defaults to true)
14440      */
14441     select : function(index, scrollIntoView){
14442         this.selectedIndex = index;
14443         this.view.select(index);
14444         if(scrollIntoView !== false){
14445             var el = this.view.getNode(index);
14446             /*
14447              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14448              */
14449             if(el){
14450                 this.list.scrollChildIntoView(el, false);
14451             }
14452         }
14453     },
14454
14455     // private
14456     selectNext : function(){
14457         var ct = this.store.getCount();
14458         if(ct > 0){
14459             if(this.selectedIndex == -1){
14460                 this.select(0);
14461             }else if(this.selectedIndex < ct-1){
14462                 this.select(this.selectedIndex+1);
14463             }
14464         }
14465     },
14466
14467     // private
14468     selectPrev : function(){
14469         var ct = this.store.getCount();
14470         if(ct > 0){
14471             if(this.selectedIndex == -1){
14472                 this.select(0);
14473             }else if(this.selectedIndex != 0){
14474                 this.select(this.selectedIndex-1);
14475             }
14476         }
14477     },
14478
14479     // private
14480     onKeyUp : function(e){
14481         if(this.editable !== false && !e.isSpecialKey()){
14482             this.lastKey = e.getKey();
14483             this.dqTask.delay(this.queryDelay);
14484         }
14485     },
14486
14487     // private
14488     validateBlur : function(){
14489         return !this.list || !this.list.isVisible();   
14490     },
14491
14492     // private
14493     initQuery : function(){
14494         
14495         var v = this.getRawValue();
14496         
14497         if(this.tickable && this.editable){
14498             v = this.tickableInputEl().getValue();
14499         }
14500         
14501         this.doQuery(v);
14502     },
14503
14504     // private
14505     doForce : function(){
14506         if(this.inputEl().dom.value.length > 0){
14507             this.inputEl().dom.value =
14508                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14509              
14510         }
14511     },
14512
14513     /**
14514      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14515      * query allowing the query action to be canceled if needed.
14516      * @param {String} query The SQL query to execute
14517      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14518      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14519      * saved in the current store (defaults to false)
14520      */
14521     doQuery : function(q, forceAll){
14522         
14523         if(q === undefined || q === null){
14524             q = '';
14525         }
14526         var qe = {
14527             query: q,
14528             forceAll: forceAll,
14529             combo: this,
14530             cancel:false
14531         };
14532         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14533             return false;
14534         }
14535         q = qe.query;
14536         
14537         forceAll = qe.forceAll;
14538         if(forceAll === true || (q.length >= this.minChars)){
14539             
14540             this.hasQuery = true;
14541             
14542             if(this.lastQuery != q || this.alwaysQuery){
14543                 this.lastQuery = q;
14544                 if(this.mode == 'local'){
14545                     this.selectedIndex = -1;
14546                     if(forceAll){
14547                         this.store.clearFilter();
14548                     }else{
14549                         
14550                         if(this.specialFilter){
14551                             this.fireEvent('specialfilter', this);
14552                             this.onLoad();
14553                             return;
14554                         }
14555                         
14556                         this.store.filter(this.displayField, q);
14557                     }
14558                     
14559                     this.store.fireEvent("datachanged", this.store);
14560                     
14561                     this.onLoad();
14562                     
14563                     
14564                 }else{
14565                     
14566                     this.store.baseParams[this.queryParam] = q;
14567                     
14568                     var options = {params : this.getParams(q)};
14569                     
14570                     if(this.loadNext){
14571                         options.add = true;
14572                         options.params.start = this.page * this.pageSize;
14573                     }
14574                     
14575                     this.store.load(options);
14576                     
14577                     /*
14578                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14579                      *  we should expand the list on onLoad
14580                      *  so command out it
14581                      */
14582 //                    this.expand();
14583                 }
14584             }else{
14585                 this.selectedIndex = -1;
14586                 this.onLoad();   
14587             }
14588         }
14589         
14590         this.loadNext = false;
14591     },
14592     
14593     // private
14594     getParams : function(q){
14595         var p = {};
14596         //p[this.queryParam] = q;
14597         
14598         if(this.pageSize){
14599             p.start = 0;
14600             p.limit = this.pageSize;
14601         }
14602         return p;
14603     },
14604
14605     /**
14606      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14607      */
14608     collapse : function(){
14609         if(!this.isExpanded()){
14610             return;
14611         }
14612         
14613         this.list.hide();
14614         
14615         this.hasFocus = false;
14616         
14617         if(this.tickable){
14618             this.okBtn.hide();
14619             this.cancelBtn.hide();
14620             this.trigger.show();
14621             
14622             if(this.editable){
14623                 this.tickableInputEl().dom.value = '';
14624                 this.tickableInputEl().blur();
14625             }
14626             
14627         }
14628         
14629         Roo.get(document).un('mousedown', this.collapseIf, this);
14630         Roo.get(document).un('mousewheel', this.collapseIf, this);
14631         if (!this.editable) {
14632             Roo.get(document).un('keydown', this.listKeyPress, this);
14633         }
14634         this.fireEvent('collapse', this);
14635         
14636         this.validate();
14637     },
14638
14639     // private
14640     collapseIf : function(e){
14641         var in_combo  = e.within(this.el);
14642         var in_list =  e.within(this.list);
14643         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14644         
14645         if (in_combo || in_list || is_list) {
14646             //e.stopPropagation();
14647             return;
14648         }
14649         
14650         if(this.tickable){
14651             this.onTickableFooterButtonClick(e, false, false);
14652         }
14653
14654         this.collapse();
14655         
14656     },
14657
14658     /**
14659      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14660      */
14661     expand : function(){
14662        
14663         if(this.isExpanded() || !this.hasFocus){
14664             return;
14665         }
14666         
14667         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14668         this.list.setWidth(lw);
14669         
14670         Roo.log('expand');
14671         
14672         this.list.show();
14673         
14674         this.restrictHeight();
14675         
14676         if(this.tickable){
14677             
14678             this.tickItems = Roo.apply([], this.item);
14679             
14680             this.okBtn.show();
14681             this.cancelBtn.show();
14682             this.trigger.hide();
14683             
14684             if(this.editable){
14685                 this.tickableInputEl().focus();
14686             }
14687             
14688         }
14689         
14690         Roo.get(document).on('mousedown', this.collapseIf, this);
14691         Roo.get(document).on('mousewheel', this.collapseIf, this);
14692         if (!this.editable) {
14693             Roo.get(document).on('keydown', this.listKeyPress, this);
14694         }
14695         
14696         this.fireEvent('expand', this);
14697     },
14698
14699     // private
14700     // Implements the default empty TriggerField.onTriggerClick function
14701     onTriggerClick : function(e)
14702     {
14703         Roo.log('trigger click');
14704         
14705         if(this.disabled || !this.triggerList){
14706             return;
14707         }
14708         
14709         this.page = 0;
14710         this.loadNext = false;
14711         
14712         if(this.isExpanded()){
14713             this.collapse();
14714             if (!this.blockFocus) {
14715                 this.inputEl().focus();
14716             }
14717             
14718         }else {
14719             this.hasFocus = true;
14720             if(this.triggerAction == 'all') {
14721                 this.doQuery(this.allQuery, true);
14722             } else {
14723                 this.doQuery(this.getRawValue());
14724             }
14725             if (!this.blockFocus) {
14726                 this.inputEl().focus();
14727             }
14728         }
14729     },
14730     
14731     onTickableTriggerClick : function(e)
14732     {
14733         if(this.disabled){
14734             return;
14735         }
14736         
14737         this.page = 0;
14738         this.loadNext = false;
14739         this.hasFocus = true;
14740         
14741         if(this.triggerAction == 'all') {
14742             this.doQuery(this.allQuery, true);
14743         } else {
14744             this.doQuery(this.getRawValue());
14745         }
14746     },
14747     
14748     onSearchFieldClick : function(e)
14749     {
14750         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14751             this.onTickableFooterButtonClick(e, false, false);
14752             return;
14753         }
14754         
14755         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14756             return;
14757         }
14758         
14759         this.page = 0;
14760         this.loadNext = false;
14761         this.hasFocus = true;
14762         
14763         if(this.triggerAction == 'all') {
14764             this.doQuery(this.allQuery, true);
14765         } else {
14766             this.doQuery(this.getRawValue());
14767         }
14768     },
14769     
14770     listKeyPress : function(e)
14771     {
14772         //Roo.log('listkeypress');
14773         // scroll to first matching element based on key pres..
14774         if (e.isSpecialKey()) {
14775             return false;
14776         }
14777         var k = String.fromCharCode(e.getKey()).toUpperCase();
14778         //Roo.log(k);
14779         var match  = false;
14780         var csel = this.view.getSelectedNodes();
14781         var cselitem = false;
14782         if (csel.length) {
14783             var ix = this.view.indexOf(csel[0]);
14784             cselitem  = this.store.getAt(ix);
14785             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14786                 cselitem = false;
14787             }
14788             
14789         }
14790         
14791         this.store.each(function(v) { 
14792             if (cselitem) {
14793                 // start at existing selection.
14794                 if (cselitem.id == v.id) {
14795                     cselitem = false;
14796                 }
14797                 return true;
14798             }
14799                 
14800             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14801                 match = this.store.indexOf(v);
14802                 return false;
14803             }
14804             return true;
14805         }, this);
14806         
14807         if (match === false) {
14808             return true; // no more action?
14809         }
14810         // scroll to?
14811         this.view.select(match);
14812         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14813         sn.scrollIntoView(sn.dom.parentNode, false);
14814     },
14815     
14816     onViewScroll : function(e, t){
14817         
14818         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){
14819             return;
14820         }
14821         
14822         this.hasQuery = true;
14823         
14824         this.loading = this.list.select('.loading', true).first();
14825         
14826         if(this.loading === null){
14827             this.list.createChild({
14828                 tag: 'div',
14829                 cls: 'loading roo-select2-more-results roo-select2-active',
14830                 html: 'Loading more results...'
14831             });
14832             
14833             this.loading = this.list.select('.loading', true).first();
14834             
14835             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14836             
14837             this.loading.hide();
14838         }
14839         
14840         this.loading.show();
14841         
14842         var _combo = this;
14843         
14844         this.page++;
14845         this.loadNext = true;
14846         
14847         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14848         
14849         return;
14850     },
14851     
14852     addItem : function(o)
14853     {   
14854         var dv = ''; // display value
14855         
14856         if (this.displayField) {
14857             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14858         } else {
14859             // this is an error condition!!!
14860             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14861         }
14862         
14863         if(!dv.length){
14864             return;
14865         }
14866         
14867         var choice = this.choices.createChild({
14868             tag: 'li',
14869             cls: 'roo-select2-search-choice',
14870             cn: [
14871                 {
14872                     tag: 'div',
14873                     html: dv
14874                 },
14875                 {
14876                     tag: 'a',
14877                     href: '#',
14878                     cls: 'roo-select2-search-choice-close fa fa-times',
14879                     tabindex: '-1'
14880                 }
14881             ]
14882             
14883         }, this.searchField);
14884         
14885         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14886         
14887         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14888         
14889         this.item.push(o);
14890         
14891         this.lastData = o;
14892         
14893         this.syncValue();
14894         
14895         this.inputEl().dom.value = '';
14896         
14897         this.validate();
14898     },
14899     
14900     onRemoveItem : function(e, _self, o)
14901     {
14902         e.preventDefault();
14903         
14904         this.lastItem = Roo.apply([], this.item);
14905         
14906         var index = this.item.indexOf(o.data) * 1;
14907         
14908         if( index < 0){
14909             Roo.log('not this item?!');
14910             return;
14911         }
14912         
14913         this.item.splice(index, 1);
14914         o.item.remove();
14915         
14916         this.syncValue();
14917         
14918         this.fireEvent('remove', this, e);
14919         
14920         this.validate();
14921         
14922     },
14923     
14924     syncValue : function()
14925     {
14926         if(!this.item.length){
14927             this.clearValue();
14928             return;
14929         }
14930             
14931         var value = [];
14932         var _this = this;
14933         Roo.each(this.item, function(i){
14934             if(_this.valueField){
14935                 value.push(i[_this.valueField]);
14936                 return;
14937             }
14938
14939             value.push(i);
14940         });
14941
14942         this.value = value.join(',');
14943
14944         if(this.hiddenField){
14945             this.hiddenField.dom.value = this.value;
14946         }
14947         
14948         this.store.fireEvent("datachanged", this.store);
14949         
14950         this.validate();
14951     },
14952     
14953     clearItem : function()
14954     {
14955         if(!this.multiple){
14956             return;
14957         }
14958         
14959         this.item = [];
14960         
14961         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14962            c.remove();
14963         });
14964         
14965         this.syncValue();
14966         
14967         this.validate();
14968         
14969         if(this.tickable && !Roo.isTouch){
14970             this.view.refresh();
14971         }
14972     },
14973     
14974     inputEl: function ()
14975     {
14976         if(Roo.isIOS && this.useNativeIOS){
14977             return this.el.select('select.roo-ios-select', true).first();
14978         }
14979         
14980         if(Roo.isTouch && this.mobileTouchView){
14981             return this.el.select('input.form-control',true).first();
14982         }
14983         
14984         if(this.tickable){
14985             return this.searchField;
14986         }
14987         
14988         return this.el.select('input.form-control',true).first();
14989     },
14990     
14991     onTickableFooterButtonClick : function(e, btn, el)
14992     {
14993         e.preventDefault();
14994         
14995         this.lastItem = Roo.apply([], this.item);
14996         
14997         if(btn && btn.name == 'cancel'){
14998             this.tickItems = Roo.apply([], this.item);
14999             this.collapse();
15000             return;
15001         }
15002         
15003         this.clearItem();
15004         
15005         var _this = this;
15006         
15007         Roo.each(this.tickItems, function(o){
15008             _this.addItem(o);
15009         });
15010         
15011         this.collapse();
15012         
15013     },
15014     
15015     validate : function()
15016     {
15017         if(this.getVisibilityEl().hasClass('hidden')){
15018             return true;
15019         }
15020         
15021         var v = this.getRawValue();
15022         
15023         if(this.multiple){
15024             v = this.getValue();
15025         }
15026         
15027         if(this.disabled || this.allowBlank || v.length){
15028             this.markValid();
15029             return true;
15030         }
15031         
15032         this.markInvalid();
15033         return false;
15034     },
15035     
15036     tickableInputEl : function()
15037     {
15038         if(!this.tickable || !this.editable){
15039             return this.inputEl();
15040         }
15041         
15042         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15043     },
15044     
15045     
15046     getAutoCreateTouchView : function()
15047     {
15048         var id = Roo.id();
15049         
15050         var cfg = {
15051             cls: 'form-group' //input-group
15052         };
15053         
15054         var input =  {
15055             tag: 'input',
15056             id : id,
15057             type : this.inputType,
15058             cls : 'form-control x-combo-noedit',
15059             autocomplete: 'new-password',
15060             placeholder : this.placeholder || '',
15061             readonly : true
15062         };
15063         
15064         if (this.name) {
15065             input.name = this.name;
15066         }
15067         
15068         if (this.size) {
15069             input.cls += ' input-' + this.size;
15070         }
15071         
15072         if (this.disabled) {
15073             input.disabled = true;
15074         }
15075         
15076         var inputblock = {
15077             cls : '',
15078             cn : [
15079                 input
15080             ]
15081         };
15082         
15083         if(this.before){
15084             inputblock.cls += ' input-group';
15085             
15086             inputblock.cn.unshift({
15087                 tag :'span',
15088                 cls : 'input-group-addon input-group-prepend input-group-text',
15089                 html : this.before
15090             });
15091         }
15092         
15093         if(this.removable && !this.multiple){
15094             inputblock.cls += ' roo-removable';
15095             
15096             inputblock.cn.push({
15097                 tag: 'button',
15098                 html : 'x',
15099                 cls : 'roo-combo-removable-btn close'
15100             });
15101         }
15102
15103         if(this.hasFeedback && !this.allowBlank){
15104             
15105             inputblock.cls += ' has-feedback';
15106             
15107             inputblock.cn.push({
15108                 tag: 'span',
15109                 cls: 'glyphicon form-control-feedback'
15110             });
15111             
15112         }
15113         
15114         if (this.after) {
15115             
15116             inputblock.cls += (this.before) ? '' : ' input-group';
15117             
15118             inputblock.cn.push({
15119                 tag :'span',
15120                 cls : 'input-group-addon input-group-append input-group-text',
15121                 html : this.after
15122             });
15123         }
15124
15125         var box = {
15126             tag: 'div',
15127             cn: [
15128                 {
15129                     tag: 'input',
15130                     type : 'hidden',
15131                     cls: 'form-hidden-field'
15132                 },
15133                 inputblock
15134             ]
15135             
15136         };
15137         
15138         if(this.multiple){
15139             box = {
15140                 tag: 'div',
15141                 cn: [
15142                     {
15143                         tag: 'input',
15144                         type : 'hidden',
15145                         cls: 'form-hidden-field'
15146                     },
15147                     {
15148                         tag: 'ul',
15149                         cls: 'roo-select2-choices',
15150                         cn:[
15151                             {
15152                                 tag: 'li',
15153                                 cls: 'roo-select2-search-field',
15154                                 cn: [
15155
15156                                     inputblock
15157                                 ]
15158                             }
15159                         ]
15160                     }
15161                 ]
15162             }
15163         };
15164         
15165         var combobox = {
15166             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15167             cn: [
15168                 box
15169             ]
15170         };
15171         
15172         if(!this.multiple && this.showToggleBtn){
15173             
15174             var caret = {
15175                         tag: 'span',
15176                         cls: 'caret'
15177             };
15178             
15179             if (this.caret != false) {
15180                 caret = {
15181                      tag: 'i',
15182                      cls: 'fa fa-' + this.caret
15183                 };
15184                 
15185             }
15186             
15187             combobox.cn.push({
15188                 tag :'span',
15189                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15190                 cn : [
15191                     caret,
15192                     {
15193                         tag: 'span',
15194                         cls: 'combobox-clear',
15195                         cn  : [
15196                             {
15197                                 tag : 'i',
15198                                 cls: 'icon-remove'
15199                             }
15200                         ]
15201                     }
15202                 ]
15203
15204             })
15205         }
15206         
15207         if(this.multiple){
15208             combobox.cls += ' roo-select2-container-multi';
15209         }
15210         
15211         var align = this.labelAlign || this.parentLabelAlign();
15212         
15213         if (align ==='left' && this.fieldLabel.length) {
15214
15215             cfg.cn = [
15216                 {
15217                    tag : 'i',
15218                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15219                    tooltip : 'This field is required'
15220                 },
15221                 {
15222                     tag: 'label',
15223                     cls : 'control-label col-form-label',
15224                     html : this.fieldLabel
15225
15226                 },
15227                 {
15228                     cls : '', 
15229                     cn: [
15230                         combobox
15231                     ]
15232                 }
15233             ];
15234             
15235             var labelCfg = cfg.cn[1];
15236             var contentCfg = cfg.cn[2];
15237             
15238
15239             if(this.indicatorpos == 'right'){
15240                 cfg.cn = [
15241                     {
15242                         tag: 'label',
15243                         'for' :  id,
15244                         cls : 'control-label col-form-label',
15245                         cn : [
15246                             {
15247                                 tag : 'span',
15248                                 html : this.fieldLabel
15249                             },
15250                             {
15251                                 tag : 'i',
15252                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15253                                 tooltip : 'This field is required'
15254                             }
15255                         ]
15256                     },
15257                     {
15258                         cls : "",
15259                         cn: [
15260                             combobox
15261                         ]
15262                     }
15263
15264                 ];
15265                 
15266                 labelCfg = cfg.cn[0];
15267                 contentCfg = cfg.cn[1];
15268             }
15269             
15270            
15271             
15272             if(this.labelWidth > 12){
15273                 labelCfg.style = "width: " + this.labelWidth + 'px';
15274             }
15275             
15276             if(this.labelWidth < 13 && this.labelmd == 0){
15277                 this.labelmd = this.labelWidth;
15278             }
15279             
15280             if(this.labellg > 0){
15281                 labelCfg.cls += ' col-lg-' + this.labellg;
15282                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15283             }
15284             
15285             if(this.labelmd > 0){
15286                 labelCfg.cls += ' col-md-' + this.labelmd;
15287                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15288             }
15289             
15290             if(this.labelsm > 0){
15291                 labelCfg.cls += ' col-sm-' + this.labelsm;
15292                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15293             }
15294             
15295             if(this.labelxs > 0){
15296                 labelCfg.cls += ' col-xs-' + this.labelxs;
15297                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15298             }
15299                 
15300                 
15301         } else if ( this.fieldLabel.length) {
15302             cfg.cn = [
15303                 {
15304                    tag : 'i',
15305                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15306                    tooltip : 'This field is required'
15307                 },
15308                 {
15309                     tag: 'label',
15310                     cls : 'control-label',
15311                     html : this.fieldLabel
15312
15313                 },
15314                 {
15315                     cls : '', 
15316                     cn: [
15317                         combobox
15318                     ]
15319                 }
15320             ];
15321             
15322             if(this.indicatorpos == 'right'){
15323                 cfg.cn = [
15324                     {
15325                         tag: 'label',
15326                         cls : 'control-label',
15327                         html : this.fieldLabel,
15328                         cn : [
15329                             {
15330                                tag : 'i',
15331                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15332                                tooltip : 'This field is required'
15333                             }
15334                         ]
15335                     },
15336                     {
15337                         cls : '', 
15338                         cn: [
15339                             combobox
15340                         ]
15341                     }
15342                 ];
15343             }
15344         } else {
15345             cfg.cn = combobox;    
15346         }
15347         
15348         
15349         var settings = this;
15350         
15351         ['xs','sm','md','lg'].map(function(size){
15352             if (settings[size]) {
15353                 cfg.cls += ' col-' + size + '-' + settings[size];
15354             }
15355         });
15356         
15357         return cfg;
15358     },
15359     
15360     initTouchView : function()
15361     {
15362         this.renderTouchView();
15363         
15364         this.touchViewEl.on('scroll', function(){
15365             this.el.dom.scrollTop = 0;
15366         }, this);
15367         
15368         this.originalValue = this.getValue();
15369         
15370         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15371         
15372         this.inputEl().on("click", this.showTouchView, this);
15373         if (this.triggerEl) {
15374             this.triggerEl.on("click", this.showTouchView, this);
15375         }
15376         
15377         
15378         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15379         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15380         
15381         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15382         
15383         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15384         this.store.on('load', this.onTouchViewLoad, this);
15385         this.store.on('loadexception', this.onTouchViewLoadException, this);
15386         
15387         if(this.hiddenName){
15388             
15389             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15390             
15391             this.hiddenField.dom.value =
15392                 this.hiddenValue !== undefined ? this.hiddenValue :
15393                 this.value !== undefined ? this.value : '';
15394         
15395             this.el.dom.removeAttribute('name');
15396             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15397         }
15398         
15399         if(this.multiple){
15400             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15401             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15402         }
15403         
15404         if(this.removable && !this.multiple){
15405             var close = this.closeTriggerEl();
15406             if(close){
15407                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15408                 close.on('click', this.removeBtnClick, this, close);
15409             }
15410         }
15411         /*
15412          * fix the bug in Safari iOS8
15413          */
15414         this.inputEl().on("focus", function(e){
15415             document.activeElement.blur();
15416         }, this);
15417         
15418         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15419         
15420         return;
15421         
15422         
15423     },
15424     
15425     renderTouchView : function()
15426     {
15427         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15428         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15429         
15430         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15431         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15432         
15433         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15434         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15435         this.touchViewBodyEl.setStyle('overflow', 'auto');
15436         
15437         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15438         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15439         
15440         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15441         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15442         
15443     },
15444     
15445     showTouchView : function()
15446     {
15447         if(this.disabled){
15448             return;
15449         }
15450         
15451         this.touchViewHeaderEl.hide();
15452
15453         if(this.modalTitle.length){
15454             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15455             this.touchViewHeaderEl.show();
15456         }
15457
15458         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15459         this.touchViewEl.show();
15460
15461         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15462         
15463         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15464         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15465
15466         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15467
15468         if(this.modalTitle.length){
15469             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15470         }
15471         
15472         this.touchViewBodyEl.setHeight(bodyHeight);
15473
15474         if(this.animate){
15475             var _this = this;
15476             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15477         }else{
15478             this.touchViewEl.addClass('in');
15479         }
15480         
15481         if(this._touchViewMask){
15482             Roo.get(document.body).addClass("x-body-masked");
15483             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15484             this._touchViewMask.setStyle('z-index', 10000);
15485             this._touchViewMask.addClass('show');
15486         }
15487         
15488         this.doTouchViewQuery();
15489         
15490     },
15491     
15492     hideTouchView : function()
15493     {
15494         this.touchViewEl.removeClass('in');
15495
15496         if(this.animate){
15497             var _this = this;
15498             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15499         }else{
15500             this.touchViewEl.setStyle('display', 'none');
15501         }
15502         
15503         if(this._touchViewMask){
15504             this._touchViewMask.removeClass('show');
15505             Roo.get(document.body).removeClass("x-body-masked");
15506         }
15507     },
15508     
15509     setTouchViewValue : function()
15510     {
15511         if(this.multiple){
15512             this.clearItem();
15513         
15514             var _this = this;
15515
15516             Roo.each(this.tickItems, function(o){
15517                 this.addItem(o);
15518             }, this);
15519         }
15520         
15521         this.hideTouchView();
15522     },
15523     
15524     doTouchViewQuery : function()
15525     {
15526         var qe = {
15527             query: '',
15528             forceAll: true,
15529             combo: this,
15530             cancel:false
15531         };
15532         
15533         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15534             return false;
15535         }
15536         
15537         if(!this.alwaysQuery || this.mode == 'local'){
15538             this.onTouchViewLoad();
15539             return;
15540         }
15541         
15542         this.store.load();
15543     },
15544     
15545     onTouchViewBeforeLoad : function(combo,opts)
15546     {
15547         return;
15548     },
15549
15550     // private
15551     onTouchViewLoad : function()
15552     {
15553         if(this.store.getCount() < 1){
15554             this.onTouchViewEmptyResults();
15555             return;
15556         }
15557         
15558         this.clearTouchView();
15559         
15560         var rawValue = this.getRawValue();
15561         
15562         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15563         
15564         this.tickItems = [];
15565         
15566         this.store.data.each(function(d, rowIndex){
15567             var row = this.touchViewListGroup.createChild(template);
15568             
15569             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15570                 row.addClass(d.data.cls);
15571             }
15572             
15573             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15574                 var cfg = {
15575                     data : d.data,
15576                     html : d.data[this.displayField]
15577                 };
15578                 
15579                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15580                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15581                 }
15582             }
15583             row.removeClass('selected');
15584             if(!this.multiple && this.valueField &&
15585                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15586             {
15587                 // radio buttons..
15588                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15589                 row.addClass('selected');
15590             }
15591             
15592             if(this.multiple && this.valueField &&
15593                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15594             {
15595                 
15596                 // checkboxes...
15597                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15598                 this.tickItems.push(d.data);
15599             }
15600             
15601             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15602             
15603         }, this);
15604         
15605         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15606         
15607         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15608
15609         if(this.modalTitle.length){
15610             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15611         }
15612
15613         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15614         
15615         if(this.mobile_restrict_height && listHeight < bodyHeight){
15616             this.touchViewBodyEl.setHeight(listHeight);
15617         }
15618         
15619         var _this = this;
15620         
15621         if(firstChecked && listHeight > bodyHeight){
15622             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15623         }
15624         
15625     },
15626     
15627     onTouchViewLoadException : function()
15628     {
15629         this.hideTouchView();
15630     },
15631     
15632     onTouchViewEmptyResults : function()
15633     {
15634         this.clearTouchView();
15635         
15636         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15637         
15638         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15639         
15640     },
15641     
15642     clearTouchView : function()
15643     {
15644         this.touchViewListGroup.dom.innerHTML = '';
15645     },
15646     
15647     onTouchViewClick : function(e, el, o)
15648     {
15649         e.preventDefault();
15650         
15651         var row = o.row;
15652         var rowIndex = o.rowIndex;
15653         
15654         var r = this.store.getAt(rowIndex);
15655         
15656         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15657             
15658             if(!this.multiple){
15659                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15660                     c.dom.removeAttribute('checked');
15661                 }, this);
15662
15663                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15664
15665                 this.setFromData(r.data);
15666
15667                 var close = this.closeTriggerEl();
15668
15669                 if(close){
15670                     close.show();
15671                 }
15672
15673                 this.hideTouchView();
15674
15675                 this.fireEvent('select', this, r, rowIndex);
15676
15677                 return;
15678             }
15679
15680             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15681                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15682                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15683                 return;
15684             }
15685
15686             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15687             this.addItem(r.data);
15688             this.tickItems.push(r.data);
15689         }
15690     },
15691     
15692     getAutoCreateNativeIOS : function()
15693     {
15694         var cfg = {
15695             cls: 'form-group' //input-group,
15696         };
15697         
15698         var combobox =  {
15699             tag: 'select',
15700             cls : 'roo-ios-select'
15701         };
15702         
15703         if (this.name) {
15704             combobox.name = this.name;
15705         }
15706         
15707         if (this.disabled) {
15708             combobox.disabled = true;
15709         }
15710         
15711         var settings = this;
15712         
15713         ['xs','sm','md','lg'].map(function(size){
15714             if (settings[size]) {
15715                 cfg.cls += ' col-' + size + '-' + settings[size];
15716             }
15717         });
15718         
15719         cfg.cn = combobox;
15720         
15721         return cfg;
15722         
15723     },
15724     
15725     initIOSView : function()
15726     {
15727         this.store.on('load', this.onIOSViewLoad, this);
15728         
15729         return;
15730     },
15731     
15732     onIOSViewLoad : function()
15733     {
15734         if(this.store.getCount() < 1){
15735             return;
15736         }
15737         
15738         this.clearIOSView();
15739         
15740         if(this.allowBlank) {
15741             
15742             var default_text = '-- SELECT --';
15743             
15744             if(this.placeholder.length){
15745                 default_text = this.placeholder;
15746             }
15747             
15748             if(this.emptyTitle.length){
15749                 default_text += ' - ' + this.emptyTitle + ' -';
15750             }
15751             
15752             var opt = this.inputEl().createChild({
15753                 tag: 'option',
15754                 value : 0,
15755                 html : default_text
15756             });
15757             
15758             var o = {};
15759             o[this.valueField] = 0;
15760             o[this.displayField] = default_text;
15761             
15762             this.ios_options.push({
15763                 data : o,
15764                 el : opt
15765             });
15766             
15767         }
15768         
15769         this.store.data.each(function(d, rowIndex){
15770             
15771             var html = '';
15772             
15773             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15774                 html = d.data[this.displayField];
15775             }
15776             
15777             var value = '';
15778             
15779             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15780                 value = d.data[this.valueField];
15781             }
15782             
15783             var option = {
15784                 tag: 'option',
15785                 value : value,
15786                 html : html
15787             };
15788             
15789             if(this.value == d.data[this.valueField]){
15790                 option['selected'] = true;
15791             }
15792             
15793             var opt = this.inputEl().createChild(option);
15794             
15795             this.ios_options.push({
15796                 data : d.data,
15797                 el : opt
15798             });
15799             
15800         }, this);
15801         
15802         this.inputEl().on('change', function(){
15803            this.fireEvent('select', this);
15804         }, this);
15805         
15806     },
15807     
15808     clearIOSView: function()
15809     {
15810         this.inputEl().dom.innerHTML = '';
15811         
15812         this.ios_options = [];
15813     },
15814     
15815     setIOSValue: function(v)
15816     {
15817         this.value = v;
15818         
15819         if(!this.ios_options){
15820             return;
15821         }
15822         
15823         Roo.each(this.ios_options, function(opts){
15824            
15825            opts.el.dom.removeAttribute('selected');
15826            
15827            if(opts.data[this.valueField] != v){
15828                return;
15829            }
15830            
15831            opts.el.dom.setAttribute('selected', true);
15832            
15833         }, this);
15834     }
15835
15836     /** 
15837     * @cfg {Boolean} grow 
15838     * @hide 
15839     */
15840     /** 
15841     * @cfg {Number} growMin 
15842     * @hide 
15843     */
15844     /** 
15845     * @cfg {Number} growMax 
15846     * @hide 
15847     */
15848     /**
15849      * @hide
15850      * @method autoSize
15851      */
15852 });
15853
15854 Roo.apply(Roo.bootstrap.ComboBox,  {
15855     
15856     header : {
15857         tag: 'div',
15858         cls: 'modal-header',
15859         cn: [
15860             {
15861                 tag: 'h4',
15862                 cls: 'modal-title'
15863             }
15864         ]
15865     },
15866     
15867     body : {
15868         tag: 'div',
15869         cls: 'modal-body',
15870         cn: [
15871             {
15872                 tag: 'ul',
15873                 cls: 'list-group'
15874             }
15875         ]
15876     },
15877     
15878     listItemRadio : {
15879         tag: 'li',
15880         cls: 'list-group-item',
15881         cn: [
15882             {
15883                 tag: 'span',
15884                 cls: 'roo-combobox-list-group-item-value'
15885             },
15886             {
15887                 tag: 'div',
15888                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15889                 cn: [
15890                     {
15891                         tag: 'input',
15892                         type: 'radio'
15893                     },
15894                     {
15895                         tag: 'label'
15896                     }
15897                 ]
15898             }
15899         ]
15900     },
15901     
15902     listItemCheckbox : {
15903         tag: 'li',
15904         cls: 'list-group-item',
15905         cn: [
15906             {
15907                 tag: 'span',
15908                 cls: 'roo-combobox-list-group-item-value'
15909             },
15910             {
15911                 tag: 'div',
15912                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15913                 cn: [
15914                     {
15915                         tag: 'input',
15916                         type: 'checkbox'
15917                     },
15918                     {
15919                         tag: 'label'
15920                     }
15921                 ]
15922             }
15923         ]
15924     },
15925     
15926     emptyResult : {
15927         tag: 'div',
15928         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15929     },
15930     
15931     footer : {
15932         tag: 'div',
15933         cls: 'modal-footer',
15934         cn: [
15935             {
15936                 tag: 'div',
15937                 cls: 'row',
15938                 cn: [
15939                     {
15940                         tag: 'div',
15941                         cls: 'col-xs-6 text-left',
15942                         cn: {
15943                             tag: 'button',
15944                             cls: 'btn btn-danger roo-touch-view-cancel',
15945                             html: 'Cancel'
15946                         }
15947                     },
15948                     {
15949                         tag: 'div',
15950                         cls: 'col-xs-6 text-right',
15951                         cn: {
15952                             tag: 'button',
15953                             cls: 'btn btn-success roo-touch-view-ok',
15954                             html: 'OK'
15955                         }
15956                     }
15957                 ]
15958             }
15959         ]
15960         
15961     }
15962 });
15963
15964 Roo.apply(Roo.bootstrap.ComboBox,  {
15965     
15966     touchViewTemplate : {
15967         tag: 'div',
15968         cls: 'modal fade roo-combobox-touch-view',
15969         cn: [
15970             {
15971                 tag: 'div',
15972                 cls: 'modal-dialog',
15973                 style : 'position:fixed', // we have to fix position....
15974                 cn: [
15975                     {
15976                         tag: 'div',
15977                         cls: 'modal-content',
15978                         cn: [
15979                             Roo.bootstrap.ComboBox.header,
15980                             Roo.bootstrap.ComboBox.body,
15981                             Roo.bootstrap.ComboBox.footer
15982                         ]
15983                     }
15984                 ]
15985             }
15986         ]
15987     }
15988 });/*
15989  * Based on:
15990  * Ext JS Library 1.1.1
15991  * Copyright(c) 2006-2007, Ext JS, LLC.
15992  *
15993  * Originally Released Under LGPL - original licence link has changed is not relivant.
15994  *
15995  * Fork - LGPL
15996  * <script type="text/javascript">
15997  */
15998
15999 /**
16000  * @class Roo.View
16001  * @extends Roo.util.Observable
16002  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16003  * This class also supports single and multi selection modes. <br>
16004  * Create a data model bound view:
16005  <pre><code>
16006  var store = new Roo.data.Store(...);
16007
16008  var view = new Roo.View({
16009     el : "my-element",
16010     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16011  
16012     singleSelect: true,
16013     selectedClass: "ydataview-selected",
16014     store: store
16015  });
16016
16017  // listen for node click?
16018  view.on("click", function(vw, index, node, e){
16019  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16020  });
16021
16022  // load XML data
16023  dataModel.load("foobar.xml");
16024  </code></pre>
16025  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16026  * <br><br>
16027  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16028  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16029  * 
16030  * Note: old style constructor is still suported (container, template, config)
16031  * 
16032  * @constructor
16033  * Create a new View
16034  * @param {Object} config The config object
16035  * 
16036  */
16037 Roo.View = function(config, depreciated_tpl, depreciated_config){
16038     
16039     this.parent = false;
16040     
16041     if (typeof(depreciated_tpl) == 'undefined') {
16042         // new way.. - universal constructor.
16043         Roo.apply(this, config);
16044         this.el  = Roo.get(this.el);
16045     } else {
16046         // old format..
16047         this.el  = Roo.get(config);
16048         this.tpl = depreciated_tpl;
16049         Roo.apply(this, depreciated_config);
16050     }
16051     this.wrapEl  = this.el.wrap().wrap();
16052     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16053     
16054     
16055     if(typeof(this.tpl) == "string"){
16056         this.tpl = new Roo.Template(this.tpl);
16057     } else {
16058         // support xtype ctors..
16059         this.tpl = new Roo.factory(this.tpl, Roo);
16060     }
16061     
16062     
16063     this.tpl.compile();
16064     
16065     /** @private */
16066     this.addEvents({
16067         /**
16068          * @event beforeclick
16069          * Fires before a click is processed. Returns false to cancel the default action.
16070          * @param {Roo.View} this
16071          * @param {Number} index The index of the target node
16072          * @param {HTMLElement} node The target node
16073          * @param {Roo.EventObject} e The raw event object
16074          */
16075             "beforeclick" : true,
16076         /**
16077          * @event click
16078          * Fires when a template node is clicked.
16079          * @param {Roo.View} this
16080          * @param {Number} index The index of the target node
16081          * @param {HTMLElement} node The target node
16082          * @param {Roo.EventObject} e The raw event object
16083          */
16084             "click" : true,
16085         /**
16086          * @event dblclick
16087          * Fires when a template node is double clicked.
16088          * @param {Roo.View} this
16089          * @param {Number} index The index of the target node
16090          * @param {HTMLElement} node The target node
16091          * @param {Roo.EventObject} e The raw event object
16092          */
16093             "dblclick" : true,
16094         /**
16095          * @event contextmenu
16096          * Fires when a template node is right clicked.
16097          * @param {Roo.View} this
16098          * @param {Number} index The index of the target node
16099          * @param {HTMLElement} node The target node
16100          * @param {Roo.EventObject} e The raw event object
16101          */
16102             "contextmenu" : true,
16103         /**
16104          * @event selectionchange
16105          * Fires when the selected nodes change.
16106          * @param {Roo.View} this
16107          * @param {Array} selections Array of the selected nodes
16108          */
16109             "selectionchange" : true,
16110     
16111         /**
16112          * @event beforeselect
16113          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16114          * @param {Roo.View} this
16115          * @param {HTMLElement} node The node to be selected
16116          * @param {Array} selections Array of currently selected nodes
16117          */
16118             "beforeselect" : true,
16119         /**
16120          * @event preparedata
16121          * Fires on every row to render, to allow you to change the data.
16122          * @param {Roo.View} this
16123          * @param {Object} data to be rendered (change this)
16124          */
16125           "preparedata" : true
16126           
16127           
16128         });
16129
16130
16131
16132     this.el.on({
16133         "click": this.onClick,
16134         "dblclick": this.onDblClick,
16135         "contextmenu": this.onContextMenu,
16136         scope:this
16137     });
16138
16139     this.selections = [];
16140     this.nodes = [];
16141     this.cmp = new Roo.CompositeElementLite([]);
16142     if(this.store){
16143         this.store = Roo.factory(this.store, Roo.data);
16144         this.setStore(this.store, true);
16145     }
16146     
16147     if ( this.footer && this.footer.xtype) {
16148            
16149          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16150         
16151         this.footer.dataSource = this.store;
16152         this.footer.container = fctr;
16153         this.footer = Roo.factory(this.footer, Roo);
16154         fctr.insertFirst(this.el);
16155         
16156         // this is a bit insane - as the paging toolbar seems to detach the el..
16157 //        dom.parentNode.parentNode.parentNode
16158          // they get detached?
16159     }
16160     
16161     
16162     Roo.View.superclass.constructor.call(this);
16163     
16164     
16165 };
16166
16167 Roo.extend(Roo.View, Roo.util.Observable, {
16168     
16169      /**
16170      * @cfg {Roo.data.Store} store Data store to load data from.
16171      */
16172     store : false,
16173     
16174     /**
16175      * @cfg {String|Roo.Element} el The container element.
16176      */
16177     el : '',
16178     
16179     /**
16180      * @cfg {String|Roo.Template} tpl The template used by this View 
16181      */
16182     tpl : false,
16183     /**
16184      * @cfg {String} dataName the named area of the template to use as the data area
16185      *                          Works with domtemplates roo-name="name"
16186      */
16187     dataName: false,
16188     /**
16189      * @cfg {String} selectedClass The css class to add to selected nodes
16190      */
16191     selectedClass : "x-view-selected",
16192      /**
16193      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16194      */
16195     emptyText : "",
16196     
16197     /**
16198      * @cfg {String} text to display on mask (default Loading)
16199      */
16200     mask : false,
16201     /**
16202      * @cfg {Boolean} multiSelect Allow multiple selection
16203      */
16204     multiSelect : false,
16205     /**
16206      * @cfg {Boolean} singleSelect Allow single selection
16207      */
16208     singleSelect:  false,
16209     
16210     /**
16211      * @cfg {Boolean} toggleSelect - selecting 
16212      */
16213     toggleSelect : false,
16214     
16215     /**
16216      * @cfg {Boolean} tickable - selecting 
16217      */
16218     tickable : false,
16219     
16220     /**
16221      * Returns the element this view is bound to.
16222      * @return {Roo.Element}
16223      */
16224     getEl : function(){
16225         return this.wrapEl;
16226     },
16227     
16228     
16229
16230     /**
16231      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16232      */
16233     refresh : function(){
16234         //Roo.log('refresh');
16235         var t = this.tpl;
16236         
16237         // if we are using something like 'domtemplate', then
16238         // the what gets used is:
16239         // t.applySubtemplate(NAME, data, wrapping data..)
16240         // the outer template then get' applied with
16241         //     the store 'extra data'
16242         // and the body get's added to the
16243         //      roo-name="data" node?
16244         //      <span class='roo-tpl-{name}'></span> ?????
16245         
16246         
16247         
16248         this.clearSelections();
16249         this.el.update("");
16250         var html = [];
16251         var records = this.store.getRange();
16252         if(records.length < 1) {
16253             
16254             // is this valid??  = should it render a template??
16255             
16256             this.el.update(this.emptyText);
16257             return;
16258         }
16259         var el = this.el;
16260         if (this.dataName) {
16261             this.el.update(t.apply(this.store.meta)); //????
16262             el = this.el.child('.roo-tpl-' + this.dataName);
16263         }
16264         
16265         for(var i = 0, len = records.length; i < len; i++){
16266             var data = this.prepareData(records[i].data, i, records[i]);
16267             this.fireEvent("preparedata", this, data, i, records[i]);
16268             
16269             var d = Roo.apply({}, data);
16270             
16271             if(this.tickable){
16272                 Roo.apply(d, {'roo-id' : Roo.id()});
16273                 
16274                 var _this = this;
16275             
16276                 Roo.each(this.parent.item, function(item){
16277                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16278                         return;
16279                     }
16280                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16281                 });
16282             }
16283             
16284             html[html.length] = Roo.util.Format.trim(
16285                 this.dataName ?
16286                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16287                     t.apply(d)
16288             );
16289         }
16290         
16291         
16292         
16293         el.update(html.join(""));
16294         this.nodes = el.dom.childNodes;
16295         this.updateIndexes(0);
16296     },
16297     
16298
16299     /**
16300      * Function to override to reformat the data that is sent to
16301      * the template for each node.
16302      * DEPRICATED - use the preparedata event handler.
16303      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16304      * a JSON object for an UpdateManager bound view).
16305      */
16306     prepareData : function(data, index, record)
16307     {
16308         this.fireEvent("preparedata", this, data, index, record);
16309         return data;
16310     },
16311
16312     onUpdate : function(ds, record){
16313         // Roo.log('on update');   
16314         this.clearSelections();
16315         var index = this.store.indexOf(record);
16316         var n = this.nodes[index];
16317         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16318         n.parentNode.removeChild(n);
16319         this.updateIndexes(index, index);
16320     },
16321
16322     
16323     
16324 // --------- FIXME     
16325     onAdd : function(ds, records, index)
16326     {
16327         //Roo.log(['on Add', ds, records, index] );        
16328         this.clearSelections();
16329         if(this.nodes.length == 0){
16330             this.refresh();
16331             return;
16332         }
16333         var n = this.nodes[index];
16334         for(var i = 0, len = records.length; i < len; i++){
16335             var d = this.prepareData(records[i].data, i, records[i]);
16336             if(n){
16337                 this.tpl.insertBefore(n, d);
16338             }else{
16339                 
16340                 this.tpl.append(this.el, d);
16341             }
16342         }
16343         this.updateIndexes(index);
16344     },
16345
16346     onRemove : function(ds, record, index){
16347        // Roo.log('onRemove');
16348         this.clearSelections();
16349         var el = this.dataName  ?
16350             this.el.child('.roo-tpl-' + this.dataName) :
16351             this.el; 
16352         
16353         el.dom.removeChild(this.nodes[index]);
16354         this.updateIndexes(index);
16355     },
16356
16357     /**
16358      * Refresh an individual node.
16359      * @param {Number} index
16360      */
16361     refreshNode : function(index){
16362         this.onUpdate(this.store, this.store.getAt(index));
16363     },
16364
16365     updateIndexes : function(startIndex, endIndex){
16366         var ns = this.nodes;
16367         startIndex = startIndex || 0;
16368         endIndex = endIndex || ns.length - 1;
16369         for(var i = startIndex; i <= endIndex; i++){
16370             ns[i].nodeIndex = i;
16371         }
16372     },
16373
16374     /**
16375      * Changes the data store this view uses and refresh the view.
16376      * @param {Store} store
16377      */
16378     setStore : function(store, initial){
16379         if(!initial && this.store){
16380             this.store.un("datachanged", this.refresh);
16381             this.store.un("add", this.onAdd);
16382             this.store.un("remove", this.onRemove);
16383             this.store.un("update", this.onUpdate);
16384             this.store.un("clear", this.refresh);
16385             this.store.un("beforeload", this.onBeforeLoad);
16386             this.store.un("load", this.onLoad);
16387             this.store.un("loadexception", this.onLoad);
16388         }
16389         if(store){
16390           
16391             store.on("datachanged", this.refresh, this);
16392             store.on("add", this.onAdd, this);
16393             store.on("remove", this.onRemove, this);
16394             store.on("update", this.onUpdate, this);
16395             store.on("clear", this.refresh, this);
16396             store.on("beforeload", this.onBeforeLoad, this);
16397             store.on("load", this.onLoad, this);
16398             store.on("loadexception", this.onLoad, this);
16399         }
16400         
16401         if(store){
16402             this.refresh();
16403         }
16404     },
16405     /**
16406      * onbeforeLoad - masks the loading area.
16407      *
16408      */
16409     onBeforeLoad : function(store,opts)
16410     {
16411          //Roo.log('onBeforeLoad');   
16412         if (!opts.add) {
16413             this.el.update("");
16414         }
16415         this.el.mask(this.mask ? this.mask : "Loading" ); 
16416     },
16417     onLoad : function ()
16418     {
16419         this.el.unmask();
16420     },
16421     
16422
16423     /**
16424      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16425      * @param {HTMLElement} node
16426      * @return {HTMLElement} The template node
16427      */
16428     findItemFromChild : function(node){
16429         var el = this.dataName  ?
16430             this.el.child('.roo-tpl-' + this.dataName,true) :
16431             this.el.dom; 
16432         
16433         if(!node || node.parentNode == el){
16434                     return node;
16435             }
16436             var p = node.parentNode;
16437             while(p && p != el){
16438             if(p.parentNode == el){
16439                 return p;
16440             }
16441             p = p.parentNode;
16442         }
16443             return null;
16444     },
16445
16446     /** @ignore */
16447     onClick : function(e){
16448         var item = this.findItemFromChild(e.getTarget());
16449         if(item){
16450             var index = this.indexOf(item);
16451             if(this.onItemClick(item, index, e) !== false){
16452                 this.fireEvent("click", this, index, item, e);
16453             }
16454         }else{
16455             this.clearSelections();
16456         }
16457     },
16458
16459     /** @ignore */
16460     onContextMenu : function(e){
16461         var item = this.findItemFromChild(e.getTarget());
16462         if(item){
16463             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16464         }
16465     },
16466
16467     /** @ignore */
16468     onDblClick : function(e){
16469         var item = this.findItemFromChild(e.getTarget());
16470         if(item){
16471             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16472         }
16473     },
16474
16475     onItemClick : function(item, index, e)
16476     {
16477         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16478             return false;
16479         }
16480         if (this.toggleSelect) {
16481             var m = this.isSelected(item) ? 'unselect' : 'select';
16482             //Roo.log(m);
16483             var _t = this;
16484             _t[m](item, true, false);
16485             return true;
16486         }
16487         if(this.multiSelect || this.singleSelect){
16488             if(this.multiSelect && e.shiftKey && this.lastSelection){
16489                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16490             }else{
16491                 this.select(item, this.multiSelect && e.ctrlKey);
16492                 this.lastSelection = item;
16493             }
16494             
16495             if(!this.tickable){
16496                 e.preventDefault();
16497             }
16498             
16499         }
16500         return true;
16501     },
16502
16503     /**
16504      * Get the number of selected nodes.
16505      * @return {Number}
16506      */
16507     getSelectionCount : function(){
16508         return this.selections.length;
16509     },
16510
16511     /**
16512      * Get the currently selected nodes.
16513      * @return {Array} An array of HTMLElements
16514      */
16515     getSelectedNodes : function(){
16516         return this.selections;
16517     },
16518
16519     /**
16520      * Get the indexes of the selected nodes.
16521      * @return {Array}
16522      */
16523     getSelectedIndexes : function(){
16524         var indexes = [], s = this.selections;
16525         for(var i = 0, len = s.length; i < len; i++){
16526             indexes.push(s[i].nodeIndex);
16527         }
16528         return indexes;
16529     },
16530
16531     /**
16532      * Clear all selections
16533      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16534      */
16535     clearSelections : function(suppressEvent){
16536         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16537             this.cmp.elements = this.selections;
16538             this.cmp.removeClass(this.selectedClass);
16539             this.selections = [];
16540             if(!suppressEvent){
16541                 this.fireEvent("selectionchange", this, this.selections);
16542             }
16543         }
16544     },
16545
16546     /**
16547      * Returns true if the passed node is selected
16548      * @param {HTMLElement/Number} node The node or node index
16549      * @return {Boolean}
16550      */
16551     isSelected : function(node){
16552         var s = this.selections;
16553         if(s.length < 1){
16554             return false;
16555         }
16556         node = this.getNode(node);
16557         return s.indexOf(node) !== -1;
16558     },
16559
16560     /**
16561      * Selects nodes.
16562      * @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
16563      * @param {Boolean} keepExisting (optional) true to keep existing selections
16564      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16565      */
16566     select : function(nodeInfo, keepExisting, suppressEvent){
16567         if(nodeInfo instanceof Array){
16568             if(!keepExisting){
16569                 this.clearSelections(true);
16570             }
16571             for(var i = 0, len = nodeInfo.length; i < len; i++){
16572                 this.select(nodeInfo[i], true, true);
16573             }
16574             return;
16575         } 
16576         var node = this.getNode(nodeInfo);
16577         if(!node || this.isSelected(node)){
16578             return; // already selected.
16579         }
16580         if(!keepExisting){
16581             this.clearSelections(true);
16582         }
16583         
16584         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16585             Roo.fly(node).addClass(this.selectedClass);
16586             this.selections.push(node);
16587             if(!suppressEvent){
16588                 this.fireEvent("selectionchange", this, this.selections);
16589             }
16590         }
16591         
16592         
16593     },
16594       /**
16595      * Unselects nodes.
16596      * @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
16597      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16598      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16599      */
16600     unselect : function(nodeInfo, keepExisting, suppressEvent)
16601     {
16602         if(nodeInfo instanceof Array){
16603             Roo.each(this.selections, function(s) {
16604                 this.unselect(s, nodeInfo);
16605             }, this);
16606             return;
16607         }
16608         var node = this.getNode(nodeInfo);
16609         if(!node || !this.isSelected(node)){
16610             //Roo.log("not selected");
16611             return; // not selected.
16612         }
16613         // fireevent???
16614         var ns = [];
16615         Roo.each(this.selections, function(s) {
16616             if (s == node ) {
16617                 Roo.fly(node).removeClass(this.selectedClass);
16618
16619                 return;
16620             }
16621             ns.push(s);
16622         },this);
16623         
16624         this.selections= ns;
16625         this.fireEvent("selectionchange", this, this.selections);
16626     },
16627
16628     /**
16629      * Gets a template node.
16630      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16631      * @return {HTMLElement} The node or null if it wasn't found
16632      */
16633     getNode : function(nodeInfo){
16634         if(typeof nodeInfo == "string"){
16635             return document.getElementById(nodeInfo);
16636         }else if(typeof nodeInfo == "number"){
16637             return this.nodes[nodeInfo];
16638         }
16639         return nodeInfo;
16640     },
16641
16642     /**
16643      * Gets a range template nodes.
16644      * @param {Number} startIndex
16645      * @param {Number} endIndex
16646      * @return {Array} An array of nodes
16647      */
16648     getNodes : function(start, end){
16649         var ns = this.nodes;
16650         start = start || 0;
16651         end = typeof end == "undefined" ? ns.length - 1 : end;
16652         var nodes = [];
16653         if(start <= end){
16654             for(var i = start; i <= end; i++){
16655                 nodes.push(ns[i]);
16656             }
16657         } else{
16658             for(var i = start; i >= end; i--){
16659                 nodes.push(ns[i]);
16660             }
16661         }
16662         return nodes;
16663     },
16664
16665     /**
16666      * Finds the index of the passed node
16667      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16668      * @return {Number} The index of the node or -1
16669      */
16670     indexOf : function(node){
16671         node = this.getNode(node);
16672         if(typeof node.nodeIndex == "number"){
16673             return node.nodeIndex;
16674         }
16675         var ns = this.nodes;
16676         for(var i = 0, len = ns.length; i < len; i++){
16677             if(ns[i] == node){
16678                 return i;
16679             }
16680         }
16681         return -1;
16682     }
16683 });
16684 /*
16685  * - LGPL
16686  *
16687  * based on jquery fullcalendar
16688  * 
16689  */
16690
16691 Roo.bootstrap = Roo.bootstrap || {};
16692 /**
16693  * @class Roo.bootstrap.Calendar
16694  * @extends Roo.bootstrap.Component
16695  * Bootstrap Calendar class
16696  * @cfg {Boolean} loadMask (true|false) default false
16697  * @cfg {Object} header generate the user specific header of the calendar, default false
16698
16699  * @constructor
16700  * Create a new Container
16701  * @param {Object} config The config object
16702  */
16703
16704
16705
16706 Roo.bootstrap.Calendar = function(config){
16707     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16708      this.addEvents({
16709         /**
16710              * @event select
16711              * Fires when a date is selected
16712              * @param {DatePicker} this
16713              * @param {Date} date The selected date
16714              */
16715         'select': true,
16716         /**
16717              * @event monthchange
16718              * Fires when the displayed month changes 
16719              * @param {DatePicker} this
16720              * @param {Date} date The selected month
16721              */
16722         'monthchange': true,
16723         /**
16724              * @event evententer
16725              * Fires when mouse over an event
16726              * @param {Calendar} this
16727              * @param {event} Event
16728              */
16729         'evententer': true,
16730         /**
16731              * @event eventleave
16732              * Fires when the mouse leaves an
16733              * @param {Calendar} this
16734              * @param {event}
16735              */
16736         'eventleave': true,
16737         /**
16738              * @event eventclick
16739              * Fires when the mouse click an
16740              * @param {Calendar} this
16741              * @param {event}
16742              */
16743         'eventclick': true
16744         
16745     });
16746
16747 };
16748
16749 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16750     
16751      /**
16752      * @cfg {Number} startDay
16753      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16754      */
16755     startDay : 0,
16756     
16757     loadMask : false,
16758     
16759     header : false,
16760       
16761     getAutoCreate : function(){
16762         
16763         
16764         var fc_button = function(name, corner, style, content ) {
16765             return Roo.apply({},{
16766                 tag : 'span',
16767                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16768                          (corner.length ?
16769                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16770                             ''
16771                         ),
16772                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16773                 unselectable: 'on'
16774             });
16775         };
16776         
16777         var header = {};
16778         
16779         if(!this.header){
16780             header = {
16781                 tag : 'table',
16782                 cls : 'fc-header',
16783                 style : 'width:100%',
16784                 cn : [
16785                     {
16786                         tag: 'tr',
16787                         cn : [
16788                             {
16789                                 tag : 'td',
16790                                 cls : 'fc-header-left',
16791                                 cn : [
16792                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16793                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16794                                     { tag: 'span', cls: 'fc-header-space' },
16795                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16796
16797
16798                                 ]
16799                             },
16800
16801                             {
16802                                 tag : 'td',
16803                                 cls : 'fc-header-center',
16804                                 cn : [
16805                                     {
16806                                         tag: 'span',
16807                                         cls: 'fc-header-title',
16808                                         cn : {
16809                                             tag: 'H2',
16810                                             html : 'month / year'
16811                                         }
16812                                     }
16813
16814                                 ]
16815                             },
16816                             {
16817                                 tag : 'td',
16818                                 cls : 'fc-header-right',
16819                                 cn : [
16820                               /*      fc_button('month', 'left', '', 'month' ),
16821                                     fc_button('week', '', '', 'week' ),
16822                                     fc_button('day', 'right', '', 'day' )
16823                                 */    
16824
16825                                 ]
16826                             }
16827
16828                         ]
16829                     }
16830                 ]
16831             };
16832         }
16833         
16834         header = this.header;
16835         
16836        
16837         var cal_heads = function() {
16838             var ret = [];
16839             // fixme - handle this.
16840             
16841             for (var i =0; i < Date.dayNames.length; i++) {
16842                 var d = Date.dayNames[i];
16843                 ret.push({
16844                     tag: 'th',
16845                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16846                     html : d.substring(0,3)
16847                 });
16848                 
16849             }
16850             ret[0].cls += ' fc-first';
16851             ret[6].cls += ' fc-last';
16852             return ret;
16853         };
16854         var cal_cell = function(n) {
16855             return  {
16856                 tag: 'td',
16857                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16858                 cn : [
16859                     {
16860                         cn : [
16861                             {
16862                                 cls: 'fc-day-number',
16863                                 html: 'D'
16864                             },
16865                             {
16866                                 cls: 'fc-day-content',
16867                              
16868                                 cn : [
16869                                      {
16870                                         style: 'position: relative;' // height: 17px;
16871                                     }
16872                                 ]
16873                             }
16874                             
16875                             
16876                         ]
16877                     }
16878                 ]
16879                 
16880             }
16881         };
16882         var cal_rows = function() {
16883             
16884             var ret = [];
16885             for (var r = 0; r < 6; r++) {
16886                 var row= {
16887                     tag : 'tr',
16888                     cls : 'fc-week',
16889                     cn : []
16890                 };
16891                 
16892                 for (var i =0; i < Date.dayNames.length; i++) {
16893                     var d = Date.dayNames[i];
16894                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16895
16896                 }
16897                 row.cn[0].cls+=' fc-first';
16898                 row.cn[0].cn[0].style = 'min-height:90px';
16899                 row.cn[6].cls+=' fc-last';
16900                 ret.push(row);
16901                 
16902             }
16903             ret[0].cls += ' fc-first';
16904             ret[4].cls += ' fc-prev-last';
16905             ret[5].cls += ' fc-last';
16906             return ret;
16907             
16908         };
16909         
16910         var cal_table = {
16911             tag: 'table',
16912             cls: 'fc-border-separate',
16913             style : 'width:100%',
16914             cellspacing  : 0,
16915             cn : [
16916                 { 
16917                     tag: 'thead',
16918                     cn : [
16919                         { 
16920                             tag: 'tr',
16921                             cls : 'fc-first fc-last',
16922                             cn : cal_heads()
16923                         }
16924                     ]
16925                 },
16926                 { 
16927                     tag: 'tbody',
16928                     cn : cal_rows()
16929                 }
16930                   
16931             ]
16932         };
16933          
16934          var cfg = {
16935             cls : 'fc fc-ltr',
16936             cn : [
16937                 header,
16938                 {
16939                     cls : 'fc-content',
16940                     style : "position: relative;",
16941                     cn : [
16942                         {
16943                             cls : 'fc-view fc-view-month fc-grid',
16944                             style : 'position: relative',
16945                             unselectable : 'on',
16946                             cn : [
16947                                 {
16948                                     cls : 'fc-event-container',
16949                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16950                                 },
16951                                 cal_table
16952                             ]
16953                         }
16954                     ]
16955     
16956                 }
16957            ] 
16958             
16959         };
16960         
16961          
16962         
16963         return cfg;
16964     },
16965     
16966     
16967     initEvents : function()
16968     {
16969         if(!this.store){
16970             throw "can not find store for calendar";
16971         }
16972         
16973         var mark = {
16974             tag: "div",
16975             cls:"x-dlg-mask",
16976             style: "text-align:center",
16977             cn: [
16978                 {
16979                     tag: "div",
16980                     style: "background-color:white;width:50%;margin:250 auto",
16981                     cn: [
16982                         {
16983                             tag: "img",
16984                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16985                         },
16986                         {
16987                             tag: "span",
16988                             html: "Loading"
16989                         }
16990                         
16991                     ]
16992                 }
16993             ]
16994         };
16995         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16996         
16997         var size = this.el.select('.fc-content', true).first().getSize();
16998         this.maskEl.setSize(size.width, size.height);
16999         this.maskEl.enableDisplayMode("block");
17000         if(!this.loadMask){
17001             this.maskEl.hide();
17002         }
17003         
17004         this.store = Roo.factory(this.store, Roo.data);
17005         this.store.on('load', this.onLoad, this);
17006         this.store.on('beforeload', this.onBeforeLoad, this);
17007         
17008         this.resize();
17009         
17010         this.cells = this.el.select('.fc-day',true);
17011         //Roo.log(this.cells);
17012         this.textNodes = this.el.query('.fc-day-number');
17013         this.cells.addClassOnOver('fc-state-hover');
17014         
17015         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17016         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17017         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17018         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17019         
17020         this.on('monthchange', this.onMonthChange, this);
17021         
17022         this.update(new Date().clearTime());
17023     },
17024     
17025     resize : function() {
17026         var sz  = this.el.getSize();
17027         
17028         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17029         this.el.select('.fc-day-content div',true).setHeight(34);
17030     },
17031     
17032     
17033     // private
17034     showPrevMonth : function(e){
17035         this.update(this.activeDate.add("mo", -1));
17036     },
17037     showToday : function(e){
17038         this.update(new Date().clearTime());
17039     },
17040     // private
17041     showNextMonth : function(e){
17042         this.update(this.activeDate.add("mo", 1));
17043     },
17044
17045     // private
17046     showPrevYear : function(){
17047         this.update(this.activeDate.add("y", -1));
17048     },
17049
17050     // private
17051     showNextYear : function(){
17052         this.update(this.activeDate.add("y", 1));
17053     },
17054
17055     
17056    // private
17057     update : function(date)
17058     {
17059         var vd = this.activeDate;
17060         this.activeDate = date;
17061 //        if(vd && this.el){
17062 //            var t = date.getTime();
17063 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17064 //                Roo.log('using add remove');
17065 //                
17066 //                this.fireEvent('monthchange', this, date);
17067 //                
17068 //                this.cells.removeClass("fc-state-highlight");
17069 //                this.cells.each(function(c){
17070 //                   if(c.dateValue == t){
17071 //                       c.addClass("fc-state-highlight");
17072 //                       setTimeout(function(){
17073 //                            try{c.dom.firstChild.focus();}catch(e){}
17074 //                       }, 50);
17075 //                       return false;
17076 //                   }
17077 //                   return true;
17078 //                });
17079 //                return;
17080 //            }
17081 //        }
17082         
17083         var days = date.getDaysInMonth();
17084         
17085         var firstOfMonth = date.getFirstDateOfMonth();
17086         var startingPos = firstOfMonth.getDay()-this.startDay;
17087         
17088         if(startingPos < this.startDay){
17089             startingPos += 7;
17090         }
17091         
17092         var pm = date.add(Date.MONTH, -1);
17093         var prevStart = pm.getDaysInMonth()-startingPos;
17094 //        
17095         this.cells = this.el.select('.fc-day',true);
17096         this.textNodes = this.el.query('.fc-day-number');
17097         this.cells.addClassOnOver('fc-state-hover');
17098         
17099         var cells = this.cells.elements;
17100         var textEls = this.textNodes;
17101         
17102         Roo.each(cells, function(cell){
17103             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17104         });
17105         
17106         days += startingPos;
17107
17108         // convert everything to numbers so it's fast
17109         var day = 86400000;
17110         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17111         //Roo.log(d);
17112         //Roo.log(pm);
17113         //Roo.log(prevStart);
17114         
17115         var today = new Date().clearTime().getTime();
17116         var sel = date.clearTime().getTime();
17117         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17118         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17119         var ddMatch = this.disabledDatesRE;
17120         var ddText = this.disabledDatesText;
17121         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17122         var ddaysText = this.disabledDaysText;
17123         var format = this.format;
17124         
17125         var setCellClass = function(cal, cell){
17126             cell.row = 0;
17127             cell.events = [];
17128             cell.more = [];
17129             //Roo.log('set Cell Class');
17130             cell.title = "";
17131             var t = d.getTime();
17132             
17133             //Roo.log(d);
17134             
17135             cell.dateValue = t;
17136             if(t == today){
17137                 cell.className += " fc-today";
17138                 cell.className += " fc-state-highlight";
17139                 cell.title = cal.todayText;
17140             }
17141             if(t == sel){
17142                 // disable highlight in other month..
17143                 //cell.className += " fc-state-highlight";
17144                 
17145             }
17146             // disabling
17147             if(t < min) {
17148                 cell.className = " fc-state-disabled";
17149                 cell.title = cal.minText;
17150                 return;
17151             }
17152             if(t > max) {
17153                 cell.className = " fc-state-disabled";
17154                 cell.title = cal.maxText;
17155                 return;
17156             }
17157             if(ddays){
17158                 if(ddays.indexOf(d.getDay()) != -1){
17159                     cell.title = ddaysText;
17160                     cell.className = " fc-state-disabled";
17161                 }
17162             }
17163             if(ddMatch && format){
17164                 var fvalue = d.dateFormat(format);
17165                 if(ddMatch.test(fvalue)){
17166                     cell.title = ddText.replace("%0", fvalue);
17167                     cell.className = " fc-state-disabled";
17168                 }
17169             }
17170             
17171             if (!cell.initialClassName) {
17172                 cell.initialClassName = cell.dom.className;
17173             }
17174             
17175             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17176         };
17177
17178         var i = 0;
17179         
17180         for(; i < startingPos; i++) {
17181             textEls[i].innerHTML = (++prevStart);
17182             d.setDate(d.getDate()+1);
17183             
17184             cells[i].className = "fc-past fc-other-month";
17185             setCellClass(this, cells[i]);
17186         }
17187         
17188         var intDay = 0;
17189         
17190         for(; i < days; i++){
17191             intDay = i - startingPos + 1;
17192             textEls[i].innerHTML = (intDay);
17193             d.setDate(d.getDate()+1);
17194             
17195             cells[i].className = ''; // "x-date-active";
17196             setCellClass(this, cells[i]);
17197         }
17198         var extraDays = 0;
17199         
17200         for(; i < 42; i++) {
17201             textEls[i].innerHTML = (++extraDays);
17202             d.setDate(d.getDate()+1);
17203             
17204             cells[i].className = "fc-future fc-other-month";
17205             setCellClass(this, cells[i]);
17206         }
17207         
17208         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17209         
17210         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17211         
17212         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17213         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17214         
17215         if(totalRows != 6){
17216             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17217             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17218         }
17219         
17220         this.fireEvent('monthchange', this, date);
17221         
17222         
17223         /*
17224         if(!this.internalRender){
17225             var main = this.el.dom.firstChild;
17226             var w = main.offsetWidth;
17227             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17228             Roo.fly(main).setWidth(w);
17229             this.internalRender = true;
17230             // opera does not respect the auto grow header center column
17231             // then, after it gets a width opera refuses to recalculate
17232             // without a second pass
17233             if(Roo.isOpera && !this.secondPass){
17234                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17235                 this.secondPass = true;
17236                 this.update.defer(10, this, [date]);
17237             }
17238         }
17239         */
17240         
17241     },
17242     
17243     findCell : function(dt) {
17244         dt = dt.clearTime().getTime();
17245         var ret = false;
17246         this.cells.each(function(c){
17247             //Roo.log("check " +c.dateValue + '?=' + dt);
17248             if(c.dateValue == dt){
17249                 ret = c;
17250                 return false;
17251             }
17252             return true;
17253         });
17254         
17255         return ret;
17256     },
17257     
17258     findCells : function(ev) {
17259         var s = ev.start.clone().clearTime().getTime();
17260        // Roo.log(s);
17261         var e= ev.end.clone().clearTime().getTime();
17262        // Roo.log(e);
17263         var ret = [];
17264         this.cells.each(function(c){
17265              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17266             
17267             if(c.dateValue > e){
17268                 return ;
17269             }
17270             if(c.dateValue < s){
17271                 return ;
17272             }
17273             ret.push(c);
17274         });
17275         
17276         return ret;    
17277     },
17278     
17279 //    findBestRow: function(cells)
17280 //    {
17281 //        var ret = 0;
17282 //        
17283 //        for (var i =0 ; i < cells.length;i++) {
17284 //            ret  = Math.max(cells[i].rows || 0,ret);
17285 //        }
17286 //        return ret;
17287 //        
17288 //    },
17289     
17290     
17291     addItem : function(ev)
17292     {
17293         // look for vertical location slot in
17294         var cells = this.findCells(ev);
17295         
17296 //        ev.row = this.findBestRow(cells);
17297         
17298         // work out the location.
17299         
17300         var crow = false;
17301         var rows = [];
17302         for(var i =0; i < cells.length; i++) {
17303             
17304             cells[i].row = cells[0].row;
17305             
17306             if(i == 0){
17307                 cells[i].row = cells[i].row + 1;
17308             }
17309             
17310             if (!crow) {
17311                 crow = {
17312                     start : cells[i],
17313                     end :  cells[i]
17314                 };
17315                 continue;
17316             }
17317             if (crow.start.getY() == cells[i].getY()) {
17318                 // on same row.
17319                 crow.end = cells[i];
17320                 continue;
17321             }
17322             // different row.
17323             rows.push(crow);
17324             crow = {
17325                 start: cells[i],
17326                 end : cells[i]
17327             };
17328             
17329         }
17330         
17331         rows.push(crow);
17332         ev.els = [];
17333         ev.rows = rows;
17334         ev.cells = cells;
17335         
17336         cells[0].events.push(ev);
17337         
17338         this.calevents.push(ev);
17339     },
17340     
17341     clearEvents: function() {
17342         
17343         if(!this.calevents){
17344             return;
17345         }
17346         
17347         Roo.each(this.cells.elements, function(c){
17348             c.row = 0;
17349             c.events = [];
17350             c.more = [];
17351         });
17352         
17353         Roo.each(this.calevents, function(e) {
17354             Roo.each(e.els, function(el) {
17355                 el.un('mouseenter' ,this.onEventEnter, this);
17356                 el.un('mouseleave' ,this.onEventLeave, this);
17357                 el.remove();
17358             },this);
17359         },this);
17360         
17361         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17362             e.remove();
17363         });
17364         
17365     },
17366     
17367     renderEvents: function()
17368     {   
17369         var _this = this;
17370         
17371         this.cells.each(function(c) {
17372             
17373             if(c.row < 5){
17374                 return;
17375             }
17376             
17377             var ev = c.events;
17378             
17379             var r = 4;
17380             if(c.row != c.events.length){
17381                 r = 4 - (4 - (c.row - c.events.length));
17382             }
17383             
17384             c.events = ev.slice(0, r);
17385             c.more = ev.slice(r);
17386             
17387             if(c.more.length && c.more.length == 1){
17388                 c.events.push(c.more.pop());
17389             }
17390             
17391             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17392             
17393         });
17394             
17395         this.cells.each(function(c) {
17396             
17397             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17398             
17399             
17400             for (var e = 0; e < c.events.length; e++){
17401                 var ev = c.events[e];
17402                 var rows = ev.rows;
17403                 
17404                 for(var i = 0; i < rows.length; i++) {
17405                 
17406                     // how many rows should it span..
17407
17408                     var  cfg = {
17409                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17410                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17411
17412                         unselectable : "on",
17413                         cn : [
17414                             {
17415                                 cls: 'fc-event-inner',
17416                                 cn : [
17417     //                                {
17418     //                                  tag:'span',
17419     //                                  cls: 'fc-event-time',
17420     //                                  html : cells.length > 1 ? '' : ev.time
17421     //                                },
17422                                     {
17423                                       tag:'span',
17424                                       cls: 'fc-event-title',
17425                                       html : String.format('{0}', ev.title)
17426                                     }
17427
17428
17429                                 ]
17430                             },
17431                             {
17432                                 cls: 'ui-resizable-handle ui-resizable-e',
17433                                 html : '&nbsp;&nbsp;&nbsp'
17434                             }
17435
17436                         ]
17437                     };
17438
17439                     if (i == 0) {
17440                         cfg.cls += ' fc-event-start';
17441                     }
17442                     if ((i+1) == rows.length) {
17443                         cfg.cls += ' fc-event-end';
17444                     }
17445
17446                     var ctr = _this.el.select('.fc-event-container',true).first();
17447                     var cg = ctr.createChild(cfg);
17448
17449                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17450                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17451
17452                     var r = (c.more.length) ? 1 : 0;
17453                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17454                     cg.setWidth(ebox.right - sbox.x -2);
17455
17456                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17457                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17458                     cg.on('click', _this.onEventClick, _this, ev);
17459
17460                     ev.els.push(cg);
17461                     
17462                 }
17463                 
17464             }
17465             
17466             
17467             if(c.more.length){
17468                 var  cfg = {
17469                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17470                     style : 'position: absolute',
17471                     unselectable : "on",
17472                     cn : [
17473                         {
17474                             cls: 'fc-event-inner',
17475                             cn : [
17476                                 {
17477                                   tag:'span',
17478                                   cls: 'fc-event-title',
17479                                   html : 'More'
17480                                 }
17481
17482
17483                             ]
17484                         },
17485                         {
17486                             cls: 'ui-resizable-handle ui-resizable-e',
17487                             html : '&nbsp;&nbsp;&nbsp'
17488                         }
17489
17490                     ]
17491                 };
17492
17493                 var ctr = _this.el.select('.fc-event-container',true).first();
17494                 var cg = ctr.createChild(cfg);
17495
17496                 var sbox = c.select('.fc-day-content',true).first().getBox();
17497                 var ebox = c.select('.fc-day-content',true).first().getBox();
17498                 //Roo.log(cg);
17499                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17500                 cg.setWidth(ebox.right - sbox.x -2);
17501
17502                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17503                 
17504             }
17505             
17506         });
17507         
17508         
17509         
17510     },
17511     
17512     onEventEnter: function (e, el,event,d) {
17513         this.fireEvent('evententer', this, el, event);
17514     },
17515     
17516     onEventLeave: function (e, el,event,d) {
17517         this.fireEvent('eventleave', this, el, event);
17518     },
17519     
17520     onEventClick: function (e, el,event,d) {
17521         this.fireEvent('eventclick', this, el, event);
17522     },
17523     
17524     onMonthChange: function () {
17525         this.store.load();
17526     },
17527     
17528     onMoreEventClick: function(e, el, more)
17529     {
17530         var _this = this;
17531         
17532         this.calpopover.placement = 'right';
17533         this.calpopover.setTitle('More');
17534         
17535         this.calpopover.setContent('');
17536         
17537         var ctr = this.calpopover.el.select('.popover-content', true).first();
17538         
17539         Roo.each(more, function(m){
17540             var cfg = {
17541                 cls : 'fc-event-hori fc-event-draggable',
17542                 html : m.title
17543             };
17544             var cg = ctr.createChild(cfg);
17545             
17546             cg.on('click', _this.onEventClick, _this, m);
17547         });
17548         
17549         this.calpopover.show(el);
17550         
17551         
17552     },
17553     
17554     onLoad: function () 
17555     {   
17556         this.calevents = [];
17557         var cal = this;
17558         
17559         if(this.store.getCount() > 0){
17560             this.store.data.each(function(d){
17561                cal.addItem({
17562                     id : d.data.id,
17563                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17564                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17565                     time : d.data.start_time,
17566                     title : d.data.title,
17567                     description : d.data.description,
17568                     venue : d.data.venue
17569                 });
17570             });
17571         }
17572         
17573         this.renderEvents();
17574         
17575         if(this.calevents.length && this.loadMask){
17576             this.maskEl.hide();
17577         }
17578     },
17579     
17580     onBeforeLoad: function()
17581     {
17582         this.clearEvents();
17583         if(this.loadMask){
17584             this.maskEl.show();
17585         }
17586     }
17587 });
17588
17589  
17590  /*
17591  * - LGPL
17592  *
17593  * element
17594  * 
17595  */
17596
17597 /**
17598  * @class Roo.bootstrap.Popover
17599  * @extends Roo.bootstrap.Component
17600  * Bootstrap Popover class
17601  * @cfg {String} html contents of the popover   (or false to use children..)
17602  * @cfg {String} title of popover (or false to hide)
17603  * @cfg {String} placement how it is placed
17604  * @cfg {String} trigger click || hover (or false to trigger manually)
17605  * @cfg {String} over what (parent or false to trigger manually.)
17606  * @cfg {Number} delay - delay before showing
17607  
17608  * @constructor
17609  * Create a new Popover
17610  * @param {Object} config The config object
17611  */
17612
17613 Roo.bootstrap.Popover = function(config){
17614     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17615     
17616     this.addEvents({
17617         // raw events
17618          /**
17619          * @event show
17620          * After the popover show
17621          * 
17622          * @param {Roo.bootstrap.Popover} this
17623          */
17624         "show" : true,
17625         /**
17626          * @event hide
17627          * After the popover hide
17628          * 
17629          * @param {Roo.bootstrap.Popover} this
17630          */
17631         "hide" : true
17632     });
17633 };
17634
17635 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17636     
17637     title: 'Fill in a title',
17638     html: false,
17639     
17640     placement : 'right',
17641     trigger : 'hover', // hover
17642     
17643     delay : 0,
17644     
17645     over: 'parent',
17646     
17647     can_build_overlaid : false,
17648     
17649     getChildContainer : function()
17650     {
17651         return this.el.select('.popover-content',true).first();
17652     },
17653     
17654     getAutoCreate : function(){
17655          
17656         var cfg = {
17657            cls : 'popover roo-dynamic',
17658            style: 'display:block',
17659            cn : [
17660                 {
17661                     cls : 'arrow'
17662                 },
17663                 {
17664                     cls : 'popover-inner',
17665                     cn : [
17666                         {
17667                             tag: 'h3',
17668                             cls: 'popover-title popover-header',
17669                             html : this.title
17670                         },
17671                         {
17672                             cls : 'popover-content popover-body',
17673                             html : this.html
17674                         }
17675                     ]
17676                     
17677                 }
17678            ]
17679         };
17680         
17681         return cfg;
17682     },
17683     setTitle: function(str)
17684     {
17685         this.title = str;
17686         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17687     },
17688     setContent: function(str)
17689     {
17690         this.html = str;
17691         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17692     },
17693     // as it get's added to the bottom of the page.
17694     onRender : function(ct, position)
17695     {
17696         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17697         if(!this.el){
17698             var cfg = Roo.apply({},  this.getAutoCreate());
17699             cfg.id = Roo.id();
17700             
17701             if (this.cls) {
17702                 cfg.cls += ' ' + this.cls;
17703             }
17704             if (this.style) {
17705                 cfg.style = this.style;
17706             }
17707             //Roo.log("adding to ");
17708             this.el = Roo.get(document.body).createChild(cfg, position);
17709 //            Roo.log(this.el);
17710         }
17711         this.initEvents();
17712     },
17713     
17714     initEvents : function()
17715     {
17716         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17717         this.el.enableDisplayMode('block');
17718         this.el.hide();
17719         if (this.over === false) {
17720             return; 
17721         }
17722         if (this.triggers === false) {
17723             return;
17724         }
17725         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17726         var triggers = this.trigger ? this.trigger.split(' ') : [];
17727         Roo.each(triggers, function(trigger) {
17728         
17729             if (trigger == 'click') {
17730                 on_el.on('click', this.toggle, this);
17731             } else if (trigger != 'manual') {
17732                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17733                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17734       
17735                 on_el.on(eventIn  ,this.enter, this);
17736                 on_el.on(eventOut, this.leave, this);
17737             }
17738         }, this);
17739         
17740     },
17741     
17742     
17743     // private
17744     timeout : null,
17745     hoverState : null,
17746     
17747     toggle : function () {
17748         this.hoverState == 'in' ? this.leave() : this.enter();
17749     },
17750     
17751     enter : function () {
17752         
17753         clearTimeout(this.timeout);
17754     
17755         this.hoverState = 'in';
17756     
17757         if (!this.delay || !this.delay.show) {
17758             this.show();
17759             return;
17760         }
17761         var _t = this;
17762         this.timeout = setTimeout(function () {
17763             if (_t.hoverState == 'in') {
17764                 _t.show();
17765             }
17766         }, this.delay.show)
17767     },
17768     
17769     leave : function() {
17770         clearTimeout(this.timeout);
17771     
17772         this.hoverState = 'out';
17773     
17774         if (!this.delay || !this.delay.hide) {
17775             this.hide();
17776             return;
17777         }
17778         var _t = this;
17779         this.timeout = setTimeout(function () {
17780             if (_t.hoverState == 'out') {
17781                 _t.hide();
17782             }
17783         }, this.delay.hide)
17784     },
17785     
17786     show : function (on_el)
17787     {
17788         if (!on_el) {
17789             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17790         }
17791         
17792         // set content.
17793         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17794         if (this.html !== false) {
17795             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17796         }
17797         this.el.removeClass([
17798             'fade','top','bottom', 'left', 'right','in',
17799             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17800         ]);
17801         if (!this.title.length) {
17802             this.el.select('.popover-title',true).hide();
17803         }
17804         
17805         var placement = typeof this.placement == 'function' ?
17806             this.placement.call(this, this.el, on_el) :
17807             this.placement;
17808             
17809         var autoToken = /\s?auto?\s?/i;
17810         var autoPlace = autoToken.test(placement);
17811         if (autoPlace) {
17812             placement = placement.replace(autoToken, '') || 'top';
17813         }
17814         
17815         //this.el.detach()
17816         //this.el.setXY([0,0]);
17817         this.el.show();
17818         this.el.dom.style.display='block';
17819         this.el.addClass(placement);
17820         
17821         //this.el.appendTo(on_el);
17822         
17823         var p = this.getPosition();
17824         var box = this.el.getBox();
17825         
17826         if (autoPlace) {
17827             // fixme..
17828         }
17829         var align = Roo.bootstrap.Popover.alignment[placement];
17830         
17831 //        Roo.log(align);
17832         this.el.alignTo(on_el, align[0],align[1]);
17833         //var arrow = this.el.select('.arrow',true).first();
17834         //arrow.set(align[2], 
17835         
17836         this.el.addClass('in');
17837         
17838         
17839         if (this.el.hasClass('fade')) {
17840             // fade it?
17841         }
17842         
17843         this.hoverState = 'in';
17844         
17845         this.fireEvent('show', this);
17846         
17847     },
17848     hide : function()
17849     {
17850         this.el.setXY([0,0]);
17851         this.el.removeClass('in');
17852         this.el.hide();
17853         this.hoverState = null;
17854         
17855         this.fireEvent('hide', this);
17856     }
17857     
17858 });
17859
17860 Roo.bootstrap.Popover.alignment = {
17861     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17862     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17863     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17864     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17865 };
17866
17867  /*
17868  * - LGPL
17869  *
17870  * Progress
17871  * 
17872  */
17873
17874 /**
17875  * @class Roo.bootstrap.Progress
17876  * @extends Roo.bootstrap.Component
17877  * Bootstrap Progress class
17878  * @cfg {Boolean} striped striped of the progress bar
17879  * @cfg {Boolean} active animated of the progress bar
17880  * 
17881  * 
17882  * @constructor
17883  * Create a new Progress
17884  * @param {Object} config The config object
17885  */
17886
17887 Roo.bootstrap.Progress = function(config){
17888     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17889 };
17890
17891 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17892     
17893     striped : false,
17894     active: false,
17895     
17896     getAutoCreate : function(){
17897         var cfg = {
17898             tag: 'div',
17899             cls: 'progress'
17900         };
17901         
17902         
17903         if(this.striped){
17904             cfg.cls += ' progress-striped';
17905         }
17906       
17907         if(this.active){
17908             cfg.cls += ' active';
17909         }
17910         
17911         
17912         return cfg;
17913     }
17914    
17915 });
17916
17917  
17918
17919  /*
17920  * - LGPL
17921  *
17922  * ProgressBar
17923  * 
17924  */
17925
17926 /**
17927  * @class Roo.bootstrap.ProgressBar
17928  * @extends Roo.bootstrap.Component
17929  * Bootstrap ProgressBar class
17930  * @cfg {Number} aria_valuenow aria-value now
17931  * @cfg {Number} aria_valuemin aria-value min
17932  * @cfg {Number} aria_valuemax aria-value max
17933  * @cfg {String} label label for the progress bar
17934  * @cfg {String} panel (success | info | warning | danger )
17935  * @cfg {String} role role of the progress bar
17936  * @cfg {String} sr_only text
17937  * 
17938  * 
17939  * @constructor
17940  * Create a new ProgressBar
17941  * @param {Object} config The config object
17942  */
17943
17944 Roo.bootstrap.ProgressBar = function(config){
17945     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17946 };
17947
17948 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17949     
17950     aria_valuenow : 0,
17951     aria_valuemin : 0,
17952     aria_valuemax : 100,
17953     label : false,
17954     panel : false,
17955     role : false,
17956     sr_only: false,
17957     
17958     getAutoCreate : function()
17959     {
17960         
17961         var cfg = {
17962             tag: 'div',
17963             cls: 'progress-bar',
17964             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17965         };
17966         
17967         if(this.sr_only){
17968             cfg.cn = {
17969                 tag: 'span',
17970                 cls: 'sr-only',
17971                 html: this.sr_only
17972             }
17973         }
17974         
17975         if(this.role){
17976             cfg.role = this.role;
17977         }
17978         
17979         if(this.aria_valuenow){
17980             cfg['aria-valuenow'] = this.aria_valuenow;
17981         }
17982         
17983         if(this.aria_valuemin){
17984             cfg['aria-valuemin'] = this.aria_valuemin;
17985         }
17986         
17987         if(this.aria_valuemax){
17988             cfg['aria-valuemax'] = this.aria_valuemax;
17989         }
17990         
17991         if(this.label && !this.sr_only){
17992             cfg.html = this.label;
17993         }
17994         
17995         if(this.panel){
17996             cfg.cls += ' progress-bar-' + this.panel;
17997         }
17998         
17999         return cfg;
18000     },
18001     
18002     update : function(aria_valuenow)
18003     {
18004         this.aria_valuenow = aria_valuenow;
18005         
18006         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18007     }
18008    
18009 });
18010
18011  
18012
18013  /*
18014  * - LGPL
18015  *
18016  * column
18017  * 
18018  */
18019
18020 /**
18021  * @class Roo.bootstrap.TabGroup
18022  * @extends Roo.bootstrap.Column
18023  * Bootstrap Column class
18024  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18025  * @cfg {Boolean} carousel true to make the group behave like a carousel
18026  * @cfg {Boolean} bullets show bullets for the panels
18027  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18028  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18029  * @cfg {Boolean} showarrow (true|false) show arrow default true
18030  * 
18031  * @constructor
18032  * Create a new TabGroup
18033  * @param {Object} config The config object
18034  */
18035
18036 Roo.bootstrap.TabGroup = function(config){
18037     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18038     if (!this.navId) {
18039         this.navId = Roo.id();
18040     }
18041     this.tabs = [];
18042     Roo.bootstrap.TabGroup.register(this);
18043     
18044 };
18045
18046 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18047     
18048     carousel : false,
18049     transition : false,
18050     bullets : 0,
18051     timer : 0,
18052     autoslide : false,
18053     slideFn : false,
18054     slideOnTouch : false,
18055     showarrow : true,
18056     
18057     getAutoCreate : function()
18058     {
18059         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18060         
18061         cfg.cls += ' tab-content';
18062         
18063         if (this.carousel) {
18064             cfg.cls += ' carousel slide';
18065             
18066             cfg.cn = [{
18067                cls : 'carousel-inner',
18068                cn : []
18069             }];
18070         
18071             if(this.bullets  && !Roo.isTouch){
18072                 
18073                 var bullets = {
18074                     cls : 'carousel-bullets',
18075                     cn : []
18076                 };
18077                
18078                 if(this.bullets_cls){
18079                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18080                 }
18081                 
18082                 bullets.cn.push({
18083                     cls : 'clear'
18084                 });
18085                 
18086                 cfg.cn[0].cn.push(bullets);
18087             }
18088             
18089             if(this.showarrow){
18090                 cfg.cn[0].cn.push({
18091                     tag : 'div',
18092                     class : 'carousel-arrow',
18093                     cn : [
18094                         {
18095                             tag : 'div',
18096                             class : 'carousel-prev',
18097                             cn : [
18098                                 {
18099                                     tag : 'i',
18100                                     class : 'fa fa-chevron-left'
18101                                 }
18102                             ]
18103                         },
18104                         {
18105                             tag : 'div',
18106                             class : 'carousel-next',
18107                             cn : [
18108                                 {
18109                                     tag : 'i',
18110                                     class : 'fa fa-chevron-right'
18111                                 }
18112                             ]
18113                         }
18114                     ]
18115                 });
18116             }
18117             
18118         }
18119         
18120         return cfg;
18121     },
18122     
18123     initEvents:  function()
18124     {
18125 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18126 //            this.el.on("touchstart", this.onTouchStart, this);
18127 //        }
18128         
18129         if(this.autoslide){
18130             var _this = this;
18131             
18132             this.slideFn = window.setInterval(function() {
18133                 _this.showPanelNext();
18134             }, this.timer);
18135         }
18136         
18137         if(this.showarrow){
18138             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18139             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18140         }
18141         
18142         
18143     },
18144     
18145 //    onTouchStart : function(e, el, o)
18146 //    {
18147 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18148 //            return;
18149 //        }
18150 //        
18151 //        this.showPanelNext();
18152 //    },
18153     
18154     
18155     getChildContainer : function()
18156     {
18157         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18158     },
18159     
18160     /**
18161     * register a Navigation item
18162     * @param {Roo.bootstrap.NavItem} the navitem to add
18163     */
18164     register : function(item)
18165     {
18166         this.tabs.push( item);
18167         item.navId = this.navId; // not really needed..
18168         this.addBullet();
18169     
18170     },
18171     
18172     getActivePanel : function()
18173     {
18174         var r = false;
18175         Roo.each(this.tabs, function(t) {
18176             if (t.active) {
18177                 r = t;
18178                 return false;
18179             }
18180             return null;
18181         });
18182         return r;
18183         
18184     },
18185     getPanelByName : function(n)
18186     {
18187         var r = false;
18188         Roo.each(this.tabs, function(t) {
18189             if (t.tabId == n) {
18190                 r = t;
18191                 return false;
18192             }
18193             return null;
18194         });
18195         return r;
18196     },
18197     indexOfPanel : function(p)
18198     {
18199         var r = false;
18200         Roo.each(this.tabs, function(t,i) {
18201             if (t.tabId == p.tabId) {
18202                 r = i;
18203                 return false;
18204             }
18205             return null;
18206         });
18207         return r;
18208     },
18209     /**
18210      * show a specific panel
18211      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18212      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18213      */
18214     showPanel : function (pan)
18215     {
18216         if(this.transition || typeof(pan) == 'undefined'){
18217             Roo.log("waiting for the transitionend");
18218             return;
18219         }
18220         
18221         if (typeof(pan) == 'number') {
18222             pan = this.tabs[pan];
18223         }
18224         
18225         if (typeof(pan) == 'string') {
18226             pan = this.getPanelByName(pan);
18227         }
18228         
18229         var cur = this.getActivePanel();
18230         
18231         if(!pan || !cur){
18232             Roo.log('pan or acitve pan is undefined');
18233             return false;
18234         }
18235         
18236         if (pan.tabId == this.getActivePanel().tabId) {
18237             return true;
18238         }
18239         
18240         if (false === cur.fireEvent('beforedeactivate')) {
18241             return false;
18242         }
18243         
18244         if(this.bullets > 0 && !Roo.isTouch){
18245             this.setActiveBullet(this.indexOfPanel(pan));
18246         }
18247         
18248         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18249             
18250             this.transition = true;
18251             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18252             var lr = dir == 'next' ? 'left' : 'right';
18253             pan.el.addClass(dir); // or prev
18254             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18255             cur.el.addClass(lr); // or right
18256             pan.el.addClass(lr);
18257             
18258             var _this = this;
18259             cur.el.on('transitionend', function() {
18260                 Roo.log("trans end?");
18261                 
18262                 pan.el.removeClass([lr,dir]);
18263                 pan.setActive(true);
18264                 
18265                 cur.el.removeClass([lr]);
18266                 cur.setActive(false);
18267                 
18268                 _this.transition = false;
18269                 
18270             }, this, { single:  true } );
18271             
18272             return true;
18273         }
18274         
18275         cur.setActive(false);
18276         pan.setActive(true);
18277         
18278         return true;
18279         
18280     },
18281     showPanelNext : function()
18282     {
18283         var i = this.indexOfPanel(this.getActivePanel());
18284         
18285         if (i >= this.tabs.length - 1 && !this.autoslide) {
18286             return;
18287         }
18288         
18289         if (i >= this.tabs.length - 1 && this.autoslide) {
18290             i = -1;
18291         }
18292         
18293         this.showPanel(this.tabs[i+1]);
18294     },
18295     
18296     showPanelPrev : function()
18297     {
18298         var i = this.indexOfPanel(this.getActivePanel());
18299         
18300         if (i  < 1 && !this.autoslide) {
18301             return;
18302         }
18303         
18304         if (i < 1 && this.autoslide) {
18305             i = this.tabs.length;
18306         }
18307         
18308         this.showPanel(this.tabs[i-1]);
18309     },
18310     
18311     
18312     addBullet: function()
18313     {
18314         if(!this.bullets || Roo.isTouch){
18315             return;
18316         }
18317         var ctr = this.el.select('.carousel-bullets',true).first();
18318         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18319         var bullet = ctr.createChild({
18320             cls : 'bullet bullet-' + i
18321         },ctr.dom.lastChild);
18322         
18323         
18324         var _this = this;
18325         
18326         bullet.on('click', (function(e, el, o, ii, t){
18327
18328             e.preventDefault();
18329
18330             this.showPanel(ii);
18331
18332             if(this.autoslide && this.slideFn){
18333                 clearInterval(this.slideFn);
18334                 this.slideFn = window.setInterval(function() {
18335                     _this.showPanelNext();
18336                 }, this.timer);
18337             }
18338
18339         }).createDelegate(this, [i, bullet], true));
18340                 
18341         
18342     },
18343      
18344     setActiveBullet : function(i)
18345     {
18346         if(Roo.isTouch){
18347             return;
18348         }
18349         
18350         Roo.each(this.el.select('.bullet', true).elements, function(el){
18351             el.removeClass('selected');
18352         });
18353
18354         var bullet = this.el.select('.bullet-' + i, true).first();
18355         
18356         if(!bullet){
18357             return;
18358         }
18359         
18360         bullet.addClass('selected');
18361     }
18362     
18363     
18364   
18365 });
18366
18367  
18368
18369  
18370  
18371 Roo.apply(Roo.bootstrap.TabGroup, {
18372     
18373     groups: {},
18374      /**
18375     * register a Navigation Group
18376     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18377     */
18378     register : function(navgrp)
18379     {
18380         this.groups[navgrp.navId] = navgrp;
18381         
18382     },
18383     /**
18384     * fetch a Navigation Group based on the navigation ID
18385     * if one does not exist , it will get created.
18386     * @param {string} the navgroup to add
18387     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18388     */
18389     get: function(navId) {
18390         if (typeof(this.groups[navId]) == 'undefined') {
18391             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18392         }
18393         return this.groups[navId] ;
18394     }
18395     
18396     
18397     
18398 });
18399
18400  /*
18401  * - LGPL
18402  *
18403  * TabPanel
18404  * 
18405  */
18406
18407 /**
18408  * @class Roo.bootstrap.TabPanel
18409  * @extends Roo.bootstrap.Component
18410  * Bootstrap TabPanel class
18411  * @cfg {Boolean} active panel active
18412  * @cfg {String} html panel content
18413  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18414  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18415  * @cfg {String} href click to link..
18416  * 
18417  * 
18418  * @constructor
18419  * Create a new TabPanel
18420  * @param {Object} config The config object
18421  */
18422
18423 Roo.bootstrap.TabPanel = function(config){
18424     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18425     this.addEvents({
18426         /**
18427              * @event changed
18428              * Fires when the active status changes
18429              * @param {Roo.bootstrap.TabPanel} this
18430              * @param {Boolean} state the new state
18431             
18432          */
18433         'changed': true,
18434         /**
18435              * @event beforedeactivate
18436              * Fires before a tab is de-activated - can be used to do validation on a form.
18437              * @param {Roo.bootstrap.TabPanel} this
18438              * @return {Boolean} false if there is an error
18439             
18440          */
18441         'beforedeactivate': true
18442      });
18443     
18444     this.tabId = this.tabId || Roo.id();
18445   
18446 };
18447
18448 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18449     
18450     active: false,
18451     html: false,
18452     tabId: false,
18453     navId : false,
18454     href : '',
18455     
18456     getAutoCreate : function(){
18457         var cfg = {
18458             tag: 'div',
18459             // item is needed for carousel - not sure if it has any effect otherwise
18460             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18461             html: this.html || ''
18462         };
18463         
18464         if(this.active){
18465             cfg.cls += ' active';
18466         }
18467         
18468         if(this.tabId){
18469             cfg.tabId = this.tabId;
18470         }
18471         
18472         
18473         return cfg;
18474     },
18475     
18476     initEvents:  function()
18477     {
18478         var p = this.parent();
18479         
18480         this.navId = this.navId || p.navId;
18481         
18482         if (typeof(this.navId) != 'undefined') {
18483             // not really needed.. but just in case.. parent should be a NavGroup.
18484             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18485             
18486             tg.register(this);
18487             
18488             var i = tg.tabs.length - 1;
18489             
18490             if(this.active && tg.bullets > 0 && i < tg.bullets){
18491                 tg.setActiveBullet(i);
18492             }
18493         }
18494         
18495         this.el.on('click', this.onClick, this);
18496         
18497         if(Roo.isTouch){
18498             this.el.on("touchstart", this.onTouchStart, this);
18499             this.el.on("touchmove", this.onTouchMove, this);
18500             this.el.on("touchend", this.onTouchEnd, this);
18501         }
18502         
18503     },
18504     
18505     onRender : function(ct, position)
18506     {
18507         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18508     },
18509     
18510     setActive : function(state)
18511     {
18512         Roo.log("panel - set active " + this.tabId + "=" + state);
18513         
18514         this.active = state;
18515         if (!state) {
18516             this.el.removeClass('active');
18517             
18518         } else  if (!this.el.hasClass('active')) {
18519             this.el.addClass('active');
18520         }
18521         
18522         this.fireEvent('changed', this, state);
18523     },
18524     
18525     onClick : function(e)
18526     {
18527         e.preventDefault();
18528         
18529         if(!this.href.length){
18530             return;
18531         }
18532         
18533         window.location.href = this.href;
18534     },
18535     
18536     startX : 0,
18537     startY : 0,
18538     endX : 0,
18539     endY : 0,
18540     swiping : false,
18541     
18542     onTouchStart : function(e)
18543     {
18544         this.swiping = false;
18545         
18546         this.startX = e.browserEvent.touches[0].clientX;
18547         this.startY = e.browserEvent.touches[0].clientY;
18548     },
18549     
18550     onTouchMove : function(e)
18551     {
18552         this.swiping = true;
18553         
18554         this.endX = e.browserEvent.touches[0].clientX;
18555         this.endY = e.browserEvent.touches[0].clientY;
18556     },
18557     
18558     onTouchEnd : function(e)
18559     {
18560         if(!this.swiping){
18561             this.onClick(e);
18562             return;
18563         }
18564         
18565         var tabGroup = this.parent();
18566         
18567         if(this.endX > this.startX){ // swiping right
18568             tabGroup.showPanelPrev();
18569             return;
18570         }
18571         
18572         if(this.startX > this.endX){ // swiping left
18573             tabGroup.showPanelNext();
18574             return;
18575         }
18576     }
18577     
18578     
18579 });
18580  
18581
18582  
18583
18584  /*
18585  * - LGPL
18586  *
18587  * DateField
18588  * 
18589  */
18590
18591 /**
18592  * @class Roo.bootstrap.DateField
18593  * @extends Roo.bootstrap.Input
18594  * Bootstrap DateField class
18595  * @cfg {Number} weekStart default 0
18596  * @cfg {String} viewMode default empty, (months|years)
18597  * @cfg {String} minViewMode default empty, (months|years)
18598  * @cfg {Number} startDate default -Infinity
18599  * @cfg {Number} endDate default Infinity
18600  * @cfg {Boolean} todayHighlight default false
18601  * @cfg {Boolean} todayBtn default false
18602  * @cfg {Boolean} calendarWeeks default false
18603  * @cfg {Object} daysOfWeekDisabled default empty
18604  * @cfg {Boolean} singleMode default false (true | false)
18605  * 
18606  * @cfg {Boolean} keyboardNavigation default true
18607  * @cfg {String} language default en
18608  * 
18609  * @constructor
18610  * Create a new DateField
18611  * @param {Object} config The config object
18612  */
18613
18614 Roo.bootstrap.DateField = function(config){
18615     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18616      this.addEvents({
18617             /**
18618              * @event show
18619              * Fires when this field show.
18620              * @param {Roo.bootstrap.DateField} this
18621              * @param {Mixed} date The date value
18622              */
18623             show : true,
18624             /**
18625              * @event show
18626              * Fires when this field hide.
18627              * @param {Roo.bootstrap.DateField} this
18628              * @param {Mixed} date The date value
18629              */
18630             hide : true,
18631             /**
18632              * @event select
18633              * Fires when select a date.
18634              * @param {Roo.bootstrap.DateField} this
18635              * @param {Mixed} date The date value
18636              */
18637             select : true,
18638             /**
18639              * @event beforeselect
18640              * Fires when before select a date.
18641              * @param {Roo.bootstrap.DateField} this
18642              * @param {Mixed} date The date value
18643              */
18644             beforeselect : true
18645         });
18646 };
18647
18648 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18649     
18650     /**
18651      * @cfg {String} format
18652      * The default date format string which can be overriden for localization support.  The format must be
18653      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18654      */
18655     format : "m/d/y",
18656     /**
18657      * @cfg {String} altFormats
18658      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18659      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18660      */
18661     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18662     
18663     weekStart : 0,
18664     
18665     viewMode : '',
18666     
18667     minViewMode : '',
18668     
18669     todayHighlight : false,
18670     
18671     todayBtn: false,
18672     
18673     language: 'en',
18674     
18675     keyboardNavigation: true,
18676     
18677     calendarWeeks: false,
18678     
18679     startDate: -Infinity,
18680     
18681     endDate: Infinity,
18682     
18683     daysOfWeekDisabled: [],
18684     
18685     _events: [],
18686     
18687     singleMode : false,
18688     
18689     UTCDate: function()
18690     {
18691         return new Date(Date.UTC.apply(Date, arguments));
18692     },
18693     
18694     UTCToday: function()
18695     {
18696         var today = new Date();
18697         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18698     },
18699     
18700     getDate: function() {
18701             var d = this.getUTCDate();
18702             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18703     },
18704     
18705     getUTCDate: function() {
18706             return this.date;
18707     },
18708     
18709     setDate: function(d) {
18710             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18711     },
18712     
18713     setUTCDate: function(d) {
18714             this.date = d;
18715             this.setValue(this.formatDate(this.date));
18716     },
18717         
18718     onRender: function(ct, position)
18719     {
18720         
18721         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18722         
18723         this.language = this.language || 'en';
18724         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18725         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18726         
18727         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18728         this.format = this.format || 'm/d/y';
18729         this.isInline = false;
18730         this.isInput = true;
18731         this.component = this.el.select('.add-on', true).first() || false;
18732         this.component = (this.component && this.component.length === 0) ? false : this.component;
18733         this.hasInput = this.component && this.inputEl().length;
18734         
18735         if (typeof(this.minViewMode === 'string')) {
18736             switch (this.minViewMode) {
18737                 case 'months':
18738                     this.minViewMode = 1;
18739                     break;
18740                 case 'years':
18741                     this.minViewMode = 2;
18742                     break;
18743                 default:
18744                     this.minViewMode = 0;
18745                     break;
18746             }
18747         }
18748         
18749         if (typeof(this.viewMode === 'string')) {
18750             switch (this.viewMode) {
18751                 case 'months':
18752                     this.viewMode = 1;
18753                     break;
18754                 case 'years':
18755                     this.viewMode = 2;
18756                     break;
18757                 default:
18758                     this.viewMode = 0;
18759                     break;
18760             }
18761         }
18762                 
18763         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18764         
18765 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18766         
18767         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18768         
18769         this.picker().on('mousedown', this.onMousedown, this);
18770         this.picker().on('click', this.onClick, this);
18771         
18772         this.picker().addClass('datepicker-dropdown');
18773         
18774         this.startViewMode = this.viewMode;
18775         
18776         if(this.singleMode){
18777             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18778                 v.setVisibilityMode(Roo.Element.DISPLAY);
18779                 v.hide();
18780             });
18781             
18782             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18783                 v.setStyle('width', '189px');
18784             });
18785         }
18786         
18787         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18788             if(!this.calendarWeeks){
18789                 v.remove();
18790                 return;
18791             }
18792             
18793             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18794             v.attr('colspan', function(i, val){
18795                 return parseInt(val) + 1;
18796             });
18797         });
18798                         
18799         
18800         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18801         
18802         this.setStartDate(this.startDate);
18803         this.setEndDate(this.endDate);
18804         
18805         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18806         
18807         this.fillDow();
18808         this.fillMonths();
18809         this.update();
18810         this.showMode();
18811         
18812         if(this.isInline) {
18813             this.showPopup();
18814         }
18815     },
18816     
18817     picker : function()
18818     {
18819         return this.pickerEl;
18820 //        return this.el.select('.datepicker', true).first();
18821     },
18822     
18823     fillDow: function()
18824     {
18825         var dowCnt = this.weekStart;
18826         
18827         var dow = {
18828             tag: 'tr',
18829             cn: [
18830                 
18831             ]
18832         };
18833         
18834         if(this.calendarWeeks){
18835             dow.cn.push({
18836                 tag: 'th',
18837                 cls: 'cw',
18838                 html: '&nbsp;'
18839             })
18840         }
18841         
18842         while (dowCnt < this.weekStart + 7) {
18843             dow.cn.push({
18844                 tag: 'th',
18845                 cls: 'dow',
18846                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18847             });
18848         }
18849         
18850         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18851     },
18852     
18853     fillMonths: function()
18854     {    
18855         var i = 0;
18856         var months = this.picker().select('>.datepicker-months td', true).first();
18857         
18858         months.dom.innerHTML = '';
18859         
18860         while (i < 12) {
18861             var month = {
18862                 tag: 'span',
18863                 cls: 'month',
18864                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18865             };
18866             
18867             months.createChild(month);
18868         }
18869         
18870     },
18871     
18872     update: function()
18873     {
18874         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;
18875         
18876         if (this.date < this.startDate) {
18877             this.viewDate = new Date(this.startDate);
18878         } else if (this.date > this.endDate) {
18879             this.viewDate = new Date(this.endDate);
18880         } else {
18881             this.viewDate = new Date(this.date);
18882         }
18883         
18884         this.fill();
18885     },
18886     
18887     fill: function() 
18888     {
18889         var d = new Date(this.viewDate),
18890                 year = d.getUTCFullYear(),
18891                 month = d.getUTCMonth(),
18892                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18893                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18894                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18895                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18896                 currentDate = this.date && this.date.valueOf(),
18897                 today = this.UTCToday();
18898         
18899         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18900         
18901 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18902         
18903 //        this.picker.select('>tfoot th.today').
18904 //                                              .text(dates[this.language].today)
18905 //                                              .toggle(this.todayBtn !== false);
18906     
18907         this.updateNavArrows();
18908         this.fillMonths();
18909                                                 
18910         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18911         
18912         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18913          
18914         prevMonth.setUTCDate(day);
18915         
18916         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18917         
18918         var nextMonth = new Date(prevMonth);
18919         
18920         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18921         
18922         nextMonth = nextMonth.valueOf();
18923         
18924         var fillMonths = false;
18925         
18926         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18927         
18928         while(prevMonth.valueOf() <= nextMonth) {
18929             var clsName = '';
18930             
18931             if (prevMonth.getUTCDay() === this.weekStart) {
18932                 if(fillMonths){
18933                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18934                 }
18935                     
18936                 fillMonths = {
18937                     tag: 'tr',
18938                     cn: []
18939                 };
18940                 
18941                 if(this.calendarWeeks){
18942                     // ISO 8601: First week contains first thursday.
18943                     // ISO also states week starts on Monday, but we can be more abstract here.
18944                     var
18945                     // Start of current week: based on weekstart/current date
18946                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18947                     // Thursday of this week
18948                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18949                     // First Thursday of year, year from thursday
18950                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18951                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18952                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18953                     
18954                     fillMonths.cn.push({
18955                         tag: 'td',
18956                         cls: 'cw',
18957                         html: calWeek
18958                     });
18959                 }
18960             }
18961             
18962             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18963                 clsName += ' old';
18964             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18965                 clsName += ' new';
18966             }
18967             if (this.todayHighlight &&
18968                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18969                 prevMonth.getUTCMonth() == today.getMonth() &&
18970                 prevMonth.getUTCDate() == today.getDate()) {
18971                 clsName += ' today';
18972             }
18973             
18974             if (currentDate && prevMonth.valueOf() === currentDate) {
18975                 clsName += ' active';
18976             }
18977             
18978             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18979                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18980                     clsName += ' disabled';
18981             }
18982             
18983             fillMonths.cn.push({
18984                 tag: 'td',
18985                 cls: 'day ' + clsName,
18986                 html: prevMonth.getDate()
18987             });
18988             
18989             prevMonth.setDate(prevMonth.getDate()+1);
18990         }
18991           
18992         var currentYear = this.date && this.date.getUTCFullYear();
18993         var currentMonth = this.date && this.date.getUTCMonth();
18994         
18995         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18996         
18997         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18998             v.removeClass('active');
18999             
19000             if(currentYear === year && k === currentMonth){
19001                 v.addClass('active');
19002             }
19003             
19004             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19005                 v.addClass('disabled');
19006             }
19007             
19008         });
19009         
19010         
19011         year = parseInt(year/10, 10) * 10;
19012         
19013         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19014         
19015         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19016         
19017         year -= 1;
19018         for (var i = -1; i < 11; i++) {
19019             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19020                 tag: 'span',
19021                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19022                 html: year
19023             });
19024             
19025             year += 1;
19026         }
19027     },
19028     
19029     showMode: function(dir) 
19030     {
19031         if (dir) {
19032             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19033         }
19034         
19035         Roo.each(this.picker().select('>div',true).elements, function(v){
19036             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19037             v.hide();
19038         });
19039         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19040     },
19041     
19042     place: function()
19043     {
19044         if(this.isInline) {
19045             return;
19046         }
19047         
19048         this.picker().removeClass(['bottom', 'top']);
19049         
19050         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19051             /*
19052              * place to the top of element!
19053              *
19054              */
19055             
19056             this.picker().addClass('top');
19057             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19058             
19059             return;
19060         }
19061         
19062         this.picker().addClass('bottom');
19063         
19064         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19065     },
19066     
19067     parseDate : function(value)
19068     {
19069         if(!value || value instanceof Date){
19070             return value;
19071         }
19072         var v = Date.parseDate(value, this.format);
19073         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19074             v = Date.parseDate(value, 'Y-m-d');
19075         }
19076         if(!v && this.altFormats){
19077             if(!this.altFormatsArray){
19078                 this.altFormatsArray = this.altFormats.split("|");
19079             }
19080             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19081                 v = Date.parseDate(value, this.altFormatsArray[i]);
19082             }
19083         }
19084         return v;
19085     },
19086     
19087     formatDate : function(date, fmt)
19088     {   
19089         return (!date || !(date instanceof Date)) ?
19090         date : date.dateFormat(fmt || this.format);
19091     },
19092     
19093     onFocus : function()
19094     {
19095         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19096         this.showPopup();
19097     },
19098     
19099     onBlur : function()
19100     {
19101         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19102         
19103         var d = this.inputEl().getValue();
19104         
19105         this.setValue(d);
19106                 
19107         this.hidePopup();
19108     },
19109     
19110     showPopup : function()
19111     {
19112         this.picker().show();
19113         this.update();
19114         this.place();
19115         
19116         this.fireEvent('showpopup', this, this.date);
19117     },
19118     
19119     hidePopup : function()
19120     {
19121         if(this.isInline) {
19122             return;
19123         }
19124         this.picker().hide();
19125         this.viewMode = this.startViewMode;
19126         this.showMode();
19127         
19128         this.fireEvent('hidepopup', this, this.date);
19129         
19130     },
19131     
19132     onMousedown: function(e)
19133     {
19134         e.stopPropagation();
19135         e.preventDefault();
19136     },
19137     
19138     keyup: function(e)
19139     {
19140         Roo.bootstrap.DateField.superclass.keyup.call(this);
19141         this.update();
19142     },
19143
19144     setValue: function(v)
19145     {
19146         if(this.fireEvent('beforeselect', this, v) !== false){
19147             var d = new Date(this.parseDate(v) ).clearTime();
19148         
19149             if(isNaN(d.getTime())){
19150                 this.date = this.viewDate = '';
19151                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19152                 return;
19153             }
19154
19155             v = this.formatDate(d);
19156
19157             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19158
19159             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19160
19161             this.update();
19162
19163             this.fireEvent('select', this, this.date);
19164         }
19165     },
19166     
19167     getValue: function()
19168     {
19169         return this.formatDate(this.date);
19170     },
19171     
19172     fireKey: function(e)
19173     {
19174         if (!this.picker().isVisible()){
19175             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19176                 this.showPopup();
19177             }
19178             return;
19179         }
19180         
19181         var dateChanged = false,
19182         dir, day, month,
19183         newDate, newViewDate;
19184         
19185         switch(e.keyCode){
19186             case 27: // escape
19187                 this.hidePopup();
19188                 e.preventDefault();
19189                 break;
19190             case 37: // left
19191             case 39: // right
19192                 if (!this.keyboardNavigation) {
19193                     break;
19194                 }
19195                 dir = e.keyCode == 37 ? -1 : 1;
19196                 
19197                 if (e.ctrlKey){
19198                     newDate = this.moveYear(this.date, dir);
19199                     newViewDate = this.moveYear(this.viewDate, dir);
19200                 } else if (e.shiftKey){
19201                     newDate = this.moveMonth(this.date, dir);
19202                     newViewDate = this.moveMonth(this.viewDate, dir);
19203                 } else {
19204                     newDate = new Date(this.date);
19205                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19206                     newViewDate = new Date(this.viewDate);
19207                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19208                 }
19209                 if (this.dateWithinRange(newDate)){
19210                     this.date = newDate;
19211                     this.viewDate = newViewDate;
19212                     this.setValue(this.formatDate(this.date));
19213 //                    this.update();
19214                     e.preventDefault();
19215                     dateChanged = true;
19216                 }
19217                 break;
19218             case 38: // up
19219             case 40: // down
19220                 if (!this.keyboardNavigation) {
19221                     break;
19222                 }
19223                 dir = e.keyCode == 38 ? -1 : 1;
19224                 if (e.ctrlKey){
19225                     newDate = this.moveYear(this.date, dir);
19226                     newViewDate = this.moveYear(this.viewDate, dir);
19227                 } else if (e.shiftKey){
19228                     newDate = this.moveMonth(this.date, dir);
19229                     newViewDate = this.moveMonth(this.viewDate, dir);
19230                 } else {
19231                     newDate = new Date(this.date);
19232                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19233                     newViewDate = new Date(this.viewDate);
19234                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19235                 }
19236                 if (this.dateWithinRange(newDate)){
19237                     this.date = newDate;
19238                     this.viewDate = newViewDate;
19239                     this.setValue(this.formatDate(this.date));
19240 //                    this.update();
19241                     e.preventDefault();
19242                     dateChanged = true;
19243                 }
19244                 break;
19245             case 13: // enter
19246                 this.setValue(this.formatDate(this.date));
19247                 this.hidePopup();
19248                 e.preventDefault();
19249                 break;
19250             case 9: // tab
19251                 this.setValue(this.formatDate(this.date));
19252                 this.hidePopup();
19253                 break;
19254             case 16: // shift
19255             case 17: // ctrl
19256             case 18: // alt
19257                 break;
19258             default :
19259                 this.hidePopup();
19260                 
19261         }
19262     },
19263     
19264     
19265     onClick: function(e) 
19266     {
19267         e.stopPropagation();
19268         e.preventDefault();
19269         
19270         var target = e.getTarget();
19271         
19272         if(target.nodeName.toLowerCase() === 'i'){
19273             target = Roo.get(target).dom.parentNode;
19274         }
19275         
19276         var nodeName = target.nodeName;
19277         var className = target.className;
19278         var html = target.innerHTML;
19279         //Roo.log(nodeName);
19280         
19281         switch(nodeName.toLowerCase()) {
19282             case 'th':
19283                 switch(className) {
19284                     case 'switch':
19285                         this.showMode(1);
19286                         break;
19287                     case 'prev':
19288                     case 'next':
19289                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19290                         switch(this.viewMode){
19291                                 case 0:
19292                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19293                                         break;
19294                                 case 1:
19295                                 case 2:
19296                                         this.viewDate = this.moveYear(this.viewDate, dir);
19297                                         break;
19298                         }
19299                         this.fill();
19300                         break;
19301                     case 'today':
19302                         var date = new Date();
19303                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19304 //                        this.fill()
19305                         this.setValue(this.formatDate(this.date));
19306                         
19307                         this.hidePopup();
19308                         break;
19309                 }
19310                 break;
19311             case 'span':
19312                 if (className.indexOf('disabled') < 0) {
19313                     this.viewDate.setUTCDate(1);
19314                     if (className.indexOf('month') > -1) {
19315                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19316                     } else {
19317                         var year = parseInt(html, 10) || 0;
19318                         this.viewDate.setUTCFullYear(year);
19319                         
19320                     }
19321                     
19322                     if(this.singleMode){
19323                         this.setValue(this.formatDate(this.viewDate));
19324                         this.hidePopup();
19325                         return;
19326                     }
19327                     
19328                     this.showMode(-1);
19329                     this.fill();
19330                 }
19331                 break;
19332                 
19333             case 'td':
19334                 //Roo.log(className);
19335                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19336                     var day = parseInt(html, 10) || 1;
19337                     var year = this.viewDate.getUTCFullYear(),
19338                         month = this.viewDate.getUTCMonth();
19339
19340                     if (className.indexOf('old') > -1) {
19341                         if(month === 0 ){
19342                             month = 11;
19343                             year -= 1;
19344                         }else{
19345                             month -= 1;
19346                         }
19347                     } else if (className.indexOf('new') > -1) {
19348                         if (month == 11) {
19349                             month = 0;
19350                             year += 1;
19351                         } else {
19352                             month += 1;
19353                         }
19354                     }
19355                     //Roo.log([year,month,day]);
19356                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19357                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19358 //                    this.fill();
19359                     //Roo.log(this.formatDate(this.date));
19360                     this.setValue(this.formatDate(this.date));
19361                     this.hidePopup();
19362                 }
19363                 break;
19364         }
19365     },
19366     
19367     setStartDate: function(startDate)
19368     {
19369         this.startDate = startDate || -Infinity;
19370         if (this.startDate !== -Infinity) {
19371             this.startDate = this.parseDate(this.startDate);
19372         }
19373         this.update();
19374         this.updateNavArrows();
19375     },
19376
19377     setEndDate: function(endDate)
19378     {
19379         this.endDate = endDate || Infinity;
19380         if (this.endDate !== Infinity) {
19381             this.endDate = this.parseDate(this.endDate);
19382         }
19383         this.update();
19384         this.updateNavArrows();
19385     },
19386     
19387     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19388     {
19389         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19390         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19391             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19392         }
19393         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19394             return parseInt(d, 10);
19395         });
19396         this.update();
19397         this.updateNavArrows();
19398     },
19399     
19400     updateNavArrows: function() 
19401     {
19402         if(this.singleMode){
19403             return;
19404         }
19405         
19406         var d = new Date(this.viewDate),
19407         year = d.getUTCFullYear(),
19408         month = d.getUTCMonth();
19409         
19410         Roo.each(this.picker().select('.prev', true).elements, function(v){
19411             v.show();
19412             switch (this.viewMode) {
19413                 case 0:
19414
19415                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19416                         v.hide();
19417                     }
19418                     break;
19419                 case 1:
19420                 case 2:
19421                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19422                         v.hide();
19423                     }
19424                     break;
19425             }
19426         });
19427         
19428         Roo.each(this.picker().select('.next', true).elements, function(v){
19429             v.show();
19430             switch (this.viewMode) {
19431                 case 0:
19432
19433                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19434                         v.hide();
19435                     }
19436                     break;
19437                 case 1:
19438                 case 2:
19439                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19440                         v.hide();
19441                     }
19442                     break;
19443             }
19444         })
19445     },
19446     
19447     moveMonth: function(date, dir)
19448     {
19449         if (!dir) {
19450             return date;
19451         }
19452         var new_date = new Date(date.valueOf()),
19453         day = new_date.getUTCDate(),
19454         month = new_date.getUTCMonth(),
19455         mag = Math.abs(dir),
19456         new_month, test;
19457         dir = dir > 0 ? 1 : -1;
19458         if (mag == 1){
19459             test = dir == -1
19460             // If going back one month, make sure month is not current month
19461             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19462             ? function(){
19463                 return new_date.getUTCMonth() == month;
19464             }
19465             // If going forward one month, make sure month is as expected
19466             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19467             : function(){
19468                 return new_date.getUTCMonth() != new_month;
19469             };
19470             new_month = month + dir;
19471             new_date.setUTCMonth(new_month);
19472             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19473             if (new_month < 0 || new_month > 11) {
19474                 new_month = (new_month + 12) % 12;
19475             }
19476         } else {
19477             // For magnitudes >1, move one month at a time...
19478             for (var i=0; i<mag; i++) {
19479                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19480                 new_date = this.moveMonth(new_date, dir);
19481             }
19482             // ...then reset the day, keeping it in the new month
19483             new_month = new_date.getUTCMonth();
19484             new_date.setUTCDate(day);
19485             test = function(){
19486                 return new_month != new_date.getUTCMonth();
19487             };
19488         }
19489         // Common date-resetting loop -- if date is beyond end of month, make it
19490         // end of month
19491         while (test()){
19492             new_date.setUTCDate(--day);
19493             new_date.setUTCMonth(new_month);
19494         }
19495         return new_date;
19496     },
19497
19498     moveYear: function(date, dir)
19499     {
19500         return this.moveMonth(date, dir*12);
19501     },
19502
19503     dateWithinRange: function(date)
19504     {
19505         return date >= this.startDate && date <= this.endDate;
19506     },
19507
19508     
19509     remove: function() 
19510     {
19511         this.picker().remove();
19512     },
19513     
19514     validateValue : function(value)
19515     {
19516         if(this.getVisibilityEl().hasClass('hidden')){
19517             return true;
19518         }
19519         
19520         if(value.length < 1)  {
19521             if(this.allowBlank){
19522                 return true;
19523             }
19524             return false;
19525         }
19526         
19527         if(value.length < this.minLength){
19528             return false;
19529         }
19530         if(value.length > this.maxLength){
19531             return false;
19532         }
19533         if(this.vtype){
19534             var vt = Roo.form.VTypes;
19535             if(!vt[this.vtype](value, this)){
19536                 return false;
19537             }
19538         }
19539         if(typeof this.validator == "function"){
19540             var msg = this.validator(value);
19541             if(msg !== true){
19542                 return false;
19543             }
19544         }
19545         
19546         if(this.regex && !this.regex.test(value)){
19547             return false;
19548         }
19549         
19550         if(typeof(this.parseDate(value)) == 'undefined'){
19551             return false;
19552         }
19553         
19554         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19555             return false;
19556         }      
19557         
19558         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19559             return false;
19560         } 
19561         
19562         
19563         return true;
19564     },
19565     
19566     reset : function()
19567     {
19568         this.date = this.viewDate = '';
19569         
19570         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19571     }
19572    
19573 });
19574
19575 Roo.apply(Roo.bootstrap.DateField,  {
19576     
19577     head : {
19578         tag: 'thead',
19579         cn: [
19580         {
19581             tag: 'tr',
19582             cn: [
19583             {
19584                 tag: 'th',
19585                 cls: 'prev',
19586                 html: '<i class="fa fa-arrow-left"/>'
19587             },
19588             {
19589                 tag: 'th',
19590                 cls: 'switch',
19591                 colspan: '5'
19592             },
19593             {
19594                 tag: 'th',
19595                 cls: 'next',
19596                 html: '<i class="fa fa-arrow-right"/>'
19597             }
19598
19599             ]
19600         }
19601         ]
19602     },
19603     
19604     content : {
19605         tag: 'tbody',
19606         cn: [
19607         {
19608             tag: 'tr',
19609             cn: [
19610             {
19611                 tag: 'td',
19612                 colspan: '7'
19613             }
19614             ]
19615         }
19616         ]
19617     },
19618     
19619     footer : {
19620         tag: 'tfoot',
19621         cn: [
19622         {
19623             tag: 'tr',
19624             cn: [
19625             {
19626                 tag: 'th',
19627                 colspan: '7',
19628                 cls: 'today'
19629             }
19630                     
19631             ]
19632         }
19633         ]
19634     },
19635     
19636     dates:{
19637         en: {
19638             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19639             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19640             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19641             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19642             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19643             today: "Today"
19644         }
19645     },
19646     
19647     modes: [
19648     {
19649         clsName: 'days',
19650         navFnc: 'Month',
19651         navStep: 1
19652     },
19653     {
19654         clsName: 'months',
19655         navFnc: 'FullYear',
19656         navStep: 1
19657     },
19658     {
19659         clsName: 'years',
19660         navFnc: 'FullYear',
19661         navStep: 10
19662     }]
19663 });
19664
19665 Roo.apply(Roo.bootstrap.DateField,  {
19666   
19667     template : {
19668         tag: 'div',
19669         cls: 'datepicker dropdown-menu roo-dynamic',
19670         cn: [
19671         {
19672             tag: 'div',
19673             cls: 'datepicker-days',
19674             cn: [
19675             {
19676                 tag: 'table',
19677                 cls: 'table-condensed',
19678                 cn:[
19679                 Roo.bootstrap.DateField.head,
19680                 {
19681                     tag: 'tbody'
19682                 },
19683                 Roo.bootstrap.DateField.footer
19684                 ]
19685             }
19686             ]
19687         },
19688         {
19689             tag: 'div',
19690             cls: 'datepicker-months',
19691             cn: [
19692             {
19693                 tag: 'table',
19694                 cls: 'table-condensed',
19695                 cn:[
19696                 Roo.bootstrap.DateField.head,
19697                 Roo.bootstrap.DateField.content,
19698                 Roo.bootstrap.DateField.footer
19699                 ]
19700             }
19701             ]
19702         },
19703         {
19704             tag: 'div',
19705             cls: 'datepicker-years',
19706             cn: [
19707             {
19708                 tag: 'table',
19709                 cls: 'table-condensed',
19710                 cn:[
19711                 Roo.bootstrap.DateField.head,
19712                 Roo.bootstrap.DateField.content,
19713                 Roo.bootstrap.DateField.footer
19714                 ]
19715             }
19716             ]
19717         }
19718         ]
19719     }
19720 });
19721
19722  
19723
19724  /*
19725  * - LGPL
19726  *
19727  * TimeField
19728  * 
19729  */
19730
19731 /**
19732  * @class Roo.bootstrap.TimeField
19733  * @extends Roo.bootstrap.Input
19734  * Bootstrap DateField class
19735  * 
19736  * 
19737  * @constructor
19738  * Create a new TimeField
19739  * @param {Object} config The config object
19740  */
19741
19742 Roo.bootstrap.TimeField = function(config){
19743     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19744     this.addEvents({
19745             /**
19746              * @event show
19747              * Fires when this field show.
19748              * @param {Roo.bootstrap.DateField} thisthis
19749              * @param {Mixed} date The date value
19750              */
19751             show : true,
19752             /**
19753              * @event show
19754              * Fires when this field hide.
19755              * @param {Roo.bootstrap.DateField} this
19756              * @param {Mixed} date The date value
19757              */
19758             hide : true,
19759             /**
19760              * @event select
19761              * Fires when select a date.
19762              * @param {Roo.bootstrap.DateField} this
19763              * @param {Mixed} date The date value
19764              */
19765             select : true
19766         });
19767 };
19768
19769 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19770     
19771     /**
19772      * @cfg {String} format
19773      * The default time format string which can be overriden for localization support.  The format must be
19774      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19775      */
19776     format : "H:i",
19777        
19778     onRender: function(ct, position)
19779     {
19780         
19781         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19782                 
19783         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19784         
19785         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19786         
19787         this.pop = this.picker().select('>.datepicker-time',true).first();
19788         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19789         
19790         this.picker().on('mousedown', this.onMousedown, this);
19791         this.picker().on('click', this.onClick, this);
19792         
19793         this.picker().addClass('datepicker-dropdown');
19794     
19795         this.fillTime();
19796         this.update();
19797             
19798         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19799         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19800         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19801         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19802         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19803         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19804
19805     },
19806     
19807     fireKey: function(e){
19808         if (!this.picker().isVisible()){
19809             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19810                 this.show();
19811             }
19812             return;
19813         }
19814
19815         e.preventDefault();
19816         
19817         switch(e.keyCode){
19818             case 27: // escape
19819                 this.hide();
19820                 break;
19821             case 37: // left
19822             case 39: // right
19823                 this.onTogglePeriod();
19824                 break;
19825             case 38: // up
19826                 this.onIncrementMinutes();
19827                 break;
19828             case 40: // down
19829                 this.onDecrementMinutes();
19830                 break;
19831             case 13: // enter
19832             case 9: // tab
19833                 this.setTime();
19834                 break;
19835         }
19836     },
19837     
19838     onClick: function(e) {
19839         e.stopPropagation();
19840         e.preventDefault();
19841     },
19842     
19843     picker : function()
19844     {
19845         return this.el.select('.datepicker', true).first();
19846     },
19847     
19848     fillTime: function()
19849     {    
19850         var time = this.pop.select('tbody', true).first();
19851         
19852         time.dom.innerHTML = '';
19853         
19854         time.createChild({
19855             tag: 'tr',
19856             cn: [
19857                 {
19858                     tag: 'td',
19859                     cn: [
19860                         {
19861                             tag: 'a',
19862                             href: '#',
19863                             cls: 'btn',
19864                             cn: [
19865                                 {
19866                                     tag: 'span',
19867                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19868                                 }
19869                             ]
19870                         } 
19871                     ]
19872                 },
19873                 {
19874                     tag: 'td',
19875                     cls: 'separator'
19876                 },
19877                 {
19878                     tag: 'td',
19879                     cn: [
19880                         {
19881                             tag: 'a',
19882                             href: '#',
19883                             cls: 'btn',
19884                             cn: [
19885                                 {
19886                                     tag: 'span',
19887                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19888                                 }
19889                             ]
19890                         }
19891                     ]
19892                 },
19893                 {
19894                     tag: 'td',
19895                     cls: 'separator'
19896                 }
19897             ]
19898         });
19899         
19900         time.createChild({
19901             tag: 'tr',
19902             cn: [
19903                 {
19904                     tag: 'td',
19905                     cn: [
19906                         {
19907                             tag: 'span',
19908                             cls: 'timepicker-hour',
19909                             html: '00'
19910                         }  
19911                     ]
19912                 },
19913                 {
19914                     tag: 'td',
19915                     cls: 'separator',
19916                     html: ':'
19917                 },
19918                 {
19919                     tag: 'td',
19920                     cn: [
19921                         {
19922                             tag: 'span',
19923                             cls: 'timepicker-minute',
19924                             html: '00'
19925                         }  
19926                     ]
19927                 },
19928                 {
19929                     tag: 'td',
19930                     cls: 'separator'
19931                 },
19932                 {
19933                     tag: 'td',
19934                     cn: [
19935                         {
19936                             tag: 'button',
19937                             type: 'button',
19938                             cls: 'btn btn-primary period',
19939                             html: 'AM'
19940                             
19941                         }
19942                     ]
19943                 }
19944             ]
19945         });
19946         
19947         time.createChild({
19948             tag: 'tr',
19949             cn: [
19950                 {
19951                     tag: 'td',
19952                     cn: [
19953                         {
19954                             tag: 'a',
19955                             href: '#',
19956                             cls: 'btn',
19957                             cn: [
19958                                 {
19959                                     tag: 'span',
19960                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19961                                 }
19962                             ]
19963                         }
19964                     ]
19965                 },
19966                 {
19967                     tag: 'td',
19968                     cls: 'separator'
19969                 },
19970                 {
19971                     tag: 'td',
19972                     cn: [
19973                         {
19974                             tag: 'a',
19975                             href: '#',
19976                             cls: 'btn',
19977                             cn: [
19978                                 {
19979                                     tag: 'span',
19980                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19981                                 }
19982                             ]
19983                         }
19984                     ]
19985                 },
19986                 {
19987                     tag: 'td',
19988                     cls: 'separator'
19989                 }
19990             ]
19991         });
19992         
19993     },
19994     
19995     update: function()
19996     {
19997         
19998         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19999         
20000         this.fill();
20001     },
20002     
20003     fill: function() 
20004     {
20005         var hours = this.time.getHours();
20006         var minutes = this.time.getMinutes();
20007         var period = 'AM';
20008         
20009         if(hours > 11){
20010             period = 'PM';
20011         }
20012         
20013         if(hours == 0){
20014             hours = 12;
20015         }
20016         
20017         
20018         if(hours > 12){
20019             hours = hours - 12;
20020         }
20021         
20022         if(hours < 10){
20023             hours = '0' + hours;
20024         }
20025         
20026         if(minutes < 10){
20027             minutes = '0' + minutes;
20028         }
20029         
20030         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20031         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20032         this.pop.select('button', true).first().dom.innerHTML = period;
20033         
20034     },
20035     
20036     place: function()
20037     {   
20038         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20039         
20040         var cls = ['bottom'];
20041         
20042         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20043             cls.pop();
20044             cls.push('top');
20045         }
20046         
20047         cls.push('right');
20048         
20049         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20050             cls.pop();
20051             cls.push('left');
20052         }
20053         
20054         this.picker().addClass(cls.join('-'));
20055         
20056         var _this = this;
20057         
20058         Roo.each(cls, function(c){
20059             if(c == 'bottom'){
20060                 _this.picker().setTop(_this.inputEl().getHeight());
20061                 return;
20062             }
20063             if(c == 'top'){
20064                 _this.picker().setTop(0 - _this.picker().getHeight());
20065                 return;
20066             }
20067             
20068             if(c == 'left'){
20069                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20070                 return;
20071             }
20072             if(c == 'right'){
20073                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20074                 return;
20075             }
20076         });
20077         
20078     },
20079   
20080     onFocus : function()
20081     {
20082         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20083         this.show();
20084     },
20085     
20086     onBlur : function()
20087     {
20088         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20089         this.hide();
20090     },
20091     
20092     show : function()
20093     {
20094         this.picker().show();
20095         this.pop.show();
20096         this.update();
20097         this.place();
20098         
20099         this.fireEvent('show', this, this.date);
20100     },
20101     
20102     hide : function()
20103     {
20104         this.picker().hide();
20105         this.pop.hide();
20106         
20107         this.fireEvent('hide', this, this.date);
20108     },
20109     
20110     setTime : function()
20111     {
20112         this.hide();
20113         this.setValue(this.time.format(this.format));
20114         
20115         this.fireEvent('select', this, this.date);
20116         
20117         
20118     },
20119     
20120     onMousedown: function(e){
20121         e.stopPropagation();
20122         e.preventDefault();
20123     },
20124     
20125     onIncrementHours: function()
20126     {
20127         Roo.log('onIncrementHours');
20128         this.time = this.time.add(Date.HOUR, 1);
20129         this.update();
20130         
20131     },
20132     
20133     onDecrementHours: function()
20134     {
20135         Roo.log('onDecrementHours');
20136         this.time = this.time.add(Date.HOUR, -1);
20137         this.update();
20138     },
20139     
20140     onIncrementMinutes: function()
20141     {
20142         Roo.log('onIncrementMinutes');
20143         this.time = this.time.add(Date.MINUTE, 1);
20144         this.update();
20145     },
20146     
20147     onDecrementMinutes: function()
20148     {
20149         Roo.log('onDecrementMinutes');
20150         this.time = this.time.add(Date.MINUTE, -1);
20151         this.update();
20152     },
20153     
20154     onTogglePeriod: function()
20155     {
20156         Roo.log('onTogglePeriod');
20157         this.time = this.time.add(Date.HOUR, 12);
20158         this.update();
20159     }
20160     
20161    
20162 });
20163
20164 Roo.apply(Roo.bootstrap.TimeField,  {
20165     
20166     content : {
20167         tag: 'tbody',
20168         cn: [
20169             {
20170                 tag: 'tr',
20171                 cn: [
20172                 {
20173                     tag: 'td',
20174                     colspan: '7'
20175                 }
20176                 ]
20177             }
20178         ]
20179     },
20180     
20181     footer : {
20182         tag: 'tfoot',
20183         cn: [
20184             {
20185                 tag: 'tr',
20186                 cn: [
20187                 {
20188                     tag: 'th',
20189                     colspan: '7',
20190                     cls: '',
20191                     cn: [
20192                         {
20193                             tag: 'button',
20194                             cls: 'btn btn-info ok',
20195                             html: 'OK'
20196                         }
20197                     ]
20198                 }
20199
20200                 ]
20201             }
20202         ]
20203     }
20204 });
20205
20206 Roo.apply(Roo.bootstrap.TimeField,  {
20207   
20208     template : {
20209         tag: 'div',
20210         cls: 'datepicker dropdown-menu',
20211         cn: [
20212             {
20213                 tag: 'div',
20214                 cls: 'datepicker-time',
20215                 cn: [
20216                 {
20217                     tag: 'table',
20218                     cls: 'table-condensed',
20219                     cn:[
20220                     Roo.bootstrap.TimeField.content,
20221                     Roo.bootstrap.TimeField.footer
20222                     ]
20223                 }
20224                 ]
20225             }
20226         ]
20227     }
20228 });
20229
20230  
20231
20232  /*
20233  * - LGPL
20234  *
20235  * MonthField
20236  * 
20237  */
20238
20239 /**
20240  * @class Roo.bootstrap.MonthField
20241  * @extends Roo.bootstrap.Input
20242  * Bootstrap MonthField class
20243  * 
20244  * @cfg {String} language default en
20245  * 
20246  * @constructor
20247  * Create a new MonthField
20248  * @param {Object} config The config object
20249  */
20250
20251 Roo.bootstrap.MonthField = function(config){
20252     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20253     
20254     this.addEvents({
20255         /**
20256          * @event show
20257          * Fires when this field show.
20258          * @param {Roo.bootstrap.MonthField} this
20259          * @param {Mixed} date The date value
20260          */
20261         show : true,
20262         /**
20263          * @event show
20264          * Fires when this field hide.
20265          * @param {Roo.bootstrap.MonthField} this
20266          * @param {Mixed} date The date value
20267          */
20268         hide : true,
20269         /**
20270          * @event select
20271          * Fires when select a date.
20272          * @param {Roo.bootstrap.MonthField} this
20273          * @param {String} oldvalue The old value
20274          * @param {String} newvalue The new value
20275          */
20276         select : true
20277     });
20278 };
20279
20280 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20281     
20282     onRender: function(ct, position)
20283     {
20284         
20285         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20286         
20287         this.language = this.language || 'en';
20288         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20289         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20290         
20291         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20292         this.isInline = false;
20293         this.isInput = true;
20294         this.component = this.el.select('.add-on', true).first() || false;
20295         this.component = (this.component && this.component.length === 0) ? false : this.component;
20296         this.hasInput = this.component && this.inputEL().length;
20297         
20298         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20299         
20300         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20301         
20302         this.picker().on('mousedown', this.onMousedown, this);
20303         this.picker().on('click', this.onClick, this);
20304         
20305         this.picker().addClass('datepicker-dropdown');
20306         
20307         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20308             v.setStyle('width', '189px');
20309         });
20310         
20311         this.fillMonths();
20312         
20313         this.update();
20314         
20315         if(this.isInline) {
20316             this.show();
20317         }
20318         
20319     },
20320     
20321     setValue: function(v, suppressEvent)
20322     {   
20323         var o = this.getValue();
20324         
20325         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20326         
20327         this.update();
20328
20329         if(suppressEvent !== true){
20330             this.fireEvent('select', this, o, v);
20331         }
20332         
20333     },
20334     
20335     getValue: function()
20336     {
20337         return this.value;
20338     },
20339     
20340     onClick: function(e) 
20341     {
20342         e.stopPropagation();
20343         e.preventDefault();
20344         
20345         var target = e.getTarget();
20346         
20347         if(target.nodeName.toLowerCase() === 'i'){
20348             target = Roo.get(target).dom.parentNode;
20349         }
20350         
20351         var nodeName = target.nodeName;
20352         var className = target.className;
20353         var html = target.innerHTML;
20354         
20355         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20356             return;
20357         }
20358         
20359         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20360         
20361         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20362         
20363         this.hide();
20364                         
20365     },
20366     
20367     picker : function()
20368     {
20369         return this.pickerEl;
20370     },
20371     
20372     fillMonths: function()
20373     {    
20374         var i = 0;
20375         var months = this.picker().select('>.datepicker-months td', true).first();
20376         
20377         months.dom.innerHTML = '';
20378         
20379         while (i < 12) {
20380             var month = {
20381                 tag: 'span',
20382                 cls: 'month',
20383                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20384             };
20385             
20386             months.createChild(month);
20387         }
20388         
20389     },
20390     
20391     update: function()
20392     {
20393         var _this = this;
20394         
20395         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20396             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20397         }
20398         
20399         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20400             e.removeClass('active');
20401             
20402             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20403                 e.addClass('active');
20404             }
20405         })
20406     },
20407     
20408     place: function()
20409     {
20410         if(this.isInline) {
20411             return;
20412         }
20413         
20414         this.picker().removeClass(['bottom', 'top']);
20415         
20416         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20417             /*
20418              * place to the top of element!
20419              *
20420              */
20421             
20422             this.picker().addClass('top');
20423             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20424             
20425             return;
20426         }
20427         
20428         this.picker().addClass('bottom');
20429         
20430         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20431     },
20432     
20433     onFocus : function()
20434     {
20435         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20436         this.show();
20437     },
20438     
20439     onBlur : function()
20440     {
20441         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20442         
20443         var d = this.inputEl().getValue();
20444         
20445         this.setValue(d);
20446                 
20447         this.hide();
20448     },
20449     
20450     show : function()
20451     {
20452         this.picker().show();
20453         this.picker().select('>.datepicker-months', true).first().show();
20454         this.update();
20455         this.place();
20456         
20457         this.fireEvent('show', this, this.date);
20458     },
20459     
20460     hide : function()
20461     {
20462         if(this.isInline) {
20463             return;
20464         }
20465         this.picker().hide();
20466         this.fireEvent('hide', this, this.date);
20467         
20468     },
20469     
20470     onMousedown: function(e)
20471     {
20472         e.stopPropagation();
20473         e.preventDefault();
20474     },
20475     
20476     keyup: function(e)
20477     {
20478         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20479         this.update();
20480     },
20481
20482     fireKey: function(e)
20483     {
20484         if (!this.picker().isVisible()){
20485             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20486                 this.show();
20487             }
20488             return;
20489         }
20490         
20491         var dir;
20492         
20493         switch(e.keyCode){
20494             case 27: // escape
20495                 this.hide();
20496                 e.preventDefault();
20497                 break;
20498             case 37: // left
20499             case 39: // right
20500                 dir = e.keyCode == 37 ? -1 : 1;
20501                 
20502                 this.vIndex = this.vIndex + dir;
20503                 
20504                 if(this.vIndex < 0){
20505                     this.vIndex = 0;
20506                 }
20507                 
20508                 if(this.vIndex > 11){
20509                     this.vIndex = 11;
20510                 }
20511                 
20512                 if(isNaN(this.vIndex)){
20513                     this.vIndex = 0;
20514                 }
20515                 
20516                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20517                 
20518                 break;
20519             case 38: // up
20520             case 40: // down
20521                 
20522                 dir = e.keyCode == 38 ? -1 : 1;
20523                 
20524                 this.vIndex = this.vIndex + dir * 4;
20525                 
20526                 if(this.vIndex < 0){
20527                     this.vIndex = 0;
20528                 }
20529                 
20530                 if(this.vIndex > 11){
20531                     this.vIndex = 11;
20532                 }
20533                 
20534                 if(isNaN(this.vIndex)){
20535                     this.vIndex = 0;
20536                 }
20537                 
20538                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20539                 break;
20540                 
20541             case 13: // enter
20542                 
20543                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20544                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20545                 }
20546                 
20547                 this.hide();
20548                 e.preventDefault();
20549                 break;
20550             case 9: // tab
20551                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20552                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20553                 }
20554                 this.hide();
20555                 break;
20556             case 16: // shift
20557             case 17: // ctrl
20558             case 18: // alt
20559                 break;
20560             default :
20561                 this.hide();
20562                 
20563         }
20564     },
20565     
20566     remove: function() 
20567     {
20568         this.picker().remove();
20569     }
20570    
20571 });
20572
20573 Roo.apply(Roo.bootstrap.MonthField,  {
20574     
20575     content : {
20576         tag: 'tbody',
20577         cn: [
20578         {
20579             tag: 'tr',
20580             cn: [
20581             {
20582                 tag: 'td',
20583                 colspan: '7'
20584             }
20585             ]
20586         }
20587         ]
20588     },
20589     
20590     dates:{
20591         en: {
20592             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20593             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20594         }
20595     }
20596 });
20597
20598 Roo.apply(Roo.bootstrap.MonthField,  {
20599   
20600     template : {
20601         tag: 'div',
20602         cls: 'datepicker dropdown-menu roo-dynamic',
20603         cn: [
20604             {
20605                 tag: 'div',
20606                 cls: 'datepicker-months',
20607                 cn: [
20608                 {
20609                     tag: 'table',
20610                     cls: 'table-condensed',
20611                     cn:[
20612                         Roo.bootstrap.DateField.content
20613                     ]
20614                 }
20615                 ]
20616             }
20617         ]
20618     }
20619 });
20620
20621  
20622
20623  
20624  /*
20625  * - LGPL
20626  *
20627  * CheckBox
20628  * 
20629  */
20630
20631 /**
20632  * @class Roo.bootstrap.CheckBox
20633  * @extends Roo.bootstrap.Input
20634  * Bootstrap CheckBox class
20635  * 
20636  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20637  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20638  * @cfg {String} boxLabel The text that appears beside the checkbox
20639  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20640  * @cfg {Boolean} checked initnal the element
20641  * @cfg {Boolean} inline inline the element (default false)
20642  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20643  * @cfg {String} tooltip label tooltip
20644  * 
20645  * @constructor
20646  * Create a new CheckBox
20647  * @param {Object} config The config object
20648  */
20649
20650 Roo.bootstrap.CheckBox = function(config){
20651     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20652    
20653     this.addEvents({
20654         /**
20655         * @event check
20656         * Fires when the element is checked or unchecked.
20657         * @param {Roo.bootstrap.CheckBox} this This input
20658         * @param {Boolean} checked The new checked value
20659         */
20660        check : true,
20661        /**
20662         * @event click
20663         * Fires when the element is click.
20664         * @param {Roo.bootstrap.CheckBox} this This input
20665         */
20666        click : true
20667     });
20668     
20669 };
20670
20671 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20672   
20673     inputType: 'checkbox',
20674     inputValue: 1,
20675     valueOff: 0,
20676     boxLabel: false,
20677     checked: false,
20678     weight : false,
20679     inline: false,
20680     tooltip : '',
20681     
20682     getAutoCreate : function()
20683     {
20684         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20685         
20686         var id = Roo.id();
20687         
20688         var cfg = {};
20689         
20690         cfg.cls = 'form-group ' + this.inputType; //input-group
20691         
20692         if(this.inline){
20693             cfg.cls += ' ' + this.inputType + '-inline';
20694         }
20695         
20696         var input =  {
20697             tag: 'input',
20698             id : id,
20699             type : this.inputType,
20700             value : this.inputValue,
20701             cls : 'roo-' + this.inputType, //'form-box',
20702             placeholder : this.placeholder || ''
20703             
20704         };
20705         
20706         if(this.inputType != 'radio'){
20707             var hidden =  {
20708                 tag: 'input',
20709                 type : 'hidden',
20710                 cls : 'roo-hidden-value',
20711                 value : this.checked ? this.inputValue : this.valueOff
20712             };
20713         }
20714         
20715             
20716         if (this.weight) { // Validity check?
20717             cfg.cls += " " + this.inputType + "-" + this.weight;
20718         }
20719         
20720         if (this.disabled) {
20721             input.disabled=true;
20722         }
20723         
20724         if(this.checked){
20725             input.checked = this.checked;
20726         }
20727         
20728         if (this.name) {
20729             
20730             input.name = this.name;
20731             
20732             if(this.inputType != 'radio'){
20733                 hidden.name = this.name;
20734                 input.name = '_hidden_' + this.name;
20735             }
20736         }
20737         
20738         if (this.size) {
20739             input.cls += ' input-' + this.size;
20740         }
20741         
20742         var settings=this;
20743         
20744         ['xs','sm','md','lg'].map(function(size){
20745             if (settings[size]) {
20746                 cfg.cls += ' col-' + size + '-' + settings[size];
20747             }
20748         });
20749         
20750         var inputblock = input;
20751          
20752         if (this.before || this.after) {
20753             
20754             inputblock = {
20755                 cls : 'input-group',
20756                 cn :  [] 
20757             };
20758             
20759             if (this.before) {
20760                 inputblock.cn.push({
20761                     tag :'span',
20762                     cls : 'input-group-addon',
20763                     html : this.before
20764                 });
20765             }
20766             
20767             inputblock.cn.push(input);
20768             
20769             if(this.inputType != 'radio'){
20770                 inputblock.cn.push(hidden);
20771             }
20772             
20773             if (this.after) {
20774                 inputblock.cn.push({
20775                     tag :'span',
20776                     cls : 'input-group-addon',
20777                     html : this.after
20778                 });
20779             }
20780             
20781         }
20782         
20783         if (align ==='left' && this.fieldLabel.length) {
20784 //                Roo.log("left and has label");
20785             cfg.cn = [
20786                 {
20787                     tag: 'label',
20788                     'for' :  id,
20789                     cls : 'control-label',
20790                     html : this.fieldLabel
20791                 },
20792                 {
20793                     cls : "", 
20794                     cn: [
20795                         inputblock
20796                     ]
20797                 }
20798             ];
20799             
20800             if(this.labelWidth > 12){
20801                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20802             }
20803             
20804             if(this.labelWidth < 13 && this.labelmd == 0){
20805                 this.labelmd = this.labelWidth;
20806             }
20807             
20808             if(this.labellg > 0){
20809                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20810                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20811             }
20812             
20813             if(this.labelmd > 0){
20814                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20815                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20816             }
20817             
20818             if(this.labelsm > 0){
20819                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20820                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20821             }
20822             
20823             if(this.labelxs > 0){
20824                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20825                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20826             }
20827             
20828         } else if ( this.fieldLabel.length) {
20829 //                Roo.log(" label");
20830                 cfg.cn = [
20831                    
20832                     {
20833                         tag: this.boxLabel ? 'span' : 'label',
20834                         'for': id,
20835                         cls: 'control-label box-input-label',
20836                         //cls : 'input-group-addon',
20837                         html : this.fieldLabel
20838                     },
20839                     
20840                     inputblock
20841                     
20842                 ];
20843
20844         } else {
20845             
20846 //                Roo.log(" no label && no align");
20847                 cfg.cn = [  inputblock ] ;
20848                 
20849                 
20850         }
20851         
20852         if(this.boxLabel){
20853              var boxLabelCfg = {
20854                 tag: 'label',
20855                 //'for': id, // box label is handled by onclick - so no for...
20856                 cls: 'box-label',
20857                 html: this.boxLabel
20858             };
20859             
20860             if(this.tooltip){
20861                 boxLabelCfg.tooltip = this.tooltip;
20862             }
20863              
20864             cfg.cn.push(boxLabelCfg);
20865         }
20866         
20867         if(this.inputType != 'radio'){
20868             cfg.cn.push(hidden);
20869         }
20870         
20871         return cfg;
20872         
20873     },
20874     
20875     /**
20876      * return the real input element.
20877      */
20878     inputEl: function ()
20879     {
20880         return this.el.select('input.roo-' + this.inputType,true).first();
20881     },
20882     hiddenEl: function ()
20883     {
20884         return this.el.select('input.roo-hidden-value',true).first();
20885     },
20886     
20887     labelEl: function()
20888     {
20889         return this.el.select('label.control-label',true).first();
20890     },
20891     /* depricated... */
20892     
20893     label: function()
20894     {
20895         return this.labelEl();
20896     },
20897     
20898     boxLabelEl: function()
20899     {
20900         return this.el.select('label.box-label',true).first();
20901     },
20902     
20903     initEvents : function()
20904     {
20905 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20906         
20907         this.inputEl().on('click', this.onClick,  this);
20908         
20909         if (this.boxLabel) { 
20910             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20911         }
20912         
20913         this.startValue = this.getValue();
20914         
20915         if(this.groupId){
20916             Roo.bootstrap.CheckBox.register(this);
20917         }
20918     },
20919     
20920     onClick : function(e)
20921     {   
20922         if(this.fireEvent('click', this, e) !== false){
20923             this.setChecked(!this.checked);
20924         }
20925         
20926     },
20927     
20928     setChecked : function(state,suppressEvent)
20929     {
20930         this.startValue = this.getValue();
20931
20932         if(this.inputType == 'radio'){
20933             
20934             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20935                 e.dom.checked = false;
20936             });
20937             
20938             this.inputEl().dom.checked = true;
20939             
20940             this.inputEl().dom.value = this.inputValue;
20941             
20942             if(suppressEvent !== true){
20943                 this.fireEvent('check', this, true);
20944             }
20945             
20946             this.validate();
20947             
20948             return;
20949         }
20950         
20951         this.checked = state;
20952         
20953         this.inputEl().dom.checked = state;
20954         
20955         
20956         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20957         
20958         if(suppressEvent !== true){
20959             this.fireEvent('check', this, state);
20960         }
20961         
20962         this.validate();
20963     },
20964     
20965     getValue : function()
20966     {
20967         if(this.inputType == 'radio'){
20968             return this.getGroupValue();
20969         }
20970         
20971         return this.hiddenEl().dom.value;
20972         
20973     },
20974     
20975     getGroupValue : function()
20976     {
20977         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20978             return '';
20979         }
20980         
20981         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20982     },
20983     
20984     setValue : function(v,suppressEvent)
20985     {
20986         if(this.inputType == 'radio'){
20987             this.setGroupValue(v, suppressEvent);
20988             return;
20989         }
20990         
20991         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20992         
20993         this.validate();
20994     },
20995     
20996     setGroupValue : function(v, suppressEvent)
20997     {
20998         this.startValue = this.getValue();
20999         
21000         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21001             e.dom.checked = false;
21002             
21003             if(e.dom.value == v){
21004                 e.dom.checked = true;
21005             }
21006         });
21007         
21008         if(suppressEvent !== true){
21009             this.fireEvent('check', this, true);
21010         }
21011
21012         this.validate();
21013         
21014         return;
21015     },
21016     
21017     validate : function()
21018     {
21019         if(this.getVisibilityEl().hasClass('hidden')){
21020             return true;
21021         }
21022         
21023         if(
21024                 this.disabled || 
21025                 (this.inputType == 'radio' && this.validateRadio()) ||
21026                 (this.inputType == 'checkbox' && this.validateCheckbox())
21027         ){
21028             this.markValid();
21029             return true;
21030         }
21031         
21032         this.markInvalid();
21033         return false;
21034     },
21035     
21036     validateRadio : function()
21037     {
21038         if(this.getVisibilityEl().hasClass('hidden')){
21039             return true;
21040         }
21041         
21042         if(this.allowBlank){
21043             return true;
21044         }
21045         
21046         var valid = false;
21047         
21048         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21049             if(!e.dom.checked){
21050                 return;
21051             }
21052             
21053             valid = true;
21054             
21055             return false;
21056         });
21057         
21058         return valid;
21059     },
21060     
21061     validateCheckbox : function()
21062     {
21063         if(!this.groupId){
21064             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21065             //return (this.getValue() == this.inputValue) ? true : false;
21066         }
21067         
21068         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21069         
21070         if(!group){
21071             return false;
21072         }
21073         
21074         var r = false;
21075         
21076         for(var i in group){
21077             if(group[i].el.isVisible(true)){
21078                 r = false;
21079                 break;
21080             }
21081             
21082             r = true;
21083         }
21084         
21085         for(var i in group){
21086             if(r){
21087                 break;
21088             }
21089             
21090             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21091         }
21092         
21093         return r;
21094     },
21095     
21096     /**
21097      * Mark this field as valid
21098      */
21099     markValid : function()
21100     {
21101         var _this = this;
21102         
21103         this.fireEvent('valid', this);
21104         
21105         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21106         
21107         if(this.groupId){
21108             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21109         }
21110         
21111         if(label){
21112             label.markValid();
21113         }
21114
21115         if(this.inputType == 'radio'){
21116             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21117                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21118                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21119             });
21120             
21121             return;
21122         }
21123
21124         if(!this.groupId){
21125             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21126             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21127             return;
21128         }
21129         
21130         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21131         
21132         if(!group){
21133             return;
21134         }
21135         
21136         for(var i in group){
21137             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21138             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21139         }
21140     },
21141     
21142      /**
21143      * Mark this field as invalid
21144      * @param {String} msg The validation message
21145      */
21146     markInvalid : function(msg)
21147     {
21148         if(this.allowBlank){
21149             return;
21150         }
21151         
21152         var _this = this;
21153         
21154         this.fireEvent('invalid', this, msg);
21155         
21156         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21157         
21158         if(this.groupId){
21159             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21160         }
21161         
21162         if(label){
21163             label.markInvalid();
21164         }
21165             
21166         if(this.inputType == 'radio'){
21167             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21168                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21169                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21170             });
21171             
21172             return;
21173         }
21174         
21175         if(!this.groupId){
21176             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21177             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21178             return;
21179         }
21180         
21181         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21182         
21183         if(!group){
21184             return;
21185         }
21186         
21187         for(var i in group){
21188             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21189             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21190         }
21191         
21192     },
21193     
21194     clearInvalid : function()
21195     {
21196         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21197         
21198         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21199         
21200         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21201         
21202         if (label && label.iconEl) {
21203             label.iconEl.removeClass(label.validClass);
21204             label.iconEl.removeClass(label.invalidClass);
21205         }
21206     },
21207     
21208     disable : function()
21209     {
21210         if(this.inputType != 'radio'){
21211             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21212             return;
21213         }
21214         
21215         var _this = this;
21216         
21217         if(this.rendered){
21218             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21219                 _this.getActionEl().addClass(this.disabledClass);
21220                 e.dom.disabled = true;
21221             });
21222         }
21223         
21224         this.disabled = true;
21225         this.fireEvent("disable", this);
21226         return this;
21227     },
21228
21229     enable : function()
21230     {
21231         if(this.inputType != 'radio'){
21232             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21233             return;
21234         }
21235         
21236         var _this = this;
21237         
21238         if(this.rendered){
21239             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21240                 _this.getActionEl().removeClass(this.disabledClass);
21241                 e.dom.disabled = false;
21242             });
21243         }
21244         
21245         this.disabled = false;
21246         this.fireEvent("enable", this);
21247         return this;
21248     },
21249     
21250     setBoxLabel : function(v)
21251     {
21252         this.boxLabel = v;
21253         
21254         if(this.rendered){
21255             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21256         }
21257     }
21258
21259 });
21260
21261 Roo.apply(Roo.bootstrap.CheckBox, {
21262     
21263     groups: {},
21264     
21265      /**
21266     * register a CheckBox Group
21267     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21268     */
21269     register : function(checkbox)
21270     {
21271         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21272             this.groups[checkbox.groupId] = {};
21273         }
21274         
21275         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21276             return;
21277         }
21278         
21279         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21280         
21281     },
21282     /**
21283     * fetch a CheckBox Group based on the group ID
21284     * @param {string} the group ID
21285     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21286     */
21287     get: function(groupId) {
21288         if (typeof(this.groups[groupId]) == 'undefined') {
21289             return false;
21290         }
21291         
21292         return this.groups[groupId] ;
21293     }
21294     
21295     
21296 });
21297 /*
21298  * - LGPL
21299  *
21300  * RadioItem
21301  * 
21302  */
21303
21304 /**
21305  * @class Roo.bootstrap.Radio
21306  * @extends Roo.bootstrap.Component
21307  * Bootstrap Radio class
21308  * @cfg {String} boxLabel - the label associated
21309  * @cfg {String} value - the value of radio
21310  * 
21311  * @constructor
21312  * Create a new Radio
21313  * @param {Object} config The config object
21314  */
21315 Roo.bootstrap.Radio = function(config){
21316     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21317     
21318 };
21319
21320 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21321     
21322     boxLabel : '',
21323     
21324     value : '',
21325     
21326     getAutoCreate : function()
21327     {
21328         var cfg = {
21329             tag : 'div',
21330             cls : 'form-group radio',
21331             cn : [
21332                 {
21333                     tag : 'label',
21334                     cls : 'box-label',
21335                     html : this.boxLabel
21336                 }
21337             ]
21338         };
21339         
21340         return cfg;
21341     },
21342     
21343     initEvents : function() 
21344     {
21345         this.parent().register(this);
21346         
21347         this.el.on('click', this.onClick, this);
21348         
21349     },
21350     
21351     onClick : function(e)
21352     {
21353         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21354             this.setChecked(true);
21355         }
21356     },
21357     
21358     setChecked : function(state, suppressEvent)
21359     {
21360         this.parent().setValue(this.value, suppressEvent);
21361         
21362     },
21363     
21364     setBoxLabel : function(v)
21365     {
21366         this.boxLabel = v;
21367         
21368         if(this.rendered){
21369             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21370         }
21371     }
21372     
21373 });
21374  
21375
21376  /*
21377  * - LGPL
21378  *
21379  * Input
21380  * 
21381  */
21382
21383 /**
21384  * @class Roo.bootstrap.SecurePass
21385  * @extends Roo.bootstrap.Input
21386  * Bootstrap SecurePass class
21387  *
21388  * 
21389  * @constructor
21390  * Create a new SecurePass
21391  * @param {Object} config The config object
21392  */
21393  
21394 Roo.bootstrap.SecurePass = function (config) {
21395     // these go here, so the translation tool can replace them..
21396     this.errors = {
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         TooWeak: "Your password is Too Weak."
21405     },
21406     this.meterLabel = "Password strength:";
21407     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21408     this.meterClass = [
21409         "roo-password-meter-tooweak", 
21410         "roo-password-meter-weak", 
21411         "roo-password-meter-medium", 
21412         "roo-password-meter-strong", 
21413         "roo-password-meter-grey"
21414     ];
21415     
21416     this.errors = {};
21417     
21418     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21419 }
21420
21421 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21422     /**
21423      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21424      * {
21425      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21426      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21427      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21428      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21429      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21430      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21431      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21432      * })
21433      */
21434     // private
21435     
21436     meterWidth: 300,
21437     errorMsg :'',    
21438     errors: false,
21439     imageRoot: '/',
21440     /**
21441      * @cfg {String/Object} Label for the strength meter (defaults to
21442      * 'Password strength:')
21443      */
21444     // private
21445     meterLabel: '',
21446     /**
21447      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21448      * ['Weak', 'Medium', 'Strong'])
21449      */
21450     // private    
21451     pwdStrengths: false,    
21452     // private
21453     strength: 0,
21454     // private
21455     _lastPwd: null,
21456     // private
21457     kCapitalLetter: 0,
21458     kSmallLetter: 1,
21459     kDigit: 2,
21460     kPunctuation: 3,
21461     
21462     insecure: false,
21463     // private
21464     initEvents: function ()
21465     {
21466         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21467
21468         if (this.el.is('input[type=password]') && Roo.isSafari) {
21469             this.el.on('keydown', this.SafariOnKeyDown, this);
21470         }
21471
21472         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21473     },
21474     // private
21475     onRender: function (ct, position)
21476     {
21477         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21478         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21479         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21480
21481         this.trigger.createChild({
21482                    cn: [
21483                     {
21484                     //id: 'PwdMeter',
21485                     tag: 'div',
21486                     cls: 'roo-password-meter-grey col-xs-12',
21487                     style: {
21488                         //width: 0,
21489                         //width: this.meterWidth + 'px'                                                
21490                         }
21491                     },
21492                     {                            
21493                          cls: 'roo-password-meter-text'                          
21494                     }
21495                 ]            
21496         });
21497
21498          
21499         if (this.hideTrigger) {
21500             this.trigger.setDisplayed(false);
21501         }
21502         this.setSize(this.width || '', this.height || '');
21503     },
21504     // private
21505     onDestroy: function ()
21506     {
21507         if (this.trigger) {
21508             this.trigger.removeAllListeners();
21509             this.trigger.remove();
21510         }
21511         if (this.wrap) {
21512             this.wrap.remove();
21513         }
21514         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21515     },
21516     // private
21517     checkStrength: function ()
21518     {
21519         var pwd = this.inputEl().getValue();
21520         if (pwd == this._lastPwd) {
21521             return;
21522         }
21523
21524         var strength;
21525         if (this.ClientSideStrongPassword(pwd)) {
21526             strength = 3;
21527         } else if (this.ClientSideMediumPassword(pwd)) {
21528             strength = 2;
21529         } else if (this.ClientSideWeakPassword(pwd)) {
21530             strength = 1;
21531         } else {
21532             strength = 0;
21533         }
21534         
21535         Roo.log('strength1: ' + strength);
21536         
21537         //var pm = this.trigger.child('div/div/div').dom;
21538         var pm = this.trigger.child('div/div');
21539         pm.removeClass(this.meterClass);
21540         pm.addClass(this.meterClass[strength]);
21541                 
21542         
21543         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21544                 
21545         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21546         
21547         this._lastPwd = pwd;
21548     },
21549     reset: function ()
21550     {
21551         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21552         
21553         this._lastPwd = '';
21554         
21555         var pm = this.trigger.child('div/div');
21556         pm.removeClass(this.meterClass);
21557         pm.addClass('roo-password-meter-grey');        
21558         
21559         
21560         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21561         
21562         pt.innerHTML = '';
21563         this.inputEl().dom.type='password';
21564     },
21565     // private
21566     validateValue: function (value)
21567     {
21568         
21569         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21570             return false;
21571         }
21572         if (value.length == 0) {
21573             if (this.allowBlank) {
21574                 this.clearInvalid();
21575                 return true;
21576             }
21577
21578             this.markInvalid(this.errors.PwdEmpty);
21579             this.errorMsg = this.errors.PwdEmpty;
21580             return false;
21581         }
21582         
21583         if(this.insecure){
21584             return true;
21585         }
21586         
21587         if ('[\x21-\x7e]*'.match(value)) {
21588             this.markInvalid(this.errors.PwdBadChar);
21589             this.errorMsg = this.errors.PwdBadChar;
21590             return false;
21591         }
21592         if (value.length < 6) {
21593             this.markInvalid(this.errors.PwdShort);
21594             this.errorMsg = this.errors.PwdShort;
21595             return false;
21596         }
21597         if (value.length > 16) {
21598             this.markInvalid(this.errors.PwdLong);
21599             this.errorMsg = this.errors.PwdLong;
21600             return false;
21601         }
21602         var strength;
21603         if (this.ClientSideStrongPassword(value)) {
21604             strength = 3;
21605         } else if (this.ClientSideMediumPassword(value)) {
21606             strength = 2;
21607         } else if (this.ClientSideWeakPassword(value)) {
21608             strength = 1;
21609         } else {
21610             strength = 0;
21611         }
21612
21613         
21614         if (strength < 2) {
21615             //this.markInvalid(this.errors.TooWeak);
21616             this.errorMsg = this.errors.TooWeak;
21617             //return false;
21618         }
21619         
21620         
21621         console.log('strength2: ' + strength);
21622         
21623         //var pm = this.trigger.child('div/div/div').dom;
21624         
21625         var pm = this.trigger.child('div/div');
21626         pm.removeClass(this.meterClass);
21627         pm.addClass(this.meterClass[strength]);
21628                 
21629         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21630                 
21631         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21632         
21633         this.errorMsg = ''; 
21634         return true;
21635     },
21636     // private
21637     CharacterSetChecks: function (type)
21638     {
21639         this.type = type;
21640         this.fResult = false;
21641     },
21642     // private
21643     isctype: function (character, type)
21644     {
21645         switch (type) {  
21646             case this.kCapitalLetter:
21647                 if (character >= 'A' && character <= 'Z') {
21648                     return true;
21649                 }
21650                 break;
21651             
21652             case this.kSmallLetter:
21653                 if (character >= 'a' && character <= 'z') {
21654                     return true;
21655                 }
21656                 break;
21657             
21658             case this.kDigit:
21659                 if (character >= '0' && character <= '9') {
21660                     return true;
21661                 }
21662                 break;
21663             
21664             case this.kPunctuation:
21665                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21666                     return true;
21667                 }
21668                 break;
21669             
21670             default:
21671                 return false;
21672         }
21673
21674     },
21675     // private
21676     IsLongEnough: function (pwd, size)
21677     {
21678         return !(pwd == null || isNaN(size) || pwd.length < size);
21679     },
21680     // private
21681     SpansEnoughCharacterSets: function (word, nb)
21682     {
21683         if (!this.IsLongEnough(word, nb))
21684         {
21685             return false;
21686         }
21687
21688         var characterSetChecks = new Array(
21689             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21690             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21691         );
21692         
21693         for (var index = 0; index < word.length; ++index) {
21694             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21695                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21696                     characterSetChecks[nCharSet].fResult = true;
21697                     break;
21698                 }
21699             }
21700         }
21701
21702         var nCharSets = 0;
21703         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21704             if (characterSetChecks[nCharSet].fResult) {
21705                 ++nCharSets;
21706             }
21707         }
21708
21709         if (nCharSets < nb) {
21710             return false;
21711         }
21712         return true;
21713     },
21714     // private
21715     ClientSideStrongPassword: function (pwd)
21716     {
21717         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21718     },
21719     // private
21720     ClientSideMediumPassword: function (pwd)
21721     {
21722         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21723     },
21724     // private
21725     ClientSideWeakPassword: function (pwd)
21726     {
21727         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21728     }
21729           
21730 })//<script type="text/javascript">
21731
21732 /*
21733  * Based  Ext JS Library 1.1.1
21734  * Copyright(c) 2006-2007, Ext JS, LLC.
21735  * LGPL
21736  *
21737  */
21738  
21739 /**
21740  * @class Roo.HtmlEditorCore
21741  * @extends Roo.Component
21742  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21743  *
21744  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21745  */
21746
21747 Roo.HtmlEditorCore = function(config){
21748     
21749     
21750     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21751     
21752     
21753     this.addEvents({
21754         /**
21755          * @event initialize
21756          * Fires when the editor is fully initialized (including the iframe)
21757          * @param {Roo.HtmlEditorCore} this
21758          */
21759         initialize: true,
21760         /**
21761          * @event activate
21762          * Fires when the editor is first receives the focus. Any insertion must wait
21763          * until after this event.
21764          * @param {Roo.HtmlEditorCore} this
21765          */
21766         activate: true,
21767          /**
21768          * @event beforesync
21769          * Fires before the textarea is updated with content from the editor iframe. Return false
21770          * to cancel the sync.
21771          * @param {Roo.HtmlEditorCore} this
21772          * @param {String} html
21773          */
21774         beforesync: true,
21775          /**
21776          * @event beforepush
21777          * Fires before the iframe editor is updated with content from the textarea. Return false
21778          * to cancel the push.
21779          * @param {Roo.HtmlEditorCore} this
21780          * @param {String} html
21781          */
21782         beforepush: true,
21783          /**
21784          * @event sync
21785          * Fires when the textarea is updated with content from the editor iframe.
21786          * @param {Roo.HtmlEditorCore} this
21787          * @param {String} html
21788          */
21789         sync: true,
21790          /**
21791          * @event push
21792          * Fires when the iframe editor is updated with content from the textarea.
21793          * @param {Roo.HtmlEditorCore} this
21794          * @param {String} html
21795          */
21796         push: true,
21797         
21798         /**
21799          * @event editorevent
21800          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21801          * @param {Roo.HtmlEditorCore} this
21802          */
21803         editorevent: true
21804         
21805     });
21806     
21807     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21808     
21809     // defaults : white / black...
21810     this.applyBlacklists();
21811     
21812     
21813     
21814 };
21815
21816
21817 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21818
21819
21820      /**
21821      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21822      */
21823     
21824     owner : false,
21825     
21826      /**
21827      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21828      *                        Roo.resizable.
21829      */
21830     resizable : false,
21831      /**
21832      * @cfg {Number} height (in pixels)
21833      */   
21834     height: 300,
21835    /**
21836      * @cfg {Number} width (in pixels)
21837      */   
21838     width: 500,
21839     
21840     /**
21841      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21842      * 
21843      */
21844     stylesheets: false,
21845     
21846     // id of frame..
21847     frameId: false,
21848     
21849     // private properties
21850     validationEvent : false,
21851     deferHeight: true,
21852     initialized : false,
21853     activated : false,
21854     sourceEditMode : false,
21855     onFocus : Roo.emptyFn,
21856     iframePad:3,
21857     hideMode:'offsets',
21858     
21859     clearUp: true,
21860     
21861     // blacklist + whitelisted elements..
21862     black: false,
21863     white: false,
21864      
21865     bodyCls : '',
21866
21867     /**
21868      * Protected method that will not generally be called directly. It
21869      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21870      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21871      */
21872     getDocMarkup : function(){
21873         // body styles..
21874         var st = '';
21875         
21876         // inherit styels from page...?? 
21877         if (this.stylesheets === false) {
21878             
21879             Roo.get(document.head).select('style').each(function(node) {
21880                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21881             });
21882             
21883             Roo.get(document.head).select('link').each(function(node) { 
21884                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21885             });
21886             
21887         } else if (!this.stylesheets.length) {
21888                 // simple..
21889                 st = '<style type="text/css">' +
21890                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21891                    '</style>';
21892         } else { 
21893             st = '<style type="text/css">' +
21894                     this.stylesheets +
21895                 '</style>';
21896         }
21897         
21898         st +=  '<style type="text/css">' +
21899             'IMG { cursor: pointer } ' +
21900         '</style>';
21901
21902         var cls = 'roo-htmleditor-body';
21903         
21904         if(this.bodyCls.length){
21905             cls += ' ' + this.bodyCls;
21906         }
21907         
21908         return '<html><head>' + st  +
21909             //<style type="text/css">' +
21910             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21911             //'</style>' +
21912             ' </head><body class="' +  cls + '"></body></html>';
21913     },
21914
21915     // private
21916     onRender : function(ct, position)
21917     {
21918         var _t = this;
21919         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21920         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21921         
21922         
21923         this.el.dom.style.border = '0 none';
21924         this.el.dom.setAttribute('tabIndex', -1);
21925         this.el.addClass('x-hidden hide');
21926         
21927         
21928         
21929         if(Roo.isIE){ // fix IE 1px bogus margin
21930             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21931         }
21932        
21933         
21934         this.frameId = Roo.id();
21935         
21936          
21937         
21938         var iframe = this.owner.wrap.createChild({
21939             tag: 'iframe',
21940             cls: 'form-control', // bootstrap..
21941             id: this.frameId,
21942             name: this.frameId,
21943             frameBorder : 'no',
21944             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21945         }, this.el
21946         );
21947         
21948         
21949         this.iframe = iframe.dom;
21950
21951          this.assignDocWin();
21952         
21953         this.doc.designMode = 'on';
21954        
21955         this.doc.open();
21956         this.doc.write(this.getDocMarkup());
21957         this.doc.close();
21958
21959         
21960         var task = { // must defer to wait for browser to be ready
21961             run : function(){
21962                 //console.log("run task?" + this.doc.readyState);
21963                 this.assignDocWin();
21964                 if(this.doc.body || this.doc.readyState == 'complete'){
21965                     try {
21966                         this.doc.designMode="on";
21967                     } catch (e) {
21968                         return;
21969                     }
21970                     Roo.TaskMgr.stop(task);
21971                     this.initEditor.defer(10, this);
21972                 }
21973             },
21974             interval : 10,
21975             duration: 10000,
21976             scope: this
21977         };
21978         Roo.TaskMgr.start(task);
21979
21980     },
21981
21982     // private
21983     onResize : function(w, h)
21984     {
21985          Roo.log('resize: ' +w + ',' + h );
21986         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21987         if(!this.iframe){
21988             return;
21989         }
21990         if(typeof w == 'number'){
21991             
21992             this.iframe.style.width = w + 'px';
21993         }
21994         if(typeof h == 'number'){
21995             
21996             this.iframe.style.height = h + 'px';
21997             if(this.doc){
21998                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21999             }
22000         }
22001         
22002     },
22003
22004     /**
22005      * Toggles the editor between standard and source edit mode.
22006      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22007      */
22008     toggleSourceEdit : function(sourceEditMode){
22009         
22010         this.sourceEditMode = sourceEditMode === true;
22011         
22012         if(this.sourceEditMode){
22013  
22014             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22015             
22016         }else{
22017             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22018             //this.iframe.className = '';
22019             this.deferFocus();
22020         }
22021         //this.setSize(this.owner.wrap.getSize());
22022         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22023     },
22024
22025     
22026   
22027
22028     /**
22029      * Protected method that will not generally be called directly. If you need/want
22030      * custom HTML cleanup, this is the method you should override.
22031      * @param {String} html The HTML to be cleaned
22032      * return {String} The cleaned HTML
22033      */
22034     cleanHtml : function(html){
22035         html = String(html);
22036         if(html.length > 5){
22037             if(Roo.isSafari){ // strip safari nonsense
22038                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22039             }
22040         }
22041         if(html == '&nbsp;'){
22042             html = '';
22043         }
22044         return html;
22045     },
22046
22047     /**
22048      * HTML Editor -> Textarea
22049      * Protected method that will not generally be called directly. Syncs the contents
22050      * of the editor iframe with the textarea.
22051      */
22052     syncValue : function(){
22053         if(this.initialized){
22054             var bd = (this.doc.body || this.doc.documentElement);
22055             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22056             var html = bd.innerHTML;
22057             if(Roo.isSafari){
22058                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22059                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22060                 if(m && m[1]){
22061                     html = '<div style="'+m[0]+'">' + html + '</div>';
22062                 }
22063             }
22064             html = this.cleanHtml(html);
22065             // fix up the special chars.. normaly like back quotes in word...
22066             // however we do not want to do this with chinese..
22067             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22068                 var cc = b.charCodeAt();
22069                 if (
22070                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22071                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22072                     (cc >= 0xf900 && cc < 0xfb00 )
22073                 ) {
22074                         return b;
22075                 }
22076                 return "&#"+cc+";" 
22077             });
22078             if(this.owner.fireEvent('beforesync', this, html) !== false){
22079                 this.el.dom.value = html;
22080                 this.owner.fireEvent('sync', this, html);
22081             }
22082         }
22083     },
22084
22085     /**
22086      * Protected method that will not generally be called directly. Pushes the value of the textarea
22087      * into the iframe editor.
22088      */
22089     pushValue : function(){
22090         if(this.initialized){
22091             var v = this.el.dom.value.trim();
22092             
22093 //            if(v.length < 1){
22094 //                v = '&#160;';
22095 //            }
22096             
22097             if(this.owner.fireEvent('beforepush', this, v) !== false){
22098                 var d = (this.doc.body || this.doc.documentElement);
22099                 d.innerHTML = v;
22100                 this.cleanUpPaste();
22101                 this.el.dom.value = d.innerHTML;
22102                 this.owner.fireEvent('push', this, v);
22103             }
22104         }
22105     },
22106
22107     // private
22108     deferFocus : function(){
22109         this.focus.defer(10, this);
22110     },
22111
22112     // doc'ed in Field
22113     focus : function(){
22114         if(this.win && !this.sourceEditMode){
22115             this.win.focus();
22116         }else{
22117             this.el.focus();
22118         }
22119     },
22120     
22121     assignDocWin: function()
22122     {
22123         var iframe = this.iframe;
22124         
22125          if(Roo.isIE){
22126             this.doc = iframe.contentWindow.document;
22127             this.win = iframe.contentWindow;
22128         } else {
22129 //            if (!Roo.get(this.frameId)) {
22130 //                return;
22131 //            }
22132 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22133 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22134             
22135             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22136                 return;
22137             }
22138             
22139             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22140             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22141         }
22142     },
22143     
22144     // private
22145     initEditor : function(){
22146         //console.log("INIT EDITOR");
22147         this.assignDocWin();
22148         
22149         
22150         
22151         this.doc.designMode="on";
22152         this.doc.open();
22153         this.doc.write(this.getDocMarkup());
22154         this.doc.close();
22155         
22156         var dbody = (this.doc.body || this.doc.documentElement);
22157         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22158         // this copies styles from the containing element into thsi one..
22159         // not sure why we need all of this..
22160         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22161         
22162         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22163         //ss['background-attachment'] = 'fixed'; // w3c
22164         dbody.bgProperties = 'fixed'; // ie
22165         //Roo.DomHelper.applyStyles(dbody, ss);
22166         Roo.EventManager.on(this.doc, {
22167             //'mousedown': this.onEditorEvent,
22168             'mouseup': this.onEditorEvent,
22169             'dblclick': this.onEditorEvent,
22170             'click': this.onEditorEvent,
22171             'keyup': this.onEditorEvent,
22172             buffer:100,
22173             scope: this
22174         });
22175         if(Roo.isGecko){
22176             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22177         }
22178         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22179             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22180         }
22181         this.initialized = true;
22182
22183         this.owner.fireEvent('initialize', this);
22184         this.pushValue();
22185     },
22186
22187     // private
22188     onDestroy : function(){
22189         
22190         
22191         
22192         if(this.rendered){
22193             
22194             //for (var i =0; i < this.toolbars.length;i++) {
22195             //    // fixme - ask toolbars for heights?
22196             //    this.toolbars[i].onDestroy();
22197            // }
22198             
22199             //this.wrap.dom.innerHTML = '';
22200             //this.wrap.remove();
22201         }
22202     },
22203
22204     // private
22205     onFirstFocus : function(){
22206         
22207         this.assignDocWin();
22208         
22209         
22210         this.activated = true;
22211          
22212     
22213         if(Roo.isGecko){ // prevent silly gecko errors
22214             this.win.focus();
22215             var s = this.win.getSelection();
22216             if(!s.focusNode || s.focusNode.nodeType != 3){
22217                 var r = s.getRangeAt(0);
22218                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22219                 r.collapse(true);
22220                 this.deferFocus();
22221             }
22222             try{
22223                 this.execCmd('useCSS', true);
22224                 this.execCmd('styleWithCSS', false);
22225             }catch(e){}
22226         }
22227         this.owner.fireEvent('activate', this);
22228     },
22229
22230     // private
22231     adjustFont: function(btn){
22232         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22233         //if(Roo.isSafari){ // safari
22234         //    adjust *= 2;
22235        // }
22236         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22237         if(Roo.isSafari){ // safari
22238             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22239             v =  (v < 10) ? 10 : v;
22240             v =  (v > 48) ? 48 : v;
22241             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22242             
22243         }
22244         
22245         
22246         v = Math.max(1, v+adjust);
22247         
22248         this.execCmd('FontSize', v  );
22249     },
22250
22251     onEditorEvent : function(e)
22252     {
22253         this.owner.fireEvent('editorevent', this, e);
22254       //  this.updateToolbar();
22255         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22256     },
22257
22258     insertTag : function(tg)
22259     {
22260         // could be a bit smarter... -> wrap the current selected tRoo..
22261         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22262             
22263             range = this.createRange(this.getSelection());
22264             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22265             wrappingNode.appendChild(range.extractContents());
22266             range.insertNode(wrappingNode);
22267
22268             return;
22269             
22270             
22271             
22272         }
22273         this.execCmd("formatblock",   tg);
22274         
22275     },
22276     
22277     insertText : function(txt)
22278     {
22279         
22280         
22281         var range = this.createRange();
22282         range.deleteContents();
22283                //alert(Sender.getAttribute('label'));
22284                
22285         range.insertNode(this.doc.createTextNode(txt));
22286     } ,
22287     
22288      
22289
22290     /**
22291      * Executes a Midas editor command on the editor document and performs necessary focus and
22292      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22293      * @param {String} cmd The Midas command
22294      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22295      */
22296     relayCmd : function(cmd, value){
22297         this.win.focus();
22298         this.execCmd(cmd, value);
22299         this.owner.fireEvent('editorevent', this);
22300         //this.updateToolbar();
22301         this.owner.deferFocus();
22302     },
22303
22304     /**
22305      * Executes a Midas editor command directly on the editor document.
22306      * For visual commands, you should use {@link #relayCmd} instead.
22307      * <b>This should only be called after the editor is initialized.</b>
22308      * @param {String} cmd The Midas command
22309      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22310      */
22311     execCmd : function(cmd, value){
22312         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22313         this.syncValue();
22314     },
22315  
22316  
22317    
22318     /**
22319      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22320      * to insert tRoo.
22321      * @param {String} text | dom node.. 
22322      */
22323     insertAtCursor : function(text)
22324     {
22325         
22326         if(!this.activated){
22327             return;
22328         }
22329         /*
22330         if(Roo.isIE){
22331             this.win.focus();
22332             var r = this.doc.selection.createRange();
22333             if(r){
22334                 r.collapse(true);
22335                 r.pasteHTML(text);
22336                 this.syncValue();
22337                 this.deferFocus();
22338             
22339             }
22340             return;
22341         }
22342         */
22343         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22344             this.win.focus();
22345             
22346             
22347             // from jquery ui (MIT licenced)
22348             var range, node;
22349             var win = this.win;
22350             
22351             if (win.getSelection && win.getSelection().getRangeAt) {
22352                 range = win.getSelection().getRangeAt(0);
22353                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22354                 range.insertNode(node);
22355             } else if (win.document.selection && win.document.selection.createRange) {
22356                 // no firefox support
22357                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22358                 win.document.selection.createRange().pasteHTML(txt);
22359             } else {
22360                 // no firefox support
22361                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22362                 this.execCmd('InsertHTML', txt);
22363             } 
22364             
22365             this.syncValue();
22366             
22367             this.deferFocus();
22368         }
22369     },
22370  // private
22371     mozKeyPress : function(e){
22372         if(e.ctrlKey){
22373             var c = e.getCharCode(), cmd;
22374           
22375             if(c > 0){
22376                 c = String.fromCharCode(c).toLowerCase();
22377                 switch(c){
22378                     case 'b':
22379                         cmd = 'bold';
22380                         break;
22381                     case 'i':
22382                         cmd = 'italic';
22383                         break;
22384                     
22385                     case 'u':
22386                         cmd = 'underline';
22387                         break;
22388                     
22389                     case 'v':
22390                         this.cleanUpPaste.defer(100, this);
22391                         return;
22392                         
22393                 }
22394                 if(cmd){
22395                     this.win.focus();
22396                     this.execCmd(cmd);
22397                     this.deferFocus();
22398                     e.preventDefault();
22399                 }
22400                 
22401             }
22402         }
22403     },
22404
22405     // private
22406     fixKeys : function(){ // load time branching for fastest keydown performance
22407         if(Roo.isIE){
22408             return function(e){
22409                 var k = e.getKey(), r;
22410                 if(k == e.TAB){
22411                     e.stopEvent();
22412                     r = this.doc.selection.createRange();
22413                     if(r){
22414                         r.collapse(true);
22415                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22416                         this.deferFocus();
22417                     }
22418                     return;
22419                 }
22420                 
22421                 if(k == e.ENTER){
22422                     r = this.doc.selection.createRange();
22423                     if(r){
22424                         var target = r.parentElement();
22425                         if(!target || target.tagName.toLowerCase() != 'li'){
22426                             e.stopEvent();
22427                             r.pasteHTML('<br />');
22428                             r.collapse(false);
22429                             r.select();
22430                         }
22431                     }
22432                 }
22433                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22434                     this.cleanUpPaste.defer(100, this);
22435                     return;
22436                 }
22437                 
22438                 
22439             };
22440         }else if(Roo.isOpera){
22441             return function(e){
22442                 var k = e.getKey();
22443                 if(k == e.TAB){
22444                     e.stopEvent();
22445                     this.win.focus();
22446                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22447                     this.deferFocus();
22448                 }
22449                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22450                     this.cleanUpPaste.defer(100, this);
22451                     return;
22452                 }
22453                 
22454             };
22455         }else if(Roo.isSafari){
22456             return function(e){
22457                 var k = e.getKey();
22458                 
22459                 if(k == e.TAB){
22460                     e.stopEvent();
22461                     this.execCmd('InsertText','\t');
22462                     this.deferFocus();
22463                     return;
22464                 }
22465                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22466                     this.cleanUpPaste.defer(100, this);
22467                     return;
22468                 }
22469                 
22470              };
22471         }
22472     }(),
22473     
22474     getAllAncestors: function()
22475     {
22476         var p = this.getSelectedNode();
22477         var a = [];
22478         if (!p) {
22479             a.push(p); // push blank onto stack..
22480             p = this.getParentElement();
22481         }
22482         
22483         
22484         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22485             a.push(p);
22486             p = p.parentNode;
22487         }
22488         a.push(this.doc.body);
22489         return a;
22490     },
22491     lastSel : false,
22492     lastSelNode : false,
22493     
22494     
22495     getSelection : function() 
22496     {
22497         this.assignDocWin();
22498         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22499     },
22500     
22501     getSelectedNode: function() 
22502     {
22503         // this may only work on Gecko!!!
22504         
22505         // should we cache this!!!!
22506         
22507         
22508         
22509          
22510         var range = this.createRange(this.getSelection()).cloneRange();
22511         
22512         if (Roo.isIE) {
22513             var parent = range.parentElement();
22514             while (true) {
22515                 var testRange = range.duplicate();
22516                 testRange.moveToElementText(parent);
22517                 if (testRange.inRange(range)) {
22518                     break;
22519                 }
22520                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22521                     break;
22522                 }
22523                 parent = parent.parentElement;
22524             }
22525             return parent;
22526         }
22527         
22528         // is ancestor a text element.
22529         var ac =  range.commonAncestorContainer;
22530         if (ac.nodeType == 3) {
22531             ac = ac.parentNode;
22532         }
22533         
22534         var ar = ac.childNodes;
22535          
22536         var nodes = [];
22537         var other_nodes = [];
22538         var has_other_nodes = false;
22539         for (var i=0;i<ar.length;i++) {
22540             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22541                 continue;
22542             }
22543             // fullly contained node.
22544             
22545             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22546                 nodes.push(ar[i]);
22547                 continue;
22548             }
22549             
22550             // probably selected..
22551             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22552                 other_nodes.push(ar[i]);
22553                 continue;
22554             }
22555             // outer..
22556             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22557                 continue;
22558             }
22559             
22560             
22561             has_other_nodes = true;
22562         }
22563         if (!nodes.length && other_nodes.length) {
22564             nodes= other_nodes;
22565         }
22566         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22567             return false;
22568         }
22569         
22570         return nodes[0];
22571     },
22572     createRange: function(sel)
22573     {
22574         // this has strange effects when using with 
22575         // top toolbar - not sure if it's a great idea.
22576         //this.editor.contentWindow.focus();
22577         if (typeof sel != "undefined") {
22578             try {
22579                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22580             } catch(e) {
22581                 return this.doc.createRange();
22582             }
22583         } else {
22584             return this.doc.createRange();
22585         }
22586     },
22587     getParentElement: function()
22588     {
22589         
22590         this.assignDocWin();
22591         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22592         
22593         var range = this.createRange(sel);
22594          
22595         try {
22596             var p = range.commonAncestorContainer;
22597             while (p.nodeType == 3) { // text node
22598                 p = p.parentNode;
22599             }
22600             return p;
22601         } catch (e) {
22602             return null;
22603         }
22604     
22605     },
22606     /***
22607      *
22608      * Range intersection.. the hard stuff...
22609      *  '-1' = before
22610      *  '0' = hits..
22611      *  '1' = after.
22612      *         [ -- selected range --- ]
22613      *   [fail]                        [fail]
22614      *
22615      *    basically..
22616      *      if end is before start or  hits it. fail.
22617      *      if start is after end or hits it fail.
22618      *
22619      *   if either hits (but other is outside. - then it's not 
22620      *   
22621      *    
22622      **/
22623     
22624     
22625     // @see http://www.thismuchiknow.co.uk/?p=64.
22626     rangeIntersectsNode : 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         var rangeStartRange = range.cloneRange();
22636         rangeStartRange.collapse(true);
22637     
22638         var rangeEndRange = range.cloneRange();
22639         rangeEndRange.collapse(false);
22640     
22641         var nodeStartRange = nodeRange.cloneRange();
22642         nodeStartRange.collapse(true);
22643     
22644         var nodeEndRange = nodeRange.cloneRange();
22645         nodeEndRange.collapse(false);
22646     
22647         return rangeStartRange.compareBoundaryPoints(
22648                  Range.START_TO_START, nodeEndRange) == -1 &&
22649                rangeEndRange.compareBoundaryPoints(
22650                  Range.START_TO_START, nodeStartRange) == 1;
22651         
22652          
22653     },
22654     rangeCompareNode : function(range, node)
22655     {
22656         var nodeRange = node.ownerDocument.createRange();
22657         try {
22658             nodeRange.selectNode(node);
22659         } catch (e) {
22660             nodeRange.selectNodeContents(node);
22661         }
22662         
22663         
22664         range.collapse(true);
22665     
22666         nodeRange.collapse(true);
22667      
22668         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22669         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22670          
22671         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22672         
22673         var nodeIsBefore   =  ss == 1;
22674         var nodeIsAfter    = ee == -1;
22675         
22676         if (nodeIsBefore && nodeIsAfter) {
22677             return 0; // outer
22678         }
22679         if (!nodeIsBefore && nodeIsAfter) {
22680             return 1; //right trailed.
22681         }
22682         
22683         if (nodeIsBefore && !nodeIsAfter) {
22684             return 2;  // left trailed.
22685         }
22686         // fully contined.
22687         return 3;
22688     },
22689
22690     // private? - in a new class?
22691     cleanUpPaste :  function()
22692     {
22693         // cleans up the whole document..
22694         Roo.log('cleanuppaste');
22695         
22696         this.cleanUpChildren(this.doc.body);
22697         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22698         if (clean != this.doc.body.innerHTML) {
22699             this.doc.body.innerHTML = clean;
22700         }
22701         
22702     },
22703     
22704     cleanWordChars : function(input) {// change the chars to hex code
22705         var he = Roo.HtmlEditorCore;
22706         
22707         var output = input;
22708         Roo.each(he.swapCodes, function(sw) { 
22709             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22710             
22711             output = output.replace(swapper, sw[1]);
22712         });
22713         
22714         return output;
22715     },
22716     
22717     
22718     cleanUpChildren : function (n)
22719     {
22720         if (!n.childNodes.length) {
22721             return;
22722         }
22723         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22724            this.cleanUpChild(n.childNodes[i]);
22725         }
22726     },
22727     
22728     
22729         
22730     
22731     cleanUpChild : function (node)
22732     {
22733         var ed = this;
22734         //console.log(node);
22735         if (node.nodeName == "#text") {
22736             // clean up silly Windows -- stuff?
22737             return; 
22738         }
22739         if (node.nodeName == "#comment") {
22740             node.parentNode.removeChild(node);
22741             // clean up silly Windows -- stuff?
22742             return; 
22743         }
22744         var lcname = node.tagName.toLowerCase();
22745         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22746         // whitelist of tags..
22747         
22748         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22749             // remove node.
22750             node.parentNode.removeChild(node);
22751             return;
22752             
22753         }
22754         
22755         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22756         
22757         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22758         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22759         
22760         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22761         //    remove_keep_children = true;
22762         //}
22763         
22764         if (remove_keep_children) {
22765             this.cleanUpChildren(node);
22766             // inserts everything just before this node...
22767             while (node.childNodes.length) {
22768                 var cn = node.childNodes[0];
22769                 node.removeChild(cn);
22770                 node.parentNode.insertBefore(cn, node);
22771             }
22772             node.parentNode.removeChild(node);
22773             return;
22774         }
22775         
22776         if (!node.attributes || !node.attributes.length) {
22777             this.cleanUpChildren(node);
22778             return;
22779         }
22780         
22781         function cleanAttr(n,v)
22782         {
22783             
22784             if (v.match(/^\./) || v.match(/^\//)) {
22785                 return;
22786             }
22787             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22788                 return;
22789             }
22790             if (v.match(/^#/)) {
22791                 return;
22792             }
22793 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22794             node.removeAttribute(n);
22795             
22796         }
22797         
22798         var cwhite = this.cwhite;
22799         var cblack = this.cblack;
22800             
22801         function cleanStyle(n,v)
22802         {
22803             if (v.match(/expression/)) { //XSS?? should we even bother..
22804                 node.removeAttribute(n);
22805                 return;
22806             }
22807             
22808             var parts = v.split(/;/);
22809             var clean = [];
22810             
22811             Roo.each(parts, function(p) {
22812                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22813                 if (!p.length) {
22814                     return true;
22815                 }
22816                 var l = p.split(':').shift().replace(/\s+/g,'');
22817                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22818                 
22819                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22820 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22821                     //node.removeAttribute(n);
22822                     return true;
22823                 }
22824                 //Roo.log()
22825                 // only allow 'c whitelisted system attributes'
22826                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22827 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22828                     //node.removeAttribute(n);
22829                     return true;
22830                 }
22831                 
22832                 
22833                  
22834                 
22835                 clean.push(p);
22836                 return true;
22837             });
22838             if (clean.length) { 
22839                 node.setAttribute(n, clean.join(';'));
22840             } else {
22841                 node.removeAttribute(n);
22842             }
22843             
22844         }
22845         
22846         
22847         for (var i = node.attributes.length-1; i > -1 ; i--) {
22848             var a = node.attributes[i];
22849             //console.log(a);
22850             
22851             if (a.name.toLowerCase().substr(0,2)=='on')  {
22852                 node.removeAttribute(a.name);
22853                 continue;
22854             }
22855             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22856                 node.removeAttribute(a.name);
22857                 continue;
22858             }
22859             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22860                 cleanAttr(a.name,a.value); // fixme..
22861                 continue;
22862             }
22863             if (a.name == 'style') {
22864                 cleanStyle(a.name,a.value);
22865                 continue;
22866             }
22867             /// clean up MS crap..
22868             // tecnically this should be a list of valid class'es..
22869             
22870             
22871             if (a.name == 'class') {
22872                 if (a.value.match(/^Mso/)) {
22873                     node.className = '';
22874                 }
22875                 
22876                 if (a.value.match(/^body$/)) {
22877                     node.className = '';
22878                 }
22879                 continue;
22880             }
22881             
22882             // style cleanup!?
22883             // class cleanup?
22884             
22885         }
22886         
22887         
22888         this.cleanUpChildren(node);
22889         
22890         
22891     },
22892     
22893     /**
22894      * Clean up MS wordisms...
22895      */
22896     cleanWord : function(node)
22897     {
22898         
22899         
22900         if (!node) {
22901             this.cleanWord(this.doc.body);
22902             return;
22903         }
22904         if (node.nodeName == "#text") {
22905             // clean up silly Windows -- stuff?
22906             return; 
22907         }
22908         if (node.nodeName == "#comment") {
22909             node.parentNode.removeChild(node);
22910             // clean up silly Windows -- stuff?
22911             return; 
22912         }
22913         
22914         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22915             node.parentNode.removeChild(node);
22916             return;
22917         }
22918         
22919         // remove - but keep children..
22920         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22921             while (node.childNodes.length) {
22922                 var cn = node.childNodes[0];
22923                 node.removeChild(cn);
22924                 node.parentNode.insertBefore(cn, node);
22925             }
22926             node.parentNode.removeChild(node);
22927             this.iterateChildren(node, this.cleanWord);
22928             return;
22929         }
22930         // clean styles
22931         if (node.className.length) {
22932             
22933             var cn = node.className.split(/\W+/);
22934             var cna = [];
22935             Roo.each(cn, function(cls) {
22936                 if (cls.match(/Mso[a-zA-Z]+/)) {
22937                     return;
22938                 }
22939                 cna.push(cls);
22940             });
22941             node.className = cna.length ? cna.join(' ') : '';
22942             if (!cna.length) {
22943                 node.removeAttribute("class");
22944             }
22945         }
22946         
22947         if (node.hasAttribute("lang")) {
22948             node.removeAttribute("lang");
22949         }
22950         
22951         if (node.hasAttribute("style")) {
22952             
22953             var styles = node.getAttribute("style").split(";");
22954             var nstyle = [];
22955             Roo.each(styles, function(s) {
22956                 if (!s.match(/:/)) {
22957                     return;
22958                 }
22959                 var kv = s.split(":");
22960                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22961                     return;
22962                 }
22963                 // what ever is left... we allow.
22964                 nstyle.push(s);
22965             });
22966             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22967             if (!nstyle.length) {
22968                 node.removeAttribute('style');
22969             }
22970         }
22971         this.iterateChildren(node, this.cleanWord);
22972         
22973         
22974         
22975     },
22976     /**
22977      * iterateChildren of a Node, calling fn each time, using this as the scole..
22978      * @param {DomNode} node node to iterate children of.
22979      * @param {Function} fn method of this class to call on each item.
22980      */
22981     iterateChildren : function(node, fn)
22982     {
22983         if (!node.childNodes.length) {
22984                 return;
22985         }
22986         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22987            fn.call(this, node.childNodes[i])
22988         }
22989     },
22990     
22991     
22992     /**
22993      * cleanTableWidths.
22994      *
22995      * Quite often pasting from word etc.. results in tables with column and widths.
22996      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22997      *
22998      */
22999     cleanTableWidths : function(node)
23000     {
23001          
23002          
23003         if (!node) {
23004             this.cleanTableWidths(this.doc.body);
23005             return;
23006         }
23007         
23008         // ignore list...
23009         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23010             return; 
23011         }
23012         Roo.log(node.tagName);
23013         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23014             this.iterateChildren(node, this.cleanTableWidths);
23015             return;
23016         }
23017         if (node.hasAttribute('width')) {
23018             node.removeAttribute('width');
23019         }
23020         
23021          
23022         if (node.hasAttribute("style")) {
23023             // pretty basic...
23024             
23025             var styles = node.getAttribute("style").split(";");
23026             var nstyle = [];
23027             Roo.each(styles, function(s) {
23028                 if (!s.match(/:/)) {
23029                     return;
23030                 }
23031                 var kv = s.split(":");
23032                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23033                     return;
23034                 }
23035                 // what ever is left... we allow.
23036                 nstyle.push(s);
23037             });
23038             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23039             if (!nstyle.length) {
23040                 node.removeAttribute('style');
23041             }
23042         }
23043         
23044         this.iterateChildren(node, this.cleanTableWidths);
23045         
23046         
23047     },
23048     
23049     
23050     
23051     
23052     domToHTML : function(currentElement, depth, nopadtext) {
23053         
23054         depth = depth || 0;
23055         nopadtext = nopadtext || false;
23056     
23057         if (!currentElement) {
23058             return this.domToHTML(this.doc.body);
23059         }
23060         
23061         //Roo.log(currentElement);
23062         var j;
23063         var allText = false;
23064         var nodeName = currentElement.nodeName;
23065         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23066         
23067         if  (nodeName == '#text') {
23068             
23069             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23070         }
23071         
23072         
23073         var ret = '';
23074         if (nodeName != 'BODY') {
23075              
23076             var i = 0;
23077             // Prints the node tagName, such as <A>, <IMG>, etc
23078             if (tagName) {
23079                 var attr = [];
23080                 for(i = 0; i < currentElement.attributes.length;i++) {
23081                     // quoting?
23082                     var aname = currentElement.attributes.item(i).name;
23083                     if (!currentElement.attributes.item(i).value.length) {
23084                         continue;
23085                     }
23086                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23087                 }
23088                 
23089                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23090             } 
23091             else {
23092                 
23093                 // eack
23094             }
23095         } else {
23096             tagName = false;
23097         }
23098         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23099             return ret;
23100         }
23101         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23102             nopadtext = true;
23103         }
23104         
23105         
23106         // Traverse the tree
23107         i = 0;
23108         var currentElementChild = currentElement.childNodes.item(i);
23109         var allText = true;
23110         var innerHTML  = '';
23111         lastnode = '';
23112         while (currentElementChild) {
23113             // Formatting code (indent the tree so it looks nice on the screen)
23114             var nopad = nopadtext;
23115             if (lastnode == 'SPAN') {
23116                 nopad  = true;
23117             }
23118             // text
23119             if  (currentElementChild.nodeName == '#text') {
23120                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23121                 toadd = nopadtext ? toadd : toadd.trim();
23122                 if (!nopad && toadd.length > 80) {
23123                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23124                 }
23125                 innerHTML  += toadd;
23126                 
23127                 i++;
23128                 currentElementChild = currentElement.childNodes.item(i);
23129                 lastNode = '';
23130                 continue;
23131             }
23132             allText = false;
23133             
23134             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23135                 
23136             // Recursively traverse the tree structure of the child node
23137             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23138             lastnode = currentElementChild.nodeName;
23139             i++;
23140             currentElementChild=currentElement.childNodes.item(i);
23141         }
23142         
23143         ret += innerHTML;
23144         
23145         if (!allText) {
23146                 // The remaining code is mostly for formatting the tree
23147             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23148         }
23149         
23150         
23151         if (tagName) {
23152             ret+= "</"+tagName+">";
23153         }
23154         return ret;
23155         
23156     },
23157         
23158     applyBlacklists : function()
23159     {
23160         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23161         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23162         
23163         this.white = [];
23164         this.black = [];
23165         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23166             if (b.indexOf(tag) > -1) {
23167                 return;
23168             }
23169             this.white.push(tag);
23170             
23171         }, this);
23172         
23173         Roo.each(w, function(tag) {
23174             if (b.indexOf(tag) > -1) {
23175                 return;
23176             }
23177             if (this.white.indexOf(tag) > -1) {
23178                 return;
23179             }
23180             this.white.push(tag);
23181             
23182         }, this);
23183         
23184         
23185         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23186             if (w.indexOf(tag) > -1) {
23187                 return;
23188             }
23189             this.black.push(tag);
23190             
23191         }, this);
23192         
23193         Roo.each(b, function(tag) {
23194             if (w.indexOf(tag) > -1) {
23195                 return;
23196             }
23197             if (this.black.indexOf(tag) > -1) {
23198                 return;
23199             }
23200             this.black.push(tag);
23201             
23202         }, this);
23203         
23204         
23205         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23206         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23207         
23208         this.cwhite = [];
23209         this.cblack = [];
23210         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23211             if (b.indexOf(tag) > -1) {
23212                 return;
23213             }
23214             this.cwhite.push(tag);
23215             
23216         }, this);
23217         
23218         Roo.each(w, function(tag) {
23219             if (b.indexOf(tag) > -1) {
23220                 return;
23221             }
23222             if (this.cwhite.indexOf(tag) > -1) {
23223                 return;
23224             }
23225             this.cwhite.push(tag);
23226             
23227         }, this);
23228         
23229         
23230         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23231             if (w.indexOf(tag) > -1) {
23232                 return;
23233             }
23234             this.cblack.push(tag);
23235             
23236         }, this);
23237         
23238         Roo.each(b, function(tag) {
23239             if (w.indexOf(tag) > -1) {
23240                 return;
23241             }
23242             if (this.cblack.indexOf(tag) > -1) {
23243                 return;
23244             }
23245             this.cblack.push(tag);
23246             
23247         }, this);
23248     },
23249     
23250     setStylesheets : function(stylesheets)
23251     {
23252         if(typeof(stylesheets) == 'string'){
23253             Roo.get(this.iframe.contentDocument.head).createChild({
23254                 tag : 'link',
23255                 rel : 'stylesheet',
23256                 type : 'text/css',
23257                 href : stylesheets
23258             });
23259             
23260             return;
23261         }
23262         var _this = this;
23263      
23264         Roo.each(stylesheets, function(s) {
23265             if(!s.length){
23266                 return;
23267             }
23268             
23269             Roo.get(_this.iframe.contentDocument.head).createChild({
23270                 tag : 'link',
23271                 rel : 'stylesheet',
23272                 type : 'text/css',
23273                 href : s
23274             });
23275         });
23276
23277         
23278     },
23279     
23280     removeStylesheets : function()
23281     {
23282         var _this = this;
23283         
23284         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23285             s.remove();
23286         });
23287     },
23288     
23289     setStyle : function(style)
23290     {
23291         Roo.get(this.iframe.contentDocument.head).createChild({
23292             tag : 'style',
23293             type : 'text/css',
23294             html : style
23295         });
23296
23297         return;
23298     }
23299     
23300     // hide stuff that is not compatible
23301     /**
23302      * @event blur
23303      * @hide
23304      */
23305     /**
23306      * @event change
23307      * @hide
23308      */
23309     /**
23310      * @event focus
23311      * @hide
23312      */
23313     /**
23314      * @event specialkey
23315      * @hide
23316      */
23317     /**
23318      * @cfg {String} fieldClass @hide
23319      */
23320     /**
23321      * @cfg {String} focusClass @hide
23322      */
23323     /**
23324      * @cfg {String} autoCreate @hide
23325      */
23326     /**
23327      * @cfg {String} inputType @hide
23328      */
23329     /**
23330      * @cfg {String} invalidClass @hide
23331      */
23332     /**
23333      * @cfg {String} invalidText @hide
23334      */
23335     /**
23336      * @cfg {String} msgFx @hide
23337      */
23338     /**
23339      * @cfg {String} validateOnBlur @hide
23340      */
23341 });
23342
23343 Roo.HtmlEditorCore.white = [
23344         'area', 'br', 'img', 'input', 'hr', 'wbr',
23345         
23346        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23347        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23348        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23349        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23350        'table',   'ul',         'xmp', 
23351        
23352        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23353       'thead',   'tr', 
23354      
23355       'dir', 'menu', 'ol', 'ul', 'dl',
23356        
23357       'embed',  'object'
23358 ];
23359
23360
23361 Roo.HtmlEditorCore.black = [
23362     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23363         'applet', // 
23364         'base',   'basefont', 'bgsound', 'blink',  'body', 
23365         'frame',  'frameset', 'head',    'html',   'ilayer', 
23366         'iframe', 'layer',  'link',     'meta',    'object',   
23367         'script', 'style' ,'title',  'xml' // clean later..
23368 ];
23369 Roo.HtmlEditorCore.clean = [
23370     'script', 'style', 'title', 'xml'
23371 ];
23372 Roo.HtmlEditorCore.remove = [
23373     'font'
23374 ];
23375 // attributes..
23376
23377 Roo.HtmlEditorCore.ablack = [
23378     'on'
23379 ];
23380     
23381 Roo.HtmlEditorCore.aclean = [ 
23382     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23383 ];
23384
23385 // protocols..
23386 Roo.HtmlEditorCore.pwhite= [
23387         'http',  'https',  'mailto'
23388 ];
23389
23390 // white listed style attributes.
23391 Roo.HtmlEditorCore.cwhite= [
23392       //  'text-align', /// default is to allow most things..
23393       
23394          
23395 //        'font-size'//??
23396 ];
23397
23398 // black listed style attributes.
23399 Roo.HtmlEditorCore.cblack= [
23400       //  'font-size' -- this can be set by the project 
23401 ];
23402
23403
23404 Roo.HtmlEditorCore.swapCodes   =[ 
23405     [    8211, "--" ], 
23406     [    8212, "--" ], 
23407     [    8216,  "'" ],  
23408     [    8217, "'" ],  
23409     [    8220, '"' ],  
23410     [    8221, '"' ],  
23411     [    8226, "*" ],  
23412     [    8230, "..." ]
23413 ]; 
23414
23415     /*
23416  * - LGPL
23417  *
23418  * HtmlEditor
23419  * 
23420  */
23421
23422 /**
23423  * @class Roo.bootstrap.HtmlEditor
23424  * @extends Roo.bootstrap.TextArea
23425  * Bootstrap HtmlEditor class
23426
23427  * @constructor
23428  * Create a new HtmlEditor
23429  * @param {Object} config The config object
23430  */
23431
23432 Roo.bootstrap.HtmlEditor = function(config){
23433     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23434     if (!this.toolbars) {
23435         this.toolbars = [];
23436     }
23437     
23438     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23439     this.addEvents({
23440             /**
23441              * @event initialize
23442              * Fires when the editor is fully initialized (including the iframe)
23443              * @param {HtmlEditor} this
23444              */
23445             initialize: true,
23446             /**
23447              * @event activate
23448              * Fires when the editor is first receives the focus. Any insertion must wait
23449              * until after this event.
23450              * @param {HtmlEditor} this
23451              */
23452             activate: true,
23453              /**
23454              * @event beforesync
23455              * Fires before the textarea is updated with content from the editor iframe. Return false
23456              * to cancel the sync.
23457              * @param {HtmlEditor} this
23458              * @param {String} html
23459              */
23460             beforesync: true,
23461              /**
23462              * @event beforepush
23463              * Fires before the iframe editor is updated with content from the textarea. Return false
23464              * to cancel the push.
23465              * @param {HtmlEditor} this
23466              * @param {String} html
23467              */
23468             beforepush: true,
23469              /**
23470              * @event sync
23471              * Fires when the textarea is updated with content from the editor iframe.
23472              * @param {HtmlEditor} this
23473              * @param {String} html
23474              */
23475             sync: true,
23476              /**
23477              * @event push
23478              * Fires when the iframe editor is updated with content from the textarea.
23479              * @param {HtmlEditor} this
23480              * @param {String} html
23481              */
23482             push: true,
23483              /**
23484              * @event editmodechange
23485              * Fires when the editor switches edit modes
23486              * @param {HtmlEditor} this
23487              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23488              */
23489             editmodechange: true,
23490             /**
23491              * @event editorevent
23492              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23493              * @param {HtmlEditor} this
23494              */
23495             editorevent: true,
23496             /**
23497              * @event firstfocus
23498              * Fires when on first focus - needed by toolbars..
23499              * @param {HtmlEditor} this
23500              */
23501             firstfocus: true,
23502             /**
23503              * @event autosave
23504              * Auto save the htmlEditor value as a file into Events
23505              * @param {HtmlEditor} this
23506              */
23507             autosave: true,
23508             /**
23509              * @event savedpreview
23510              * preview the saved version of htmlEditor
23511              * @param {HtmlEditor} this
23512              */
23513             savedpreview: true
23514         });
23515 };
23516
23517
23518 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23519     
23520     
23521       /**
23522      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23523      */
23524     toolbars : false,
23525     
23526      /**
23527     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23528     */
23529     btns : [],
23530    
23531      /**
23532      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23533      *                        Roo.resizable.
23534      */
23535     resizable : false,
23536      /**
23537      * @cfg {Number} height (in pixels)
23538      */   
23539     height: 300,
23540    /**
23541      * @cfg {Number} width (in pixels)
23542      */   
23543     width: false,
23544     
23545     /**
23546      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23547      * 
23548      */
23549     stylesheets: false,
23550     
23551     // id of frame..
23552     frameId: false,
23553     
23554     // private properties
23555     validationEvent : false,
23556     deferHeight: true,
23557     initialized : false,
23558     activated : false,
23559     
23560     onFocus : Roo.emptyFn,
23561     iframePad:3,
23562     hideMode:'offsets',
23563     
23564     tbContainer : false,
23565     
23566     bodyCls : '',
23567     
23568     toolbarContainer :function() {
23569         return this.wrap.select('.x-html-editor-tb',true).first();
23570     },
23571
23572     /**
23573      * Protected method that will not generally be called directly. It
23574      * is called when the editor creates its toolbar. Override this method if you need to
23575      * add custom toolbar buttons.
23576      * @param {HtmlEditor} editor
23577      */
23578     createToolbar : function(){
23579         Roo.log('renewing');
23580         Roo.log("create toolbars");
23581         
23582         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23583         this.toolbars[0].render(this.toolbarContainer());
23584         
23585         return;
23586         
23587 //        if (!editor.toolbars || !editor.toolbars.length) {
23588 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23589 //        }
23590 //        
23591 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23592 //            editor.toolbars[i] = Roo.factory(
23593 //                    typeof(editor.toolbars[i]) == 'string' ?
23594 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23595 //                Roo.bootstrap.HtmlEditor);
23596 //            editor.toolbars[i].init(editor);
23597 //        }
23598     },
23599
23600      
23601     // private
23602     onRender : function(ct, position)
23603     {
23604        // Roo.log("Call onRender: " + this.xtype);
23605         var _t = this;
23606         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23607       
23608         this.wrap = this.inputEl().wrap({
23609             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23610         });
23611         
23612         this.editorcore.onRender(ct, position);
23613          
23614         if (this.resizable) {
23615             this.resizeEl = new Roo.Resizable(this.wrap, {
23616                 pinned : true,
23617                 wrap: true,
23618                 dynamic : true,
23619                 minHeight : this.height,
23620                 height: this.height,
23621                 handles : this.resizable,
23622                 width: this.width,
23623                 listeners : {
23624                     resize : function(r, w, h) {
23625                         _t.onResize(w,h); // -something
23626                     }
23627                 }
23628             });
23629             
23630         }
23631         this.createToolbar(this);
23632        
23633         
23634         if(!this.width && this.resizable){
23635             this.setSize(this.wrap.getSize());
23636         }
23637         if (this.resizeEl) {
23638             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23639             // should trigger onReize..
23640         }
23641         
23642     },
23643
23644     // private
23645     onResize : function(w, h)
23646     {
23647         Roo.log('resize: ' +w + ',' + h );
23648         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23649         var ew = false;
23650         var eh = false;
23651         
23652         if(this.inputEl() ){
23653             if(typeof w == 'number'){
23654                 var aw = w - this.wrap.getFrameWidth('lr');
23655                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23656                 ew = aw;
23657             }
23658             if(typeof h == 'number'){
23659                  var tbh = -11;  // fixme it needs to tool bar size!
23660                 for (var i =0; i < this.toolbars.length;i++) {
23661                     // fixme - ask toolbars for heights?
23662                     tbh += this.toolbars[i].el.getHeight();
23663                     //if (this.toolbars[i].footer) {
23664                     //    tbh += this.toolbars[i].footer.el.getHeight();
23665                     //}
23666                 }
23667               
23668                 
23669                 
23670                 
23671                 
23672                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23673                 ah -= 5; // knock a few pixes off for look..
23674                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23675                 var eh = ah;
23676             }
23677         }
23678         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23679         this.editorcore.onResize(ew,eh);
23680         
23681     },
23682
23683     /**
23684      * Toggles the editor between standard and source edit mode.
23685      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23686      */
23687     toggleSourceEdit : function(sourceEditMode)
23688     {
23689         this.editorcore.toggleSourceEdit(sourceEditMode);
23690         
23691         if(this.editorcore.sourceEditMode){
23692             Roo.log('editor - showing textarea');
23693             
23694 //            Roo.log('in');
23695 //            Roo.log(this.syncValue());
23696             this.syncValue();
23697             this.inputEl().removeClass(['hide', 'x-hidden']);
23698             this.inputEl().dom.removeAttribute('tabIndex');
23699             this.inputEl().focus();
23700         }else{
23701             Roo.log('editor - hiding textarea');
23702 //            Roo.log('out')
23703 //            Roo.log(this.pushValue()); 
23704             this.pushValue();
23705             
23706             this.inputEl().addClass(['hide', 'x-hidden']);
23707             this.inputEl().dom.setAttribute('tabIndex', -1);
23708             //this.deferFocus();
23709         }
23710          
23711         if(this.resizable){
23712             this.setSize(this.wrap.getSize());
23713         }
23714         
23715         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23716     },
23717  
23718     // private (for BoxComponent)
23719     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23720
23721     // private (for BoxComponent)
23722     getResizeEl : function(){
23723         return this.wrap;
23724     },
23725
23726     // private (for BoxComponent)
23727     getPositionEl : function(){
23728         return this.wrap;
23729     },
23730
23731     // private
23732     initEvents : function(){
23733         this.originalValue = this.getValue();
23734     },
23735
23736 //    /**
23737 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23738 //     * @method
23739 //     */
23740 //    markInvalid : Roo.emptyFn,
23741 //    /**
23742 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23743 //     * @method
23744 //     */
23745 //    clearInvalid : Roo.emptyFn,
23746
23747     setValue : function(v){
23748         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23749         this.editorcore.pushValue();
23750     },
23751
23752      
23753     // private
23754     deferFocus : function(){
23755         this.focus.defer(10, this);
23756     },
23757
23758     // doc'ed in Field
23759     focus : function(){
23760         this.editorcore.focus();
23761         
23762     },
23763       
23764
23765     // private
23766     onDestroy : function(){
23767         
23768         
23769         
23770         if(this.rendered){
23771             
23772             for (var i =0; i < this.toolbars.length;i++) {
23773                 // fixme - ask toolbars for heights?
23774                 this.toolbars[i].onDestroy();
23775             }
23776             
23777             this.wrap.dom.innerHTML = '';
23778             this.wrap.remove();
23779         }
23780     },
23781
23782     // private
23783     onFirstFocus : function(){
23784         //Roo.log("onFirstFocus");
23785         this.editorcore.onFirstFocus();
23786          for (var i =0; i < this.toolbars.length;i++) {
23787             this.toolbars[i].onFirstFocus();
23788         }
23789         
23790     },
23791     
23792     // private
23793     syncValue : function()
23794     {   
23795         this.editorcore.syncValue();
23796     },
23797     
23798     pushValue : function()
23799     {   
23800         this.editorcore.pushValue();
23801     }
23802      
23803     
23804     // hide stuff that is not compatible
23805     /**
23806      * @event blur
23807      * @hide
23808      */
23809     /**
23810      * @event change
23811      * @hide
23812      */
23813     /**
23814      * @event focus
23815      * @hide
23816      */
23817     /**
23818      * @event specialkey
23819      * @hide
23820      */
23821     /**
23822      * @cfg {String} fieldClass @hide
23823      */
23824     /**
23825      * @cfg {String} focusClass @hide
23826      */
23827     /**
23828      * @cfg {String} autoCreate @hide
23829      */
23830     /**
23831      * @cfg {String} inputType @hide
23832      */
23833     /**
23834      * @cfg {String} invalidClass @hide
23835      */
23836     /**
23837      * @cfg {String} invalidText @hide
23838      */
23839     /**
23840      * @cfg {String} msgFx @hide
23841      */
23842     /**
23843      * @cfg {String} validateOnBlur @hide
23844      */
23845 });
23846  
23847     
23848    
23849    
23850    
23851       
23852 Roo.namespace('Roo.bootstrap.htmleditor');
23853 /**
23854  * @class Roo.bootstrap.HtmlEditorToolbar1
23855  * Basic Toolbar
23856  * 
23857  * Usage:
23858  *
23859  new Roo.bootstrap.HtmlEditor({
23860     ....
23861     toolbars : [
23862         new Roo.bootstrap.HtmlEditorToolbar1({
23863             disable : { fonts: 1 , format: 1, ..., ... , ...],
23864             btns : [ .... ]
23865         })
23866     }
23867      
23868  * 
23869  * @cfg {Object} disable List of elements to disable..
23870  * @cfg {Array} btns List of additional buttons.
23871  * 
23872  * 
23873  * NEEDS Extra CSS? 
23874  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23875  */
23876  
23877 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23878 {
23879     
23880     Roo.apply(this, config);
23881     
23882     // default disabled, based on 'good practice'..
23883     this.disable = this.disable || {};
23884     Roo.applyIf(this.disable, {
23885         fontSize : true,
23886         colors : true,
23887         specialElements : true
23888     });
23889     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23890     
23891     this.editor = config.editor;
23892     this.editorcore = config.editor.editorcore;
23893     
23894     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23895     
23896     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23897     // dont call parent... till later.
23898 }
23899 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23900      
23901     bar : true,
23902     
23903     editor : false,
23904     editorcore : false,
23905     
23906     
23907     formats : [
23908         "p" ,  
23909         "h1","h2","h3","h4","h5","h6", 
23910         "pre", "code", 
23911         "abbr", "acronym", "address", "cite", "samp", "var",
23912         'div','span'
23913     ],
23914     
23915     onRender : function(ct, position)
23916     {
23917        // Roo.log("Call onRender: " + this.xtype);
23918         
23919        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23920        Roo.log(this.el);
23921        this.el.dom.style.marginBottom = '0';
23922        var _this = this;
23923        var editorcore = this.editorcore;
23924        var editor= this.editor;
23925        
23926        var children = [];
23927        var btn = function(id,cmd , toggle, handler, html){
23928        
23929             var  event = toggle ? 'toggle' : 'click';
23930        
23931             var a = {
23932                 size : 'sm',
23933                 xtype: 'Button',
23934                 xns: Roo.bootstrap,
23935                 glyphicon : id,
23936                 cmd : id || cmd,
23937                 enableToggle:toggle !== false,
23938                 html : html || '',
23939                 pressed : toggle ? false : null,
23940                 listeners : {}
23941             };
23942             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23943                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23944             };
23945             children.push(a);
23946             return a;
23947        }
23948        
23949     //    var cb_box = function...
23950         
23951         var style = {
23952                 xtype: 'Button',
23953                 size : 'sm',
23954                 xns: Roo.bootstrap,
23955                 glyphicon : 'font',
23956                 //html : 'submit'
23957                 menu : {
23958                     xtype: 'Menu',
23959                     xns: Roo.bootstrap,
23960                     items:  []
23961                 }
23962         };
23963         Roo.each(this.formats, function(f) {
23964             style.menu.items.push({
23965                 xtype :'MenuItem',
23966                 xns: Roo.bootstrap,
23967                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23968                 tagname : f,
23969                 listeners : {
23970                     click : function()
23971                     {
23972                         editorcore.insertTag(this.tagname);
23973                         editor.focus();
23974                     }
23975                 }
23976                 
23977             });
23978         });
23979         children.push(style);   
23980         
23981         btn('bold',false,true);
23982         btn('italic',false,true);
23983         btn('align-left', 'justifyleft',true);
23984         btn('align-center', 'justifycenter',true);
23985         btn('align-right' , 'justifyright',true);
23986         btn('link', false, false, function(btn) {
23987             //Roo.log("create link?");
23988             var url = prompt(this.createLinkText, this.defaultLinkValue);
23989             if(url && url != 'http:/'+'/'){
23990                 this.editorcore.relayCmd('createlink', url);
23991             }
23992         }),
23993         btn('list','insertunorderedlist',true);
23994         btn('pencil', false,true, function(btn){
23995                 Roo.log(this);
23996                 this.toggleSourceEdit(btn.pressed);
23997         });
23998         
23999         if (this.editor.btns.length > 0) {
24000             for (var i = 0; i<this.editor.btns.length; i++) {
24001                 children.push(this.editor.btns[i]);
24002             }
24003         }
24004         
24005         /*
24006         var cog = {
24007                 xtype: 'Button',
24008                 size : 'sm',
24009                 xns: Roo.bootstrap,
24010                 glyphicon : 'cog',
24011                 //html : 'submit'
24012                 menu : {
24013                     xtype: 'Menu',
24014                     xns: Roo.bootstrap,
24015                     items:  []
24016                 }
24017         };
24018         
24019         cog.menu.items.push({
24020             xtype :'MenuItem',
24021             xns: Roo.bootstrap,
24022             html : Clean styles,
24023             tagname : f,
24024             listeners : {
24025                 click : function()
24026                 {
24027                     editorcore.insertTag(this.tagname);
24028                     editor.focus();
24029                 }
24030             }
24031             
24032         });
24033        */
24034         
24035          
24036        this.xtype = 'NavSimplebar';
24037         
24038         for(var i=0;i< children.length;i++) {
24039             
24040             this.buttons.add(this.addxtypeChild(children[i]));
24041             
24042         }
24043         
24044         editor.on('editorevent', this.updateToolbar, this);
24045     },
24046     onBtnClick : function(id)
24047     {
24048        this.editorcore.relayCmd(id);
24049        this.editorcore.focus();
24050     },
24051     
24052     /**
24053      * Protected method that will not generally be called directly. It triggers
24054      * a toolbar update by reading the markup state of the current selection in the editor.
24055      */
24056     updateToolbar: function(){
24057
24058         if(!this.editorcore.activated){
24059             this.editor.onFirstFocus(); // is this neeed?
24060             return;
24061         }
24062
24063         var btns = this.buttons; 
24064         var doc = this.editorcore.doc;
24065         btns.get('bold').setActive(doc.queryCommandState('bold'));
24066         btns.get('italic').setActive(doc.queryCommandState('italic'));
24067         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24068         
24069         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24070         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24071         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24072         
24073         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24074         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24075          /*
24076         
24077         var ans = this.editorcore.getAllAncestors();
24078         if (this.formatCombo) {
24079             
24080             
24081             var store = this.formatCombo.store;
24082             this.formatCombo.setValue("");
24083             for (var i =0; i < ans.length;i++) {
24084                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24085                     // select it..
24086                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24087                     break;
24088                 }
24089             }
24090         }
24091         
24092         
24093         
24094         // hides menus... - so this cant be on a menu...
24095         Roo.bootstrap.MenuMgr.hideAll();
24096         */
24097         Roo.bootstrap.MenuMgr.hideAll();
24098         //this.editorsyncValue();
24099     },
24100     onFirstFocus: function() {
24101         this.buttons.each(function(item){
24102            item.enable();
24103         });
24104     },
24105     toggleSourceEdit : function(sourceEditMode){
24106         
24107           
24108         if(sourceEditMode){
24109             Roo.log("disabling buttons");
24110            this.buttons.each( function(item){
24111                 if(item.cmd != 'pencil'){
24112                     item.disable();
24113                 }
24114             });
24115           
24116         }else{
24117             Roo.log("enabling buttons");
24118             if(this.editorcore.initialized){
24119                 this.buttons.each( function(item){
24120                     item.enable();
24121                 });
24122             }
24123             
24124         }
24125         Roo.log("calling toggole on editor");
24126         // tell the editor that it's been pressed..
24127         this.editor.toggleSourceEdit(sourceEditMode);
24128        
24129     }
24130 });
24131
24132
24133
24134
24135
24136 /**
24137  * @class Roo.bootstrap.Table.AbstractSelectionModel
24138  * @extends Roo.util.Observable
24139  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24140  * implemented by descendant classes.  This class should not be directly instantiated.
24141  * @constructor
24142  */
24143 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24144     this.locked = false;
24145     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24146 };
24147
24148
24149 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24150     /** @ignore Called by the grid automatically. Do not call directly. */
24151     init : function(grid){
24152         this.grid = grid;
24153         this.initEvents();
24154     },
24155
24156     /**
24157      * Locks the selections.
24158      */
24159     lock : function(){
24160         this.locked = true;
24161     },
24162
24163     /**
24164      * Unlocks the selections.
24165      */
24166     unlock : function(){
24167         this.locked = false;
24168     },
24169
24170     /**
24171      * Returns true if the selections are locked.
24172      * @return {Boolean}
24173      */
24174     isLocked : function(){
24175         return this.locked;
24176     }
24177 });
24178 /**
24179  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24180  * @class Roo.bootstrap.Table.RowSelectionModel
24181  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24182  * It supports multiple selections and keyboard selection/navigation. 
24183  * @constructor
24184  * @param {Object} config
24185  */
24186
24187 Roo.bootstrap.Table.RowSelectionModel = function(config){
24188     Roo.apply(this, config);
24189     this.selections = new Roo.util.MixedCollection(false, function(o){
24190         return o.id;
24191     });
24192
24193     this.last = false;
24194     this.lastActive = false;
24195
24196     this.addEvents({
24197         /**
24198              * @event selectionchange
24199              * Fires when the selection changes
24200              * @param {SelectionModel} this
24201              */
24202             "selectionchange" : true,
24203         /**
24204              * @event afterselectionchange
24205              * Fires after the selection changes (eg. by key press or clicking)
24206              * @param {SelectionModel} this
24207              */
24208             "afterselectionchange" : true,
24209         /**
24210              * @event beforerowselect
24211              * Fires when a row is selected being selected, return false to cancel.
24212              * @param {SelectionModel} this
24213              * @param {Number} rowIndex The selected index
24214              * @param {Boolean} keepExisting False if other selections will be cleared
24215              */
24216             "beforerowselect" : true,
24217         /**
24218              * @event rowselect
24219              * Fires when a row is selected.
24220              * @param {SelectionModel} this
24221              * @param {Number} rowIndex The selected index
24222              * @param {Roo.data.Record} r The record
24223              */
24224             "rowselect" : true,
24225         /**
24226              * @event rowdeselect
24227              * Fires when a row is deselected.
24228              * @param {SelectionModel} this
24229              * @param {Number} rowIndex The selected index
24230              */
24231         "rowdeselect" : true
24232     });
24233     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24234     this.locked = false;
24235  };
24236
24237 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24238     /**
24239      * @cfg {Boolean} singleSelect
24240      * True to allow selection of only one row at a time (defaults to false)
24241      */
24242     singleSelect : false,
24243
24244     // private
24245     initEvents : function()
24246     {
24247
24248         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24249         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24250         //}else{ // allow click to work like normal
24251          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24252         //}
24253         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24254         this.grid.on("rowclick", this.handleMouseDown, this);
24255         
24256         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24257             "up" : function(e){
24258                 if(!e.shiftKey){
24259                     this.selectPrevious(e.shiftKey);
24260                 }else if(this.last !== false && this.lastActive !== false){
24261                     var last = this.last;
24262                     this.selectRange(this.last,  this.lastActive-1);
24263                     this.grid.getView().focusRow(this.lastActive);
24264                     if(last !== false){
24265                         this.last = last;
24266                     }
24267                 }else{
24268                     this.selectFirstRow();
24269                 }
24270                 this.fireEvent("afterselectionchange", this);
24271             },
24272             "down" : function(e){
24273                 if(!e.shiftKey){
24274                     this.selectNext(e.shiftKey);
24275                 }else if(this.last !== false && this.lastActive !== false){
24276                     var last = this.last;
24277                     this.selectRange(this.last,  this.lastActive+1);
24278                     this.grid.getView().focusRow(this.lastActive);
24279                     if(last !== false){
24280                         this.last = last;
24281                     }
24282                 }else{
24283                     this.selectFirstRow();
24284                 }
24285                 this.fireEvent("afterselectionchange", this);
24286             },
24287             scope: this
24288         });
24289         this.grid.store.on('load', function(){
24290             this.selections.clear();
24291         },this);
24292         /*
24293         var view = this.grid.view;
24294         view.on("refresh", this.onRefresh, this);
24295         view.on("rowupdated", this.onRowUpdated, this);
24296         view.on("rowremoved", this.onRemove, this);
24297         */
24298     },
24299
24300     // private
24301     onRefresh : function()
24302     {
24303         var ds = this.grid.store, i, v = this.grid.view;
24304         var s = this.selections;
24305         s.each(function(r){
24306             if((i = ds.indexOfId(r.id)) != -1){
24307                 v.onRowSelect(i);
24308             }else{
24309                 s.remove(r);
24310             }
24311         });
24312     },
24313
24314     // private
24315     onRemove : function(v, index, r){
24316         this.selections.remove(r);
24317     },
24318
24319     // private
24320     onRowUpdated : function(v, index, r){
24321         if(this.isSelected(r)){
24322             v.onRowSelect(index);
24323         }
24324     },
24325
24326     /**
24327      * Select records.
24328      * @param {Array} records The records to select
24329      * @param {Boolean} keepExisting (optional) True to keep existing selections
24330      */
24331     selectRecords : function(records, keepExisting)
24332     {
24333         if(!keepExisting){
24334             this.clearSelections();
24335         }
24336             var ds = this.grid.store;
24337         for(var i = 0, len = records.length; i < len; i++){
24338             this.selectRow(ds.indexOf(records[i]), true);
24339         }
24340     },
24341
24342     /**
24343      * Gets the number of selected rows.
24344      * @return {Number}
24345      */
24346     getCount : function(){
24347         return this.selections.length;
24348     },
24349
24350     /**
24351      * Selects the first row in the grid.
24352      */
24353     selectFirstRow : function(){
24354         this.selectRow(0);
24355     },
24356
24357     /**
24358      * Select the last row.
24359      * @param {Boolean} keepExisting (optional) True to keep existing selections
24360      */
24361     selectLastRow : function(keepExisting){
24362         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24363         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24364     },
24365
24366     /**
24367      * Selects the row immediately following the last selected row.
24368      * @param {Boolean} keepExisting (optional) True to keep existing selections
24369      */
24370     selectNext : function(keepExisting)
24371     {
24372             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24373             this.selectRow(this.last+1, keepExisting);
24374             this.grid.getView().focusRow(this.last);
24375         }
24376     },
24377
24378     /**
24379      * Selects the row that precedes the last selected row.
24380      * @param {Boolean} keepExisting (optional) True to keep existing selections
24381      */
24382     selectPrevious : function(keepExisting){
24383         if(this.last){
24384             this.selectRow(this.last-1, keepExisting);
24385             this.grid.getView().focusRow(this.last);
24386         }
24387     },
24388
24389     /**
24390      * Returns the selected records
24391      * @return {Array} Array of selected records
24392      */
24393     getSelections : function(){
24394         return [].concat(this.selections.items);
24395     },
24396
24397     /**
24398      * Returns the first selected record.
24399      * @return {Record}
24400      */
24401     getSelected : function(){
24402         return this.selections.itemAt(0);
24403     },
24404
24405
24406     /**
24407      * Clears all selections.
24408      */
24409     clearSelections : function(fast)
24410     {
24411         if(this.locked) {
24412             return;
24413         }
24414         if(fast !== true){
24415                 var ds = this.grid.store;
24416             var s = this.selections;
24417             s.each(function(r){
24418                 this.deselectRow(ds.indexOfId(r.id));
24419             }, this);
24420             s.clear();
24421         }else{
24422             this.selections.clear();
24423         }
24424         this.last = false;
24425     },
24426
24427
24428     /**
24429      * Selects all rows.
24430      */
24431     selectAll : function(){
24432         if(this.locked) {
24433             return;
24434         }
24435         this.selections.clear();
24436         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24437             this.selectRow(i, true);
24438         }
24439     },
24440
24441     /**
24442      * Returns True if there is a selection.
24443      * @return {Boolean}
24444      */
24445     hasSelection : function(){
24446         return this.selections.length > 0;
24447     },
24448
24449     /**
24450      * Returns True if the specified row is selected.
24451      * @param {Number/Record} record The record or index of the record to check
24452      * @return {Boolean}
24453      */
24454     isSelected : function(index){
24455             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24456         return (r && this.selections.key(r.id) ? true : false);
24457     },
24458
24459     /**
24460      * Returns True if the specified record id is selected.
24461      * @param {String} id The id of record to check
24462      * @return {Boolean}
24463      */
24464     isIdSelected : function(id){
24465         return (this.selections.key(id) ? true : false);
24466     },
24467
24468
24469     // private
24470     handleMouseDBClick : function(e, t){
24471         
24472     },
24473     // private
24474     handleMouseDown : function(e, t)
24475     {
24476             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24477         if(this.isLocked() || rowIndex < 0 ){
24478             return;
24479         };
24480         if(e.shiftKey && this.last !== false){
24481             var last = this.last;
24482             this.selectRange(last, rowIndex, e.ctrlKey);
24483             this.last = last; // reset the last
24484             t.focus();
24485     
24486         }else{
24487             var isSelected = this.isSelected(rowIndex);
24488             //Roo.log("select row:" + rowIndex);
24489             if(isSelected){
24490                 this.deselectRow(rowIndex);
24491             } else {
24492                         this.selectRow(rowIndex, true);
24493             }
24494     
24495             /*
24496                 if(e.button !== 0 && isSelected){
24497                 alert('rowIndex 2: ' + rowIndex);
24498                     view.focusRow(rowIndex);
24499                 }else if(e.ctrlKey && isSelected){
24500                     this.deselectRow(rowIndex);
24501                 }else if(!isSelected){
24502                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24503                     view.focusRow(rowIndex);
24504                 }
24505             */
24506         }
24507         this.fireEvent("afterselectionchange", this);
24508     },
24509     // private
24510     handleDragableRowClick :  function(grid, rowIndex, e) 
24511     {
24512         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24513             this.selectRow(rowIndex, false);
24514             grid.view.focusRow(rowIndex);
24515              this.fireEvent("afterselectionchange", this);
24516         }
24517     },
24518     
24519     /**
24520      * Selects multiple rows.
24521      * @param {Array} rows Array of the indexes of the row to select
24522      * @param {Boolean} keepExisting (optional) True to keep existing selections
24523      */
24524     selectRows : function(rows, keepExisting){
24525         if(!keepExisting){
24526             this.clearSelections();
24527         }
24528         for(var i = 0, len = rows.length; i < len; i++){
24529             this.selectRow(rows[i], true);
24530         }
24531     },
24532
24533     /**
24534      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24535      * @param {Number} startRow The index of the first row in the range
24536      * @param {Number} endRow The index of the last row in the range
24537      * @param {Boolean} keepExisting (optional) True to retain existing selections
24538      */
24539     selectRange : function(startRow, endRow, keepExisting){
24540         if(this.locked) {
24541             return;
24542         }
24543         if(!keepExisting){
24544             this.clearSelections();
24545         }
24546         if(startRow <= endRow){
24547             for(var i = startRow; i <= endRow; i++){
24548                 this.selectRow(i, true);
24549             }
24550         }else{
24551             for(var i = startRow; i >= endRow; i--){
24552                 this.selectRow(i, true);
24553             }
24554         }
24555     },
24556
24557     /**
24558      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24559      * @param {Number} startRow The index of the first row in the range
24560      * @param {Number} endRow The index of the last row in the range
24561      */
24562     deselectRange : function(startRow, endRow, preventViewNotify){
24563         if(this.locked) {
24564             return;
24565         }
24566         for(var i = startRow; i <= endRow; i++){
24567             this.deselectRow(i, preventViewNotify);
24568         }
24569     },
24570
24571     /**
24572      * Selects a row.
24573      * @param {Number} row The index of the row to select
24574      * @param {Boolean} keepExisting (optional) True to keep existing selections
24575      */
24576     selectRow : function(index, keepExisting, preventViewNotify)
24577     {
24578             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24579             return;
24580         }
24581         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24582             if(!keepExisting || this.singleSelect){
24583                 this.clearSelections();
24584             }
24585             
24586             var r = this.grid.store.getAt(index);
24587             //console.log('selectRow - record id :' + r.id);
24588             
24589             this.selections.add(r);
24590             this.last = this.lastActive = index;
24591             if(!preventViewNotify){
24592                 var proxy = new Roo.Element(
24593                                 this.grid.getRowDom(index)
24594                 );
24595                 proxy.addClass('bg-info info');
24596             }
24597             this.fireEvent("rowselect", this, index, r);
24598             this.fireEvent("selectionchange", this);
24599         }
24600     },
24601
24602     /**
24603      * Deselects a row.
24604      * @param {Number} row The index of the row to deselect
24605      */
24606     deselectRow : function(index, preventViewNotify)
24607     {
24608         if(this.locked) {
24609             return;
24610         }
24611         if(this.last == index){
24612             this.last = false;
24613         }
24614         if(this.lastActive == index){
24615             this.lastActive = false;
24616         }
24617         
24618         var r = this.grid.store.getAt(index);
24619         if (!r) {
24620             return;
24621         }
24622         
24623         this.selections.remove(r);
24624         //.console.log('deselectRow - record id :' + r.id);
24625         if(!preventViewNotify){
24626         
24627             var proxy = new Roo.Element(
24628                 this.grid.getRowDom(index)
24629             );
24630             proxy.removeClass('bg-info info');
24631         }
24632         this.fireEvent("rowdeselect", this, index);
24633         this.fireEvent("selectionchange", this);
24634     },
24635
24636     // private
24637     restoreLast : function(){
24638         if(this._last){
24639             this.last = this._last;
24640         }
24641     },
24642
24643     // private
24644     acceptsNav : function(row, col, cm){
24645         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24646     },
24647
24648     // private
24649     onEditorKey : function(field, e){
24650         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24651         if(k == e.TAB){
24652             e.stopEvent();
24653             ed.completeEdit();
24654             if(e.shiftKey){
24655                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24656             }else{
24657                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24658             }
24659         }else if(k == e.ENTER && !e.ctrlKey){
24660             e.stopEvent();
24661             ed.completeEdit();
24662             if(e.shiftKey){
24663                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24664             }else{
24665                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24666             }
24667         }else if(k == e.ESC){
24668             ed.cancelEdit();
24669         }
24670         if(newCell){
24671             g.startEditing(newCell[0], newCell[1]);
24672         }
24673     }
24674 });
24675 /*
24676  * Based on:
24677  * Ext JS Library 1.1.1
24678  * Copyright(c) 2006-2007, Ext JS, LLC.
24679  *
24680  * Originally Released Under LGPL - original licence link has changed is not relivant.
24681  *
24682  * Fork - LGPL
24683  * <script type="text/javascript">
24684  */
24685  
24686 /**
24687  * @class Roo.bootstrap.PagingToolbar
24688  * @extends Roo.bootstrap.NavSimplebar
24689  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24690  * @constructor
24691  * Create a new PagingToolbar
24692  * @param {Object} config The config object
24693  * @param {Roo.data.Store} store
24694  */
24695 Roo.bootstrap.PagingToolbar = function(config)
24696 {
24697     // old args format still supported... - xtype is prefered..
24698         // created from xtype...
24699     
24700     this.ds = config.dataSource;
24701     
24702     if (config.store && !this.ds) {
24703         this.store= Roo.factory(config.store, Roo.data);
24704         this.ds = this.store;
24705         this.ds.xmodule = this.xmodule || false;
24706     }
24707     
24708     this.toolbarItems = [];
24709     if (config.items) {
24710         this.toolbarItems = config.items;
24711     }
24712     
24713     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24714     
24715     this.cursor = 0;
24716     
24717     if (this.ds) { 
24718         this.bind(this.ds);
24719     }
24720     
24721     if (Roo.bootstrap.version == 4) {
24722         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24723     } else {
24724         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24725     }
24726     
24727 };
24728
24729 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24730     /**
24731      * @cfg {Roo.data.Store} dataSource
24732      * The underlying data store providing the paged data
24733      */
24734     /**
24735      * @cfg {String/HTMLElement/Element} container
24736      * container The id or element that will contain the toolbar
24737      */
24738     /**
24739      * @cfg {Boolean} displayInfo
24740      * True to display the displayMsg (defaults to false)
24741      */
24742     /**
24743      * @cfg {Number} pageSize
24744      * The number of records to display per page (defaults to 20)
24745      */
24746     pageSize: 20,
24747     /**
24748      * @cfg {String} displayMsg
24749      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24750      */
24751     displayMsg : 'Displaying {0} - {1} of {2}',
24752     /**
24753      * @cfg {String} emptyMsg
24754      * The message to display when no records are found (defaults to "No data to display")
24755      */
24756     emptyMsg : 'No data to display',
24757     /**
24758      * Customizable piece of the default paging text (defaults to "Page")
24759      * @type String
24760      */
24761     beforePageText : "Page",
24762     /**
24763      * Customizable piece of the default paging text (defaults to "of %0")
24764      * @type String
24765      */
24766     afterPageText : "of {0}",
24767     /**
24768      * Customizable piece of the default paging text (defaults to "First Page")
24769      * @type String
24770      */
24771     firstText : "First Page",
24772     /**
24773      * Customizable piece of the default paging text (defaults to "Previous Page")
24774      * @type String
24775      */
24776     prevText : "Previous Page",
24777     /**
24778      * Customizable piece of the default paging text (defaults to "Next Page")
24779      * @type String
24780      */
24781     nextText : "Next Page",
24782     /**
24783      * Customizable piece of the default paging text (defaults to "Last Page")
24784      * @type String
24785      */
24786     lastText : "Last Page",
24787     /**
24788      * Customizable piece of the default paging text (defaults to "Refresh")
24789      * @type String
24790      */
24791     refreshText : "Refresh",
24792
24793     buttons : false,
24794     // private
24795     onRender : function(ct, position) 
24796     {
24797         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24798         this.navgroup.parentId = this.id;
24799         this.navgroup.onRender(this.el, null);
24800         // add the buttons to the navgroup
24801         
24802         if(this.displayInfo){
24803             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24804             this.displayEl = this.el.select('.x-paging-info', true).first();
24805 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24806 //            this.displayEl = navel.el.select('span',true).first();
24807         }
24808         
24809         var _this = this;
24810         
24811         if(this.buttons){
24812             Roo.each(_this.buttons, function(e){ // this might need to use render????
24813                Roo.factory(e).render(_this.el);
24814             });
24815         }
24816             
24817         Roo.each(_this.toolbarItems, function(e) {
24818             _this.navgroup.addItem(e);
24819         });
24820         
24821         
24822         this.first = this.navgroup.addItem({
24823             tooltip: this.firstText,
24824             cls: "prev btn-outline-secondary",
24825             html : ' <i class="fa fa-step-backward"></i>',
24826             disabled: true,
24827             preventDefault: true,
24828             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24829         });
24830         
24831         this.prev =  this.navgroup.addItem({
24832             tooltip: this.prevText,
24833             cls: "prev btn-outline-secondary",
24834             html : ' <i class="fa fa-backward"></i>',
24835             disabled: true,
24836             preventDefault: true,
24837             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24838         });
24839     //this.addSeparator();
24840         
24841         
24842         var field = this.navgroup.addItem( {
24843             tagtype : 'span',
24844             cls : 'x-paging-position  btn-outline-secondary',
24845              disabled: true,
24846             html : this.beforePageText  +
24847                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24848                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24849          } ); //?? escaped?
24850         
24851         this.field = field.el.select('input', true).first();
24852         this.field.on("keydown", this.onPagingKeydown, this);
24853         this.field.on("focus", function(){this.dom.select();});
24854     
24855     
24856         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24857         //this.field.setHeight(18);
24858         //this.addSeparator();
24859         this.next = this.navgroup.addItem({
24860             tooltip: this.nextText,
24861             cls: "next btn-outline-secondary",
24862             html : ' <i class="fa fa-forward"></i>',
24863             disabled: true,
24864             preventDefault: true,
24865             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24866         });
24867         this.last = this.navgroup.addItem({
24868             tooltip: this.lastText,
24869             html : ' <i class="fa fa-step-forward"></i>',
24870             cls: "next btn-outline-secondary",
24871             disabled: true,
24872             preventDefault: true,
24873             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24874         });
24875     //this.addSeparator();
24876         this.loading = this.navgroup.addItem({
24877             tooltip: this.refreshText,
24878             cls: "btn-outline-secondary",
24879             html : ' <i class="fa fa-refresh"></i>',
24880             preventDefault: true,
24881             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24882         });
24883         
24884     },
24885
24886     // private
24887     updateInfo : function(){
24888         if(this.displayEl){
24889             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24890             var msg = count == 0 ?
24891                 this.emptyMsg :
24892                 String.format(
24893                     this.displayMsg,
24894                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24895                 );
24896             this.displayEl.update(msg);
24897         }
24898     },
24899
24900     // private
24901     onLoad : function(ds, r, o)
24902     {
24903         this.cursor = o.params.start ? o.params.start : 0;
24904         
24905         var d = this.getPageData(),
24906             ap = d.activePage,
24907             ps = d.pages;
24908         
24909         
24910         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24911         this.field.dom.value = ap;
24912         this.first.setDisabled(ap == 1);
24913         this.prev.setDisabled(ap == 1);
24914         this.next.setDisabled(ap == ps);
24915         this.last.setDisabled(ap == ps);
24916         this.loading.enable();
24917         this.updateInfo();
24918     },
24919
24920     // private
24921     getPageData : function(){
24922         var total = this.ds.getTotalCount();
24923         return {
24924             total : total,
24925             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24926             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24927         };
24928     },
24929
24930     // private
24931     onLoadError : function(){
24932         this.loading.enable();
24933     },
24934
24935     // private
24936     onPagingKeydown : function(e){
24937         var k = e.getKey();
24938         var d = this.getPageData();
24939         if(k == e.RETURN){
24940             var v = this.field.dom.value, pageNum;
24941             if(!v || isNaN(pageNum = parseInt(v, 10))){
24942                 this.field.dom.value = d.activePage;
24943                 return;
24944             }
24945             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24946             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24947             e.stopEvent();
24948         }
24949         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))
24950         {
24951           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24952           this.field.dom.value = pageNum;
24953           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24954           e.stopEvent();
24955         }
24956         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24957         {
24958           var v = this.field.dom.value, pageNum; 
24959           var increment = (e.shiftKey) ? 10 : 1;
24960           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24961                 increment *= -1;
24962           }
24963           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24964             this.field.dom.value = d.activePage;
24965             return;
24966           }
24967           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24968           {
24969             this.field.dom.value = parseInt(v, 10) + increment;
24970             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24971             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24972           }
24973           e.stopEvent();
24974         }
24975     },
24976
24977     // private
24978     beforeLoad : function(){
24979         if(this.loading){
24980             this.loading.disable();
24981         }
24982     },
24983
24984     // private
24985     onClick : function(which){
24986         
24987         var ds = this.ds;
24988         if (!ds) {
24989             return;
24990         }
24991         
24992         switch(which){
24993             case "first":
24994                 ds.load({params:{start: 0, limit: this.pageSize}});
24995             break;
24996             case "prev":
24997                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24998             break;
24999             case "next":
25000                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25001             break;
25002             case "last":
25003                 var total = ds.getTotalCount();
25004                 var extra = total % this.pageSize;
25005                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25006                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25007             break;
25008             case "refresh":
25009                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25010             break;
25011         }
25012     },
25013
25014     /**
25015      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25016      * @param {Roo.data.Store} store The data store to unbind
25017      */
25018     unbind : function(ds){
25019         ds.un("beforeload", this.beforeLoad, this);
25020         ds.un("load", this.onLoad, this);
25021         ds.un("loadexception", this.onLoadError, this);
25022         ds.un("remove", this.updateInfo, this);
25023         ds.un("add", this.updateInfo, this);
25024         this.ds = undefined;
25025     },
25026
25027     /**
25028      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25029      * @param {Roo.data.Store} store The data store to bind
25030      */
25031     bind : function(ds){
25032         ds.on("beforeload", this.beforeLoad, this);
25033         ds.on("load", this.onLoad, this);
25034         ds.on("loadexception", this.onLoadError, this);
25035         ds.on("remove", this.updateInfo, this);
25036         ds.on("add", this.updateInfo, this);
25037         this.ds = ds;
25038     }
25039 });/*
25040  * - LGPL
25041  *
25042  * element
25043  * 
25044  */
25045
25046 /**
25047  * @class Roo.bootstrap.MessageBar
25048  * @extends Roo.bootstrap.Component
25049  * Bootstrap MessageBar class
25050  * @cfg {String} html contents of the MessageBar
25051  * @cfg {String} weight (info | success | warning | danger) default info
25052  * @cfg {String} beforeClass insert the bar before the given class
25053  * @cfg {Boolean} closable (true | false) default false
25054  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25055  * 
25056  * @constructor
25057  * Create a new Element
25058  * @param {Object} config The config object
25059  */
25060
25061 Roo.bootstrap.MessageBar = function(config){
25062     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25063 };
25064
25065 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25066     
25067     html: '',
25068     weight: 'info',
25069     closable: false,
25070     fixed: false,
25071     beforeClass: 'bootstrap-sticky-wrap',
25072     
25073     getAutoCreate : function(){
25074         
25075         var cfg = {
25076             tag: 'div',
25077             cls: 'alert alert-dismissable alert-' + this.weight,
25078             cn: [
25079                 {
25080                     tag: 'span',
25081                     cls: 'message',
25082                     html: this.html || ''
25083                 }
25084             ]
25085         };
25086         
25087         if(this.fixed){
25088             cfg.cls += ' alert-messages-fixed';
25089         }
25090         
25091         if(this.closable){
25092             cfg.cn.push({
25093                 tag: 'button',
25094                 cls: 'close',
25095                 html: 'x'
25096             });
25097         }
25098         
25099         return cfg;
25100     },
25101     
25102     onRender : function(ct, position)
25103     {
25104         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25105         
25106         if(!this.el){
25107             var cfg = Roo.apply({},  this.getAutoCreate());
25108             cfg.id = Roo.id();
25109             
25110             if (this.cls) {
25111                 cfg.cls += ' ' + this.cls;
25112             }
25113             if (this.style) {
25114                 cfg.style = this.style;
25115             }
25116             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25117             
25118             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25119         }
25120         
25121         this.el.select('>button.close').on('click', this.hide, this);
25122         
25123     },
25124     
25125     show : function()
25126     {
25127         if (!this.rendered) {
25128             this.render();
25129         }
25130         
25131         this.el.show();
25132         
25133         this.fireEvent('show', this);
25134         
25135     },
25136     
25137     hide : function()
25138     {
25139         if (!this.rendered) {
25140             this.render();
25141         }
25142         
25143         this.el.hide();
25144         
25145         this.fireEvent('hide', this);
25146     },
25147     
25148     update : function()
25149     {
25150 //        var e = this.el.dom.firstChild;
25151 //        
25152 //        if(this.closable){
25153 //            e = e.nextSibling;
25154 //        }
25155 //        
25156 //        e.data = this.html || '';
25157
25158         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25159     }
25160    
25161 });
25162
25163  
25164
25165      /*
25166  * - LGPL
25167  *
25168  * Graph
25169  * 
25170  */
25171
25172
25173 /**
25174  * @class Roo.bootstrap.Graph
25175  * @extends Roo.bootstrap.Component
25176  * Bootstrap Graph class
25177 > Prameters
25178  -sm {number} sm 4
25179  -md {number} md 5
25180  @cfg {String} graphtype  bar | vbar | pie
25181  @cfg {number} g_x coodinator | centre x (pie)
25182  @cfg {number} g_y coodinator | centre y (pie)
25183  @cfg {number} g_r radius (pie)
25184  @cfg {number} g_height height of the chart (respected by all elements in the set)
25185  @cfg {number} g_width width of the chart (respected by all elements in the set)
25186  @cfg {Object} title The title of the chart
25187     
25188  -{Array}  values
25189  -opts (object) options for the chart 
25190      o {
25191      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25192      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25193      o vgutter (number)
25194      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.
25195      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25196      o to
25197      o stretch (boolean)
25198      o }
25199  -opts (object) options for the pie
25200      o{
25201      o cut
25202      o startAngle (number)
25203      o endAngle (number)
25204      } 
25205  *
25206  * @constructor
25207  * Create a new Input
25208  * @param {Object} config The config object
25209  */
25210
25211 Roo.bootstrap.Graph = function(config){
25212     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25213     
25214     this.addEvents({
25215         // img events
25216         /**
25217          * @event click
25218          * The img click event for the img.
25219          * @param {Roo.EventObject} e
25220          */
25221         "click" : true
25222     });
25223 };
25224
25225 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25226     
25227     sm: 4,
25228     md: 5,
25229     graphtype: 'bar',
25230     g_height: 250,
25231     g_width: 400,
25232     g_x: 50,
25233     g_y: 50,
25234     g_r: 30,
25235     opts:{
25236         //g_colors: this.colors,
25237         g_type: 'soft',
25238         g_gutter: '20%'
25239
25240     },
25241     title : false,
25242
25243     getAutoCreate : function(){
25244         
25245         var cfg = {
25246             tag: 'div',
25247             html : null
25248         };
25249         
25250         
25251         return  cfg;
25252     },
25253
25254     onRender : function(ct,position){
25255         
25256         
25257         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25258         
25259         if (typeof(Raphael) == 'undefined') {
25260             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25261             return;
25262         }
25263         
25264         this.raphael = Raphael(this.el.dom);
25265         
25266                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25267                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25268                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25269                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25270                 /*
25271                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25272                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25273                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25274                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25275                 
25276                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25277                 r.barchart(330, 10, 300, 220, data1);
25278                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25279                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25280                 */
25281                 
25282                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25283                 // r.barchart(30, 30, 560, 250,  xdata, {
25284                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25285                 //     axis : "0 0 1 1",
25286                 //     axisxlabels :  xdata
25287                 //     //yvalues : cols,
25288                    
25289                 // });
25290 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25291 //        
25292 //        this.load(null,xdata,{
25293 //                axis : "0 0 1 1",
25294 //                axisxlabels :  xdata
25295 //                });
25296
25297     },
25298
25299     load : function(graphtype,xdata,opts)
25300     {
25301         this.raphael.clear();
25302         if(!graphtype) {
25303             graphtype = this.graphtype;
25304         }
25305         if(!opts){
25306             opts = this.opts;
25307         }
25308         var r = this.raphael,
25309             fin = function () {
25310                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25311             },
25312             fout = function () {
25313                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25314             },
25315             pfin = function() {
25316                 this.sector.stop();
25317                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25318
25319                 if (this.label) {
25320                     this.label[0].stop();
25321                     this.label[0].attr({ r: 7.5 });
25322                     this.label[1].attr({ "font-weight": 800 });
25323                 }
25324             },
25325             pfout = function() {
25326                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25327
25328                 if (this.label) {
25329                     this.label[0].animate({ r: 5 }, 500, "bounce");
25330                     this.label[1].attr({ "font-weight": 400 });
25331                 }
25332             };
25333
25334         switch(graphtype){
25335             case 'bar':
25336                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25337                 break;
25338             case 'hbar':
25339                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25340                 break;
25341             case 'pie':
25342 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25343 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25344 //            
25345                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25346                 
25347                 break;
25348
25349         }
25350         
25351         if(this.title){
25352             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25353         }
25354         
25355     },
25356     
25357     setTitle: function(o)
25358     {
25359         this.title = o;
25360     },
25361     
25362     initEvents: function() {
25363         
25364         if(!this.href){
25365             this.el.on('click', this.onClick, this);
25366         }
25367     },
25368     
25369     onClick : function(e)
25370     {
25371         Roo.log('img onclick');
25372         this.fireEvent('click', this, e);
25373     }
25374    
25375 });
25376
25377  
25378 /*
25379  * - LGPL
25380  *
25381  * numberBox
25382  * 
25383  */
25384 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25385
25386 /**
25387  * @class Roo.bootstrap.dash.NumberBox
25388  * @extends Roo.bootstrap.Component
25389  * Bootstrap NumberBox class
25390  * @cfg {String} headline Box headline
25391  * @cfg {String} content Box content
25392  * @cfg {String} icon Box icon
25393  * @cfg {String} footer Footer text
25394  * @cfg {String} fhref Footer href
25395  * 
25396  * @constructor
25397  * Create a new NumberBox
25398  * @param {Object} config The config object
25399  */
25400
25401
25402 Roo.bootstrap.dash.NumberBox = function(config){
25403     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25404     
25405 };
25406
25407 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25408     
25409     headline : '',
25410     content : '',
25411     icon : '',
25412     footer : '',
25413     fhref : '',
25414     ficon : '',
25415     
25416     getAutoCreate : function(){
25417         
25418         var cfg = {
25419             tag : 'div',
25420             cls : 'small-box ',
25421             cn : [
25422                 {
25423                     tag : 'div',
25424                     cls : 'inner',
25425                     cn :[
25426                         {
25427                             tag : 'h3',
25428                             cls : 'roo-headline',
25429                             html : this.headline
25430                         },
25431                         {
25432                             tag : 'p',
25433                             cls : 'roo-content',
25434                             html : this.content
25435                         }
25436                     ]
25437                 }
25438             ]
25439         };
25440         
25441         if(this.icon){
25442             cfg.cn.push({
25443                 tag : 'div',
25444                 cls : 'icon',
25445                 cn :[
25446                     {
25447                         tag : 'i',
25448                         cls : 'ion ' + this.icon
25449                     }
25450                 ]
25451             });
25452         }
25453         
25454         if(this.footer){
25455             var footer = {
25456                 tag : 'a',
25457                 cls : 'small-box-footer',
25458                 href : this.fhref || '#',
25459                 html : this.footer
25460             };
25461             
25462             cfg.cn.push(footer);
25463             
25464         }
25465         
25466         return  cfg;
25467     },
25468
25469     onRender : function(ct,position){
25470         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25471
25472
25473        
25474                 
25475     },
25476
25477     setHeadline: function (value)
25478     {
25479         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25480     },
25481     
25482     setFooter: function (value, href)
25483     {
25484         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25485         
25486         if(href){
25487             this.el.select('a.small-box-footer',true).first().attr('href', href);
25488         }
25489         
25490     },
25491
25492     setContent: function (value)
25493     {
25494         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25495     },
25496
25497     initEvents: function() 
25498     {   
25499         
25500     }
25501     
25502 });
25503
25504  
25505 /*
25506  * - LGPL
25507  *
25508  * TabBox
25509  * 
25510  */
25511 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25512
25513 /**
25514  * @class Roo.bootstrap.dash.TabBox
25515  * @extends Roo.bootstrap.Component
25516  * Bootstrap TabBox class
25517  * @cfg {String} title Title of the TabBox
25518  * @cfg {String} icon Icon of the TabBox
25519  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25520  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25521  * 
25522  * @constructor
25523  * Create a new TabBox
25524  * @param {Object} config The config object
25525  */
25526
25527
25528 Roo.bootstrap.dash.TabBox = function(config){
25529     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25530     this.addEvents({
25531         // raw events
25532         /**
25533          * @event addpane
25534          * When a pane is added
25535          * @param {Roo.bootstrap.dash.TabPane} pane
25536          */
25537         "addpane" : true,
25538         /**
25539          * @event activatepane
25540          * When a pane is activated
25541          * @param {Roo.bootstrap.dash.TabPane} pane
25542          */
25543         "activatepane" : true
25544         
25545          
25546     });
25547     
25548     this.panes = [];
25549 };
25550
25551 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25552
25553     title : '',
25554     icon : false,
25555     showtabs : true,
25556     tabScrollable : false,
25557     
25558     getChildContainer : function()
25559     {
25560         return this.el.select('.tab-content', true).first();
25561     },
25562     
25563     getAutoCreate : function(){
25564         
25565         var header = {
25566             tag: 'li',
25567             cls: 'pull-left header',
25568             html: this.title,
25569             cn : []
25570         };
25571         
25572         if(this.icon){
25573             header.cn.push({
25574                 tag: 'i',
25575                 cls: 'fa ' + this.icon
25576             });
25577         }
25578         
25579         var h = {
25580             tag: 'ul',
25581             cls: 'nav nav-tabs pull-right',
25582             cn: [
25583                 header
25584             ]
25585         };
25586         
25587         if(this.tabScrollable){
25588             h = {
25589                 tag: 'div',
25590                 cls: 'tab-header',
25591                 cn: [
25592                     {
25593                         tag: 'ul',
25594                         cls: 'nav nav-tabs pull-right',
25595                         cn: [
25596                             header
25597                         ]
25598                     }
25599                 ]
25600             };
25601         }
25602         
25603         var cfg = {
25604             tag: 'div',
25605             cls: 'nav-tabs-custom',
25606             cn: [
25607                 h,
25608                 {
25609                     tag: 'div',
25610                     cls: 'tab-content no-padding',
25611                     cn: []
25612                 }
25613             ]
25614         };
25615
25616         return  cfg;
25617     },
25618     initEvents : function()
25619     {
25620         //Roo.log('add add pane handler');
25621         this.on('addpane', this.onAddPane, this);
25622     },
25623      /**
25624      * Updates the box title
25625      * @param {String} html to set the title to.
25626      */
25627     setTitle : function(value)
25628     {
25629         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25630     },
25631     onAddPane : function(pane)
25632     {
25633         this.panes.push(pane);
25634         //Roo.log('addpane');
25635         //Roo.log(pane);
25636         // tabs are rendere left to right..
25637         if(!this.showtabs){
25638             return;
25639         }
25640         
25641         var ctr = this.el.select('.nav-tabs', true).first();
25642          
25643          
25644         var existing = ctr.select('.nav-tab',true);
25645         var qty = existing.getCount();;
25646         
25647         
25648         var tab = ctr.createChild({
25649             tag : 'li',
25650             cls : 'nav-tab' + (qty ? '' : ' active'),
25651             cn : [
25652                 {
25653                     tag : 'a',
25654                     href:'#',
25655                     html : pane.title
25656                 }
25657             ]
25658         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25659         pane.tab = tab;
25660         
25661         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25662         if (!qty) {
25663             pane.el.addClass('active');
25664         }
25665         
25666                 
25667     },
25668     onTabClick : function(ev,un,ob,pane)
25669     {
25670         //Roo.log('tab - prev default');
25671         ev.preventDefault();
25672         
25673         
25674         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25675         pane.tab.addClass('active');
25676         //Roo.log(pane.title);
25677         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25678         // technically we should have a deactivate event.. but maybe add later.
25679         // and it should not de-activate the selected tab...
25680         this.fireEvent('activatepane', pane);
25681         pane.el.addClass('active');
25682         pane.fireEvent('activate');
25683         
25684         
25685     },
25686     
25687     getActivePane : function()
25688     {
25689         var r = false;
25690         Roo.each(this.panes, function(p) {
25691             if(p.el.hasClass('active')){
25692                 r = p;
25693                 return false;
25694             }
25695             
25696             return;
25697         });
25698         
25699         return r;
25700     }
25701     
25702     
25703 });
25704
25705  
25706 /*
25707  * - LGPL
25708  *
25709  * Tab pane
25710  * 
25711  */
25712 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25713 /**
25714  * @class Roo.bootstrap.TabPane
25715  * @extends Roo.bootstrap.Component
25716  * Bootstrap TabPane class
25717  * @cfg {Boolean} active (false | true) Default false
25718  * @cfg {String} title title of panel
25719
25720  * 
25721  * @constructor
25722  * Create a new TabPane
25723  * @param {Object} config The config object
25724  */
25725
25726 Roo.bootstrap.dash.TabPane = function(config){
25727     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25728     
25729     this.addEvents({
25730         // raw events
25731         /**
25732          * @event activate
25733          * When a pane is activated
25734          * @param {Roo.bootstrap.dash.TabPane} pane
25735          */
25736         "activate" : true
25737          
25738     });
25739 };
25740
25741 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25742     
25743     active : false,
25744     title : '',
25745     
25746     // the tabBox that this is attached to.
25747     tab : false,
25748      
25749     getAutoCreate : function() 
25750     {
25751         var cfg = {
25752             tag: 'div',
25753             cls: 'tab-pane'
25754         };
25755         
25756         if(this.active){
25757             cfg.cls += ' active';
25758         }
25759         
25760         return cfg;
25761     },
25762     initEvents  : function()
25763     {
25764         //Roo.log('trigger add pane handler');
25765         this.parent().fireEvent('addpane', this)
25766     },
25767     
25768      /**
25769      * Updates the tab title 
25770      * @param {String} html to set the title to.
25771      */
25772     setTitle: function(str)
25773     {
25774         if (!this.tab) {
25775             return;
25776         }
25777         this.title = str;
25778         this.tab.select('a', true).first().dom.innerHTML = str;
25779         
25780     }
25781     
25782     
25783     
25784 });
25785
25786  
25787
25788
25789  /*
25790  * - LGPL
25791  *
25792  * menu
25793  * 
25794  */
25795 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25796
25797 /**
25798  * @class Roo.bootstrap.menu.Menu
25799  * @extends Roo.bootstrap.Component
25800  * Bootstrap Menu class - container for Menu
25801  * @cfg {String} html Text of the menu
25802  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25803  * @cfg {String} icon Font awesome icon
25804  * @cfg {String} pos Menu align to (top | bottom) default bottom
25805  * 
25806  * 
25807  * @constructor
25808  * Create a new Menu
25809  * @param {Object} config The config object
25810  */
25811
25812
25813 Roo.bootstrap.menu.Menu = function(config){
25814     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25815     
25816     this.addEvents({
25817         /**
25818          * @event beforeshow
25819          * Fires before this menu is displayed
25820          * @param {Roo.bootstrap.menu.Menu} this
25821          */
25822         beforeshow : true,
25823         /**
25824          * @event beforehide
25825          * Fires before this menu is hidden
25826          * @param {Roo.bootstrap.menu.Menu} this
25827          */
25828         beforehide : true,
25829         /**
25830          * @event show
25831          * Fires after this menu is displayed
25832          * @param {Roo.bootstrap.menu.Menu} this
25833          */
25834         show : true,
25835         /**
25836          * @event hide
25837          * Fires after this menu is hidden
25838          * @param {Roo.bootstrap.menu.Menu} this
25839          */
25840         hide : true,
25841         /**
25842          * @event click
25843          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25844          * @param {Roo.bootstrap.menu.Menu} this
25845          * @param {Roo.EventObject} e
25846          */
25847         click : true
25848     });
25849     
25850 };
25851
25852 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25853     
25854     submenu : false,
25855     html : '',
25856     weight : 'default',
25857     icon : false,
25858     pos : 'bottom',
25859     
25860     
25861     getChildContainer : function() {
25862         if(this.isSubMenu){
25863             return this.el;
25864         }
25865         
25866         return this.el.select('ul.dropdown-menu', true).first();  
25867     },
25868     
25869     getAutoCreate : function()
25870     {
25871         var text = [
25872             {
25873                 tag : 'span',
25874                 cls : 'roo-menu-text',
25875                 html : this.html
25876             }
25877         ];
25878         
25879         if(this.icon){
25880             text.unshift({
25881                 tag : 'i',
25882                 cls : 'fa ' + this.icon
25883             })
25884         }
25885         
25886         
25887         var cfg = {
25888             tag : 'div',
25889             cls : 'btn-group',
25890             cn : [
25891                 {
25892                     tag : 'button',
25893                     cls : 'dropdown-button btn btn-' + this.weight,
25894                     cn : text
25895                 },
25896                 {
25897                     tag : 'button',
25898                     cls : 'dropdown-toggle btn btn-' + this.weight,
25899                     cn : [
25900                         {
25901                             tag : 'span',
25902                             cls : 'caret'
25903                         }
25904                     ]
25905                 },
25906                 {
25907                     tag : 'ul',
25908                     cls : 'dropdown-menu'
25909                 }
25910             ]
25911             
25912         };
25913         
25914         if(this.pos == 'top'){
25915             cfg.cls += ' dropup';
25916         }
25917         
25918         if(this.isSubMenu){
25919             cfg = {
25920                 tag : 'ul',
25921                 cls : 'dropdown-menu'
25922             }
25923         }
25924         
25925         return cfg;
25926     },
25927     
25928     onRender : function(ct, position)
25929     {
25930         this.isSubMenu = ct.hasClass('dropdown-submenu');
25931         
25932         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25933     },
25934     
25935     initEvents : function() 
25936     {
25937         if(this.isSubMenu){
25938             return;
25939         }
25940         
25941         this.hidden = true;
25942         
25943         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25944         this.triggerEl.on('click', this.onTriggerPress, this);
25945         
25946         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25947         this.buttonEl.on('click', this.onClick, this);
25948         
25949     },
25950     
25951     list : function()
25952     {
25953         if(this.isSubMenu){
25954             return this.el;
25955         }
25956         
25957         return this.el.select('ul.dropdown-menu', true).first();
25958     },
25959     
25960     onClick : function(e)
25961     {
25962         this.fireEvent("click", this, e);
25963     },
25964     
25965     onTriggerPress  : function(e)
25966     {   
25967         if (this.isVisible()) {
25968             this.hide();
25969         } else {
25970             this.show();
25971         }
25972     },
25973     
25974     isVisible : function(){
25975         return !this.hidden;
25976     },
25977     
25978     show : function()
25979     {
25980         this.fireEvent("beforeshow", this);
25981         
25982         this.hidden = false;
25983         this.el.addClass('open');
25984         
25985         Roo.get(document).on("mouseup", this.onMouseUp, this);
25986         
25987         this.fireEvent("show", this);
25988         
25989         
25990     },
25991     
25992     hide : function()
25993     {
25994         this.fireEvent("beforehide", this);
25995         
25996         this.hidden = true;
25997         this.el.removeClass('open');
25998         
25999         Roo.get(document).un("mouseup", this.onMouseUp);
26000         
26001         this.fireEvent("hide", this);
26002     },
26003     
26004     onMouseUp : function()
26005     {
26006         this.hide();
26007     }
26008     
26009 });
26010
26011  
26012  /*
26013  * - LGPL
26014  *
26015  * menu item
26016  * 
26017  */
26018 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26019
26020 /**
26021  * @class Roo.bootstrap.menu.Item
26022  * @extends Roo.bootstrap.Component
26023  * Bootstrap MenuItem class
26024  * @cfg {Boolean} submenu (true | false) default false
26025  * @cfg {String} html text of the item
26026  * @cfg {String} href the link
26027  * @cfg {Boolean} disable (true | false) default false
26028  * @cfg {Boolean} preventDefault (true | false) default true
26029  * @cfg {String} icon Font awesome icon
26030  * @cfg {String} pos Submenu align to (left | right) default right 
26031  * 
26032  * 
26033  * @constructor
26034  * Create a new Item
26035  * @param {Object} config The config object
26036  */
26037
26038
26039 Roo.bootstrap.menu.Item = function(config){
26040     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26041     this.addEvents({
26042         /**
26043          * @event mouseover
26044          * Fires when the mouse is hovering over this menu
26045          * @param {Roo.bootstrap.menu.Item} this
26046          * @param {Roo.EventObject} e
26047          */
26048         mouseover : true,
26049         /**
26050          * @event mouseout
26051          * Fires when the mouse exits this menu
26052          * @param {Roo.bootstrap.menu.Item} this
26053          * @param {Roo.EventObject} e
26054          */
26055         mouseout : true,
26056         // raw events
26057         /**
26058          * @event click
26059          * The raw click event for the entire grid.
26060          * @param {Roo.EventObject} e
26061          */
26062         click : true
26063     });
26064 };
26065
26066 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26067     
26068     submenu : false,
26069     href : '',
26070     html : '',
26071     preventDefault: true,
26072     disable : false,
26073     icon : false,
26074     pos : 'right',
26075     
26076     getAutoCreate : function()
26077     {
26078         var text = [
26079             {
26080                 tag : 'span',
26081                 cls : 'roo-menu-item-text',
26082                 html : this.html
26083             }
26084         ];
26085         
26086         if(this.icon){
26087             text.unshift({
26088                 tag : 'i',
26089                 cls : 'fa ' + this.icon
26090             })
26091         }
26092         
26093         var cfg = {
26094             tag : 'li',
26095             cn : [
26096                 {
26097                     tag : 'a',
26098                     href : this.href || '#',
26099                     cn : text
26100                 }
26101             ]
26102         };
26103         
26104         if(this.disable){
26105             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26106         }
26107         
26108         if(this.submenu){
26109             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26110             
26111             if(this.pos == 'left'){
26112                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26113             }
26114         }
26115         
26116         return cfg;
26117     },
26118     
26119     initEvents : function() 
26120     {
26121         this.el.on('mouseover', this.onMouseOver, this);
26122         this.el.on('mouseout', this.onMouseOut, this);
26123         
26124         this.el.select('a', true).first().on('click', this.onClick, this);
26125         
26126     },
26127     
26128     onClick : function(e)
26129     {
26130         if(this.preventDefault){
26131             e.preventDefault();
26132         }
26133         
26134         this.fireEvent("click", this, e);
26135     },
26136     
26137     onMouseOver : function(e)
26138     {
26139         if(this.submenu && this.pos == 'left'){
26140             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26141         }
26142         
26143         this.fireEvent("mouseover", this, e);
26144     },
26145     
26146     onMouseOut : function(e)
26147     {
26148         this.fireEvent("mouseout", this, e);
26149     }
26150 });
26151
26152  
26153
26154  /*
26155  * - LGPL
26156  *
26157  * menu separator
26158  * 
26159  */
26160 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26161
26162 /**
26163  * @class Roo.bootstrap.menu.Separator
26164  * @extends Roo.bootstrap.Component
26165  * Bootstrap Separator class
26166  * 
26167  * @constructor
26168  * Create a new Separator
26169  * @param {Object} config The config object
26170  */
26171
26172
26173 Roo.bootstrap.menu.Separator = function(config){
26174     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26175 };
26176
26177 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26178     
26179     getAutoCreate : function(){
26180         var cfg = {
26181             tag : 'li',
26182             cls: 'divider'
26183         };
26184         
26185         return cfg;
26186     }
26187    
26188 });
26189
26190  
26191
26192  /*
26193  * - LGPL
26194  *
26195  * Tooltip
26196  * 
26197  */
26198
26199 /**
26200  * @class Roo.bootstrap.Tooltip
26201  * Bootstrap Tooltip class
26202  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26203  * to determine which dom element triggers the tooltip.
26204  * 
26205  * It needs to add support for additional attributes like tooltip-position
26206  * 
26207  * @constructor
26208  * Create a new Toolti
26209  * @param {Object} config The config object
26210  */
26211
26212 Roo.bootstrap.Tooltip = function(config){
26213     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26214     
26215     this.alignment = Roo.bootstrap.Tooltip.alignment;
26216     
26217     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26218         this.alignment = config.alignment;
26219     }
26220     
26221 };
26222
26223 Roo.apply(Roo.bootstrap.Tooltip, {
26224     /**
26225      * @function init initialize tooltip monitoring.
26226      * @static
26227      */
26228     currentEl : false,
26229     currentTip : false,
26230     currentRegion : false,
26231     
26232     //  init : delay?
26233     
26234     init : function()
26235     {
26236         Roo.get(document).on('mouseover', this.enter ,this);
26237         Roo.get(document).on('mouseout', this.leave, this);
26238          
26239         
26240         this.currentTip = new Roo.bootstrap.Tooltip();
26241     },
26242     
26243     enter : function(ev)
26244     {
26245         var dom = ev.getTarget();
26246         
26247         //Roo.log(['enter',dom]);
26248         var el = Roo.fly(dom);
26249         if (this.currentEl) {
26250             //Roo.log(dom);
26251             //Roo.log(this.currentEl);
26252             //Roo.log(this.currentEl.contains(dom));
26253             if (this.currentEl == el) {
26254                 return;
26255             }
26256             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26257                 return;
26258             }
26259
26260         }
26261         
26262         if (this.currentTip.el) {
26263             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26264         }    
26265         //Roo.log(ev);
26266         
26267         if(!el || el.dom == document){
26268             return;
26269         }
26270         
26271         var bindEl = el;
26272         
26273         // you can not look for children, as if el is the body.. then everythign is the child..
26274         if (!el.attr('tooltip')) { //
26275             if (!el.select("[tooltip]").elements.length) {
26276                 return;
26277             }
26278             // is the mouse over this child...?
26279             bindEl = el.select("[tooltip]").first();
26280             var xy = ev.getXY();
26281             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26282                 //Roo.log("not in region.");
26283                 return;
26284             }
26285             //Roo.log("child element over..");
26286             
26287         }
26288         this.currentEl = bindEl;
26289         this.currentTip.bind(bindEl);
26290         this.currentRegion = Roo.lib.Region.getRegion(dom);
26291         this.currentTip.enter();
26292         
26293     },
26294     leave : function(ev)
26295     {
26296         var dom = ev.getTarget();
26297         //Roo.log(['leave',dom]);
26298         if (!this.currentEl) {
26299             return;
26300         }
26301         
26302         
26303         if (dom != this.currentEl.dom) {
26304             return;
26305         }
26306         var xy = ev.getXY();
26307         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26308             return;
26309         }
26310         // only activate leave if mouse cursor is outside... bounding box..
26311         
26312         
26313         
26314         
26315         if (this.currentTip) {
26316             this.currentTip.leave();
26317         }
26318         //Roo.log('clear currentEl');
26319         this.currentEl = false;
26320         
26321         
26322     },
26323     alignment : {
26324         'left' : ['r-l', [-2,0], 'right'],
26325         'right' : ['l-r', [2,0], 'left'],
26326         'bottom' : ['t-b', [0,2], 'top'],
26327         'top' : [ 'b-t', [0,-2], 'bottom']
26328     }
26329     
26330 });
26331
26332
26333 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26334     
26335     
26336     bindEl : false,
26337     
26338     delay : null, // can be { show : 300 , hide: 500}
26339     
26340     timeout : null,
26341     
26342     hoverState : null, //???
26343     
26344     placement : 'bottom', 
26345     
26346     alignment : false,
26347     
26348     getAutoCreate : function(){
26349     
26350         var cfg = {
26351            cls : 'tooltip',
26352            role : 'tooltip',
26353            cn : [
26354                 {
26355                     cls : 'tooltip-arrow'
26356                 },
26357                 {
26358                     cls : 'tooltip-inner'
26359                 }
26360            ]
26361         };
26362         
26363         return cfg;
26364     },
26365     bind : function(el)
26366     {
26367         this.bindEl = el;
26368     },
26369       
26370     
26371     enter : function () {
26372        
26373         if (this.timeout != null) {
26374             clearTimeout(this.timeout);
26375         }
26376         
26377         this.hoverState = 'in';
26378          //Roo.log("enter - show");
26379         if (!this.delay || !this.delay.show) {
26380             this.show();
26381             return;
26382         }
26383         var _t = this;
26384         this.timeout = setTimeout(function () {
26385             if (_t.hoverState == 'in') {
26386                 _t.show();
26387             }
26388         }, this.delay.show);
26389     },
26390     leave : function()
26391     {
26392         clearTimeout(this.timeout);
26393     
26394         this.hoverState = 'out';
26395          if (!this.delay || !this.delay.hide) {
26396             this.hide();
26397             return;
26398         }
26399        
26400         var _t = this;
26401         this.timeout = setTimeout(function () {
26402             //Roo.log("leave - timeout");
26403             
26404             if (_t.hoverState == 'out') {
26405                 _t.hide();
26406                 Roo.bootstrap.Tooltip.currentEl = false;
26407             }
26408         }, delay);
26409     },
26410     
26411     show : function (msg)
26412     {
26413         if (!this.el) {
26414             this.render(document.body);
26415         }
26416         // set content.
26417         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26418         
26419         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26420         
26421         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26422         
26423         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26424         
26425         var placement = typeof this.placement == 'function' ?
26426             this.placement.call(this, this.el, on_el) :
26427             this.placement;
26428             
26429         var autoToken = /\s?auto?\s?/i;
26430         var autoPlace = autoToken.test(placement);
26431         if (autoPlace) {
26432             placement = placement.replace(autoToken, '') || 'top';
26433         }
26434         
26435         //this.el.detach()
26436         //this.el.setXY([0,0]);
26437         this.el.show();
26438         //this.el.dom.style.display='block';
26439         
26440         //this.el.appendTo(on_el);
26441         
26442         var p = this.getPosition();
26443         var box = this.el.getBox();
26444         
26445         if (autoPlace) {
26446             // fixme..
26447         }
26448         
26449         var align = this.alignment[placement];
26450         
26451         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26452         
26453         if(placement == 'top' || placement == 'bottom'){
26454             if(xy[0] < 0){
26455                 placement = 'right';
26456             }
26457             
26458             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26459                 placement = 'left';
26460             }
26461             
26462             var scroll = Roo.select('body', true).first().getScroll();
26463             
26464             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26465                 placement = 'top';
26466             }
26467             
26468             align = this.alignment[placement];
26469         }
26470         
26471         this.el.alignTo(this.bindEl, align[0],align[1]);
26472         //var arrow = this.el.select('.arrow',true).first();
26473         //arrow.set(align[2], 
26474         
26475         this.el.addClass(placement);
26476         
26477         this.el.addClass('in fade');
26478         
26479         this.hoverState = null;
26480         
26481         if (this.el.hasClass('fade')) {
26482             // fade it?
26483         }
26484         
26485     },
26486     hide : function()
26487     {
26488          
26489         if (!this.el) {
26490             return;
26491         }
26492         //this.el.setXY([0,0]);
26493         this.el.removeClass('in');
26494         //this.el.hide();
26495         
26496     }
26497     
26498 });
26499  
26500
26501  /*
26502  * - LGPL
26503  *
26504  * Location Picker
26505  * 
26506  */
26507
26508 /**
26509  * @class Roo.bootstrap.LocationPicker
26510  * @extends Roo.bootstrap.Component
26511  * Bootstrap LocationPicker class
26512  * @cfg {Number} latitude Position when init default 0
26513  * @cfg {Number} longitude Position when init default 0
26514  * @cfg {Number} zoom default 15
26515  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26516  * @cfg {Boolean} mapTypeControl default false
26517  * @cfg {Boolean} disableDoubleClickZoom default false
26518  * @cfg {Boolean} scrollwheel default true
26519  * @cfg {Boolean} streetViewControl default false
26520  * @cfg {Number} radius default 0
26521  * @cfg {String} locationName
26522  * @cfg {Boolean} draggable default true
26523  * @cfg {Boolean} enableAutocomplete default false
26524  * @cfg {Boolean} enableReverseGeocode default true
26525  * @cfg {String} markerTitle
26526  * 
26527  * @constructor
26528  * Create a new LocationPicker
26529  * @param {Object} config The config object
26530  */
26531
26532
26533 Roo.bootstrap.LocationPicker = function(config){
26534     
26535     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26536     
26537     this.addEvents({
26538         /**
26539          * @event initial
26540          * Fires when the picker initialized.
26541          * @param {Roo.bootstrap.LocationPicker} this
26542          * @param {Google Location} location
26543          */
26544         initial : true,
26545         /**
26546          * @event positionchanged
26547          * Fires when the picker position changed.
26548          * @param {Roo.bootstrap.LocationPicker} this
26549          * @param {Google Location} location
26550          */
26551         positionchanged : true,
26552         /**
26553          * @event resize
26554          * Fires when the map resize.
26555          * @param {Roo.bootstrap.LocationPicker} this
26556          */
26557         resize : true,
26558         /**
26559          * @event show
26560          * Fires when the map show.
26561          * @param {Roo.bootstrap.LocationPicker} this
26562          */
26563         show : true,
26564         /**
26565          * @event hide
26566          * Fires when the map hide.
26567          * @param {Roo.bootstrap.LocationPicker} this
26568          */
26569         hide : true,
26570         /**
26571          * @event mapClick
26572          * Fires when click the map.
26573          * @param {Roo.bootstrap.LocationPicker} this
26574          * @param {Map event} e
26575          */
26576         mapClick : true,
26577         /**
26578          * @event mapRightClick
26579          * Fires when right click the map.
26580          * @param {Roo.bootstrap.LocationPicker} this
26581          * @param {Map event} e
26582          */
26583         mapRightClick : true,
26584         /**
26585          * @event markerClick
26586          * Fires when click the marker.
26587          * @param {Roo.bootstrap.LocationPicker} this
26588          * @param {Map event} e
26589          */
26590         markerClick : true,
26591         /**
26592          * @event markerRightClick
26593          * Fires when right click the marker.
26594          * @param {Roo.bootstrap.LocationPicker} this
26595          * @param {Map event} e
26596          */
26597         markerRightClick : true,
26598         /**
26599          * @event OverlayViewDraw
26600          * Fires when OverlayView Draw
26601          * @param {Roo.bootstrap.LocationPicker} this
26602          */
26603         OverlayViewDraw : true,
26604         /**
26605          * @event OverlayViewOnAdd
26606          * Fires when OverlayView Draw
26607          * @param {Roo.bootstrap.LocationPicker} this
26608          */
26609         OverlayViewOnAdd : true,
26610         /**
26611          * @event OverlayViewOnRemove
26612          * Fires when OverlayView Draw
26613          * @param {Roo.bootstrap.LocationPicker} this
26614          */
26615         OverlayViewOnRemove : true,
26616         /**
26617          * @event OverlayViewShow
26618          * Fires when OverlayView Draw
26619          * @param {Roo.bootstrap.LocationPicker} this
26620          * @param {Pixel} cpx
26621          */
26622         OverlayViewShow : true,
26623         /**
26624          * @event OverlayViewHide
26625          * Fires when OverlayView Draw
26626          * @param {Roo.bootstrap.LocationPicker} this
26627          */
26628         OverlayViewHide : true,
26629         /**
26630          * @event loadexception
26631          * Fires when load google lib failed.
26632          * @param {Roo.bootstrap.LocationPicker} this
26633          */
26634         loadexception : true
26635     });
26636         
26637 };
26638
26639 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26640     
26641     gMapContext: false,
26642     
26643     latitude: 0,
26644     longitude: 0,
26645     zoom: 15,
26646     mapTypeId: false,
26647     mapTypeControl: false,
26648     disableDoubleClickZoom: false,
26649     scrollwheel: true,
26650     streetViewControl: false,
26651     radius: 0,
26652     locationName: '',
26653     draggable: true,
26654     enableAutocomplete: false,
26655     enableReverseGeocode: true,
26656     markerTitle: '',
26657     
26658     getAutoCreate: function()
26659     {
26660
26661         var cfg = {
26662             tag: 'div',
26663             cls: 'roo-location-picker'
26664         };
26665         
26666         return cfg
26667     },
26668     
26669     initEvents: function(ct, position)
26670     {       
26671         if(!this.el.getWidth() || this.isApplied()){
26672             return;
26673         }
26674         
26675         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26676         
26677         this.initial();
26678     },
26679     
26680     initial: function()
26681     {
26682         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26683             this.fireEvent('loadexception', this);
26684             return;
26685         }
26686         
26687         if(!this.mapTypeId){
26688             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26689         }
26690         
26691         this.gMapContext = this.GMapContext();
26692         
26693         this.initOverlayView();
26694         
26695         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26696         
26697         var _this = this;
26698                 
26699         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26700             _this.setPosition(_this.gMapContext.marker.position);
26701         });
26702         
26703         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26704             _this.fireEvent('mapClick', this, event);
26705             
26706         });
26707
26708         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26709             _this.fireEvent('mapRightClick', this, event);
26710             
26711         });
26712         
26713         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26714             _this.fireEvent('markerClick', this, event);
26715             
26716         });
26717
26718         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26719             _this.fireEvent('markerRightClick', this, event);
26720             
26721         });
26722         
26723         this.setPosition(this.gMapContext.location);
26724         
26725         this.fireEvent('initial', this, this.gMapContext.location);
26726     },
26727     
26728     initOverlayView: function()
26729     {
26730         var _this = this;
26731         
26732         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26733             
26734             draw: function()
26735             {
26736                 _this.fireEvent('OverlayViewDraw', _this);
26737             },
26738             
26739             onAdd: function()
26740             {
26741                 _this.fireEvent('OverlayViewOnAdd', _this);
26742             },
26743             
26744             onRemove: function()
26745             {
26746                 _this.fireEvent('OverlayViewOnRemove', _this);
26747             },
26748             
26749             show: function(cpx)
26750             {
26751                 _this.fireEvent('OverlayViewShow', _this, cpx);
26752             },
26753             
26754             hide: function()
26755             {
26756                 _this.fireEvent('OverlayViewHide', _this);
26757             }
26758             
26759         });
26760     },
26761     
26762     fromLatLngToContainerPixel: function(event)
26763     {
26764         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26765     },
26766     
26767     isApplied: function() 
26768     {
26769         return this.getGmapContext() == false ? false : true;
26770     },
26771     
26772     getGmapContext: function() 
26773     {
26774         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26775     },
26776     
26777     GMapContext: function() 
26778     {
26779         var position = new google.maps.LatLng(this.latitude, this.longitude);
26780         
26781         var _map = new google.maps.Map(this.el.dom, {
26782             center: position,
26783             zoom: this.zoom,
26784             mapTypeId: this.mapTypeId,
26785             mapTypeControl: this.mapTypeControl,
26786             disableDoubleClickZoom: this.disableDoubleClickZoom,
26787             scrollwheel: this.scrollwheel,
26788             streetViewControl: this.streetViewControl,
26789             locationName: this.locationName,
26790             draggable: this.draggable,
26791             enableAutocomplete: this.enableAutocomplete,
26792             enableReverseGeocode: this.enableReverseGeocode
26793         });
26794         
26795         var _marker = new google.maps.Marker({
26796             position: position,
26797             map: _map,
26798             title: this.markerTitle,
26799             draggable: this.draggable
26800         });
26801         
26802         return {
26803             map: _map,
26804             marker: _marker,
26805             circle: null,
26806             location: position,
26807             radius: this.radius,
26808             locationName: this.locationName,
26809             addressComponents: {
26810                 formatted_address: null,
26811                 addressLine1: null,
26812                 addressLine2: null,
26813                 streetName: null,
26814                 streetNumber: null,
26815                 city: null,
26816                 district: null,
26817                 state: null,
26818                 stateOrProvince: null
26819             },
26820             settings: this,
26821             domContainer: this.el.dom,
26822             geodecoder: new google.maps.Geocoder()
26823         };
26824     },
26825     
26826     drawCircle: function(center, radius, options) 
26827     {
26828         if (this.gMapContext.circle != null) {
26829             this.gMapContext.circle.setMap(null);
26830         }
26831         if (radius > 0) {
26832             radius *= 1;
26833             options = Roo.apply({}, options, {
26834                 strokeColor: "#0000FF",
26835                 strokeOpacity: .35,
26836                 strokeWeight: 2,
26837                 fillColor: "#0000FF",
26838                 fillOpacity: .2
26839             });
26840             
26841             options.map = this.gMapContext.map;
26842             options.radius = radius;
26843             options.center = center;
26844             this.gMapContext.circle = new google.maps.Circle(options);
26845             return this.gMapContext.circle;
26846         }
26847         
26848         return null;
26849     },
26850     
26851     setPosition: function(location) 
26852     {
26853         this.gMapContext.location = location;
26854         this.gMapContext.marker.setPosition(location);
26855         this.gMapContext.map.panTo(location);
26856         this.drawCircle(location, this.gMapContext.radius, {});
26857         
26858         var _this = this;
26859         
26860         if (this.gMapContext.settings.enableReverseGeocode) {
26861             this.gMapContext.geodecoder.geocode({
26862                 latLng: this.gMapContext.location
26863             }, function(results, status) {
26864                 
26865                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26866                     _this.gMapContext.locationName = results[0].formatted_address;
26867                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26868                     
26869                     _this.fireEvent('positionchanged', this, location);
26870                 }
26871             });
26872             
26873             return;
26874         }
26875         
26876         this.fireEvent('positionchanged', this, location);
26877     },
26878     
26879     resize: function()
26880     {
26881         google.maps.event.trigger(this.gMapContext.map, "resize");
26882         
26883         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26884         
26885         this.fireEvent('resize', this);
26886     },
26887     
26888     setPositionByLatLng: function(latitude, longitude)
26889     {
26890         this.setPosition(new google.maps.LatLng(latitude, longitude));
26891     },
26892     
26893     getCurrentPosition: function() 
26894     {
26895         return {
26896             latitude: this.gMapContext.location.lat(),
26897             longitude: this.gMapContext.location.lng()
26898         };
26899     },
26900     
26901     getAddressName: function() 
26902     {
26903         return this.gMapContext.locationName;
26904     },
26905     
26906     getAddressComponents: function() 
26907     {
26908         return this.gMapContext.addressComponents;
26909     },
26910     
26911     address_component_from_google_geocode: function(address_components) 
26912     {
26913         var result = {};
26914         
26915         for (var i = 0; i < address_components.length; i++) {
26916             var component = address_components[i];
26917             if (component.types.indexOf("postal_code") >= 0) {
26918                 result.postalCode = component.short_name;
26919             } else if (component.types.indexOf("street_number") >= 0) {
26920                 result.streetNumber = component.short_name;
26921             } else if (component.types.indexOf("route") >= 0) {
26922                 result.streetName = component.short_name;
26923             } else if (component.types.indexOf("neighborhood") >= 0) {
26924                 result.city = component.short_name;
26925             } else if (component.types.indexOf("locality") >= 0) {
26926                 result.city = component.short_name;
26927             } else if (component.types.indexOf("sublocality") >= 0) {
26928                 result.district = component.short_name;
26929             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26930                 result.stateOrProvince = component.short_name;
26931             } else if (component.types.indexOf("country") >= 0) {
26932                 result.country = component.short_name;
26933             }
26934         }
26935         
26936         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26937         result.addressLine2 = "";
26938         return result;
26939     },
26940     
26941     setZoomLevel: function(zoom)
26942     {
26943         this.gMapContext.map.setZoom(zoom);
26944     },
26945     
26946     show: function()
26947     {
26948         if(!this.el){
26949             return;
26950         }
26951         
26952         this.el.show();
26953         
26954         this.resize();
26955         
26956         this.fireEvent('show', this);
26957     },
26958     
26959     hide: function()
26960     {
26961         if(!this.el){
26962             return;
26963         }
26964         
26965         this.el.hide();
26966         
26967         this.fireEvent('hide', this);
26968     }
26969     
26970 });
26971
26972 Roo.apply(Roo.bootstrap.LocationPicker, {
26973     
26974     OverlayView : function(map, options)
26975     {
26976         options = options || {};
26977         
26978         this.setMap(map);
26979     }
26980     
26981     
26982 });/*
26983  * - LGPL
26984  *
26985  * Alert
26986  * 
26987  */
26988
26989 /**
26990  * @class Roo.bootstrap.Alert
26991  * @extends Roo.bootstrap.Component
26992  * Bootstrap Alert class
26993  * @cfg {String} title The title of alert
26994  * @cfg {String} html The content of alert
26995  * @cfg {String} weight (  success | info | warning | danger )
26996  * @cfg {String} faicon font-awesomeicon
26997  * 
26998  * @constructor
26999  * Create a new alert
27000  * @param {Object} config The config object
27001  */
27002
27003
27004 Roo.bootstrap.Alert = function(config){
27005     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27006     
27007 };
27008
27009 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27010     
27011     title: '',
27012     html: '',
27013     weight: false,
27014     faicon: false,
27015     
27016     getAutoCreate : function()
27017     {
27018         
27019         var cfg = {
27020             tag : 'div',
27021             cls : 'alert',
27022             cn : [
27023                 {
27024                     tag : 'i',
27025                     cls : 'roo-alert-icon'
27026                     
27027                 },
27028                 {
27029                     tag : 'b',
27030                     cls : 'roo-alert-title',
27031                     html : this.title
27032                 },
27033                 {
27034                     tag : 'span',
27035                     cls : 'roo-alert-text',
27036                     html : this.html
27037                 }
27038             ]
27039         };
27040         
27041         if(this.faicon){
27042             cfg.cn[0].cls += ' fa ' + this.faicon;
27043         }
27044         
27045         if(this.weight){
27046             cfg.cls += ' alert-' + this.weight;
27047         }
27048         
27049         return cfg;
27050     },
27051     
27052     initEvents: function() 
27053     {
27054         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27055     },
27056     
27057     setTitle : function(str)
27058     {
27059         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27060     },
27061     
27062     setText : function(str)
27063     {
27064         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27065     },
27066     
27067     setWeight : function(weight)
27068     {
27069         if(this.weight){
27070             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27071         }
27072         
27073         this.weight = weight;
27074         
27075         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27076     },
27077     
27078     setIcon : function(icon)
27079     {
27080         if(this.faicon){
27081             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27082         }
27083         
27084         this.faicon = icon;
27085         
27086         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27087     },
27088     
27089     hide: function() 
27090     {
27091         this.el.hide();   
27092     },
27093     
27094     show: function() 
27095     {  
27096         this.el.show();   
27097     }
27098     
27099 });
27100
27101  
27102 /*
27103 * Licence: LGPL
27104 */
27105
27106 /**
27107  * @class Roo.bootstrap.UploadCropbox
27108  * @extends Roo.bootstrap.Component
27109  * Bootstrap UploadCropbox class
27110  * @cfg {String} emptyText show when image has been loaded
27111  * @cfg {String} rotateNotify show when image too small to rotate
27112  * @cfg {Number} errorTimeout default 3000
27113  * @cfg {Number} minWidth default 300
27114  * @cfg {Number} minHeight default 300
27115  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27116  * @cfg {Boolean} isDocument (true|false) default false
27117  * @cfg {String} url action url
27118  * @cfg {String} paramName default 'imageUpload'
27119  * @cfg {String} method default POST
27120  * @cfg {Boolean} loadMask (true|false) default true
27121  * @cfg {Boolean} loadingText default 'Loading...'
27122  * 
27123  * @constructor
27124  * Create a new UploadCropbox
27125  * @param {Object} config The config object
27126  */
27127
27128 Roo.bootstrap.UploadCropbox = function(config){
27129     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27130     
27131     this.addEvents({
27132         /**
27133          * @event beforeselectfile
27134          * Fire before select file
27135          * @param {Roo.bootstrap.UploadCropbox} this
27136          */
27137         "beforeselectfile" : true,
27138         /**
27139          * @event initial
27140          * Fire after initEvent
27141          * @param {Roo.bootstrap.UploadCropbox} this
27142          */
27143         "initial" : true,
27144         /**
27145          * @event crop
27146          * Fire after initEvent
27147          * @param {Roo.bootstrap.UploadCropbox} this
27148          * @param {String} data
27149          */
27150         "crop" : true,
27151         /**
27152          * @event prepare
27153          * Fire when preparing the file data
27154          * @param {Roo.bootstrap.UploadCropbox} this
27155          * @param {Object} file
27156          */
27157         "prepare" : true,
27158         /**
27159          * @event exception
27160          * Fire when get exception
27161          * @param {Roo.bootstrap.UploadCropbox} this
27162          * @param {XMLHttpRequest} xhr
27163          */
27164         "exception" : true,
27165         /**
27166          * @event beforeloadcanvas
27167          * Fire before load the canvas
27168          * @param {Roo.bootstrap.UploadCropbox} this
27169          * @param {String} src
27170          */
27171         "beforeloadcanvas" : true,
27172         /**
27173          * @event trash
27174          * Fire when trash image
27175          * @param {Roo.bootstrap.UploadCropbox} this
27176          */
27177         "trash" : true,
27178         /**
27179          * @event download
27180          * Fire when download the image
27181          * @param {Roo.bootstrap.UploadCropbox} this
27182          */
27183         "download" : true,
27184         /**
27185          * @event footerbuttonclick
27186          * Fire when footerbuttonclick
27187          * @param {Roo.bootstrap.UploadCropbox} this
27188          * @param {String} type
27189          */
27190         "footerbuttonclick" : true,
27191         /**
27192          * @event resize
27193          * Fire when resize
27194          * @param {Roo.bootstrap.UploadCropbox} this
27195          */
27196         "resize" : true,
27197         /**
27198          * @event rotate
27199          * Fire when rotate the image
27200          * @param {Roo.bootstrap.UploadCropbox} this
27201          * @param {String} pos
27202          */
27203         "rotate" : true,
27204         /**
27205          * @event inspect
27206          * Fire when inspect the file
27207          * @param {Roo.bootstrap.UploadCropbox} this
27208          * @param {Object} file
27209          */
27210         "inspect" : true,
27211         /**
27212          * @event upload
27213          * Fire when xhr upload the file
27214          * @param {Roo.bootstrap.UploadCropbox} this
27215          * @param {Object} data
27216          */
27217         "upload" : true,
27218         /**
27219          * @event arrange
27220          * Fire when arrange the file data
27221          * @param {Roo.bootstrap.UploadCropbox} this
27222          * @param {Object} formData
27223          */
27224         "arrange" : true
27225     });
27226     
27227     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27228 };
27229
27230 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27231     
27232     emptyText : 'Click to upload image',
27233     rotateNotify : 'Image is too small to rotate',
27234     errorTimeout : 3000,
27235     scale : 0,
27236     baseScale : 1,
27237     rotate : 0,
27238     dragable : false,
27239     pinching : false,
27240     mouseX : 0,
27241     mouseY : 0,
27242     cropData : false,
27243     minWidth : 300,
27244     minHeight : 300,
27245     file : false,
27246     exif : {},
27247     baseRotate : 1,
27248     cropType : 'image/jpeg',
27249     buttons : false,
27250     canvasLoaded : false,
27251     isDocument : false,
27252     method : 'POST',
27253     paramName : 'imageUpload',
27254     loadMask : true,
27255     loadingText : 'Loading...',
27256     maskEl : false,
27257     
27258     getAutoCreate : function()
27259     {
27260         var cfg = {
27261             tag : 'div',
27262             cls : 'roo-upload-cropbox',
27263             cn : [
27264                 {
27265                     tag : 'input',
27266                     cls : 'roo-upload-cropbox-selector',
27267                     type : 'file'
27268                 },
27269                 {
27270                     tag : 'div',
27271                     cls : 'roo-upload-cropbox-body',
27272                     style : 'cursor:pointer',
27273                     cn : [
27274                         {
27275                             tag : 'div',
27276                             cls : 'roo-upload-cropbox-preview'
27277                         },
27278                         {
27279                             tag : 'div',
27280                             cls : 'roo-upload-cropbox-thumb'
27281                         },
27282                         {
27283                             tag : 'div',
27284                             cls : 'roo-upload-cropbox-empty-notify',
27285                             html : this.emptyText
27286                         },
27287                         {
27288                             tag : 'div',
27289                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27290                             html : this.rotateNotify
27291                         }
27292                     ]
27293                 },
27294                 {
27295                     tag : 'div',
27296                     cls : 'roo-upload-cropbox-footer',
27297                     cn : {
27298                         tag : 'div',
27299                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27300                         cn : []
27301                     }
27302                 }
27303             ]
27304         };
27305         
27306         return cfg;
27307     },
27308     
27309     onRender : function(ct, position)
27310     {
27311         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27312         
27313         if (this.buttons.length) {
27314             
27315             Roo.each(this.buttons, function(bb) {
27316                 
27317                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27318                 
27319                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27320                 
27321             }, this);
27322         }
27323         
27324         if(this.loadMask){
27325             this.maskEl = this.el;
27326         }
27327     },
27328     
27329     initEvents : function()
27330     {
27331         this.urlAPI = (window.createObjectURL && window) || 
27332                                 (window.URL && URL.revokeObjectURL && URL) || 
27333                                 (window.webkitURL && webkitURL);
27334                         
27335         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27336         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27337         
27338         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27339         this.selectorEl.hide();
27340         
27341         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27342         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27343         
27344         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27345         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27346         this.thumbEl.hide();
27347         
27348         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27349         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27350         
27351         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27352         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27353         this.errorEl.hide();
27354         
27355         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27356         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27357         this.footerEl.hide();
27358         
27359         this.setThumbBoxSize();
27360         
27361         this.bind();
27362         
27363         this.resize();
27364         
27365         this.fireEvent('initial', this);
27366     },
27367
27368     bind : function()
27369     {
27370         var _this = this;
27371         
27372         window.addEventListener("resize", function() { _this.resize(); } );
27373         
27374         this.bodyEl.on('click', this.beforeSelectFile, this);
27375         
27376         if(Roo.isTouch){
27377             this.bodyEl.on('touchstart', this.onTouchStart, this);
27378             this.bodyEl.on('touchmove', this.onTouchMove, this);
27379             this.bodyEl.on('touchend', this.onTouchEnd, this);
27380         }
27381         
27382         if(!Roo.isTouch){
27383             this.bodyEl.on('mousedown', this.onMouseDown, this);
27384             this.bodyEl.on('mousemove', this.onMouseMove, this);
27385             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27386             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27387             Roo.get(document).on('mouseup', this.onMouseUp, this);
27388         }
27389         
27390         this.selectorEl.on('change', this.onFileSelected, this);
27391     },
27392     
27393     reset : function()
27394     {    
27395         this.scale = 0;
27396         this.baseScale = 1;
27397         this.rotate = 0;
27398         this.baseRotate = 1;
27399         this.dragable = false;
27400         this.pinching = false;
27401         this.mouseX = 0;
27402         this.mouseY = 0;
27403         this.cropData = false;
27404         this.notifyEl.dom.innerHTML = this.emptyText;
27405         
27406         this.selectorEl.dom.value = '';
27407         
27408     },
27409     
27410     resize : function()
27411     {
27412         if(this.fireEvent('resize', this) != false){
27413             this.setThumbBoxPosition();
27414             this.setCanvasPosition();
27415         }
27416     },
27417     
27418     onFooterButtonClick : function(e, el, o, type)
27419     {
27420         switch (type) {
27421             case 'rotate-left' :
27422                 this.onRotateLeft(e);
27423                 break;
27424             case 'rotate-right' :
27425                 this.onRotateRight(e);
27426                 break;
27427             case 'picture' :
27428                 this.beforeSelectFile(e);
27429                 break;
27430             case 'trash' :
27431                 this.trash(e);
27432                 break;
27433             case 'crop' :
27434                 this.crop(e);
27435                 break;
27436             case 'download' :
27437                 this.download(e);
27438                 break;
27439             default :
27440                 break;
27441         }
27442         
27443         this.fireEvent('footerbuttonclick', this, type);
27444     },
27445     
27446     beforeSelectFile : function(e)
27447     {
27448         e.preventDefault();
27449         
27450         if(this.fireEvent('beforeselectfile', this) != false){
27451             this.selectorEl.dom.click();
27452         }
27453     },
27454     
27455     onFileSelected : function(e)
27456     {
27457         e.preventDefault();
27458         
27459         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27460             return;
27461         }
27462         
27463         var file = this.selectorEl.dom.files[0];
27464         
27465         if(this.fireEvent('inspect', this, file) != false){
27466             this.prepare(file);
27467         }
27468         
27469     },
27470     
27471     trash : function(e)
27472     {
27473         this.fireEvent('trash', this);
27474     },
27475     
27476     download : function(e)
27477     {
27478         this.fireEvent('download', this);
27479     },
27480     
27481     loadCanvas : function(src)
27482     {   
27483         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27484             
27485             this.reset();
27486             
27487             this.imageEl = document.createElement('img');
27488             
27489             var _this = this;
27490             
27491             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27492             
27493             this.imageEl.src = src;
27494         }
27495     },
27496     
27497     onLoadCanvas : function()
27498     {   
27499         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27500         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27501         
27502         this.bodyEl.un('click', this.beforeSelectFile, this);
27503         
27504         this.notifyEl.hide();
27505         this.thumbEl.show();
27506         this.footerEl.show();
27507         
27508         this.baseRotateLevel();
27509         
27510         if(this.isDocument){
27511             this.setThumbBoxSize();
27512         }
27513         
27514         this.setThumbBoxPosition();
27515         
27516         this.baseScaleLevel();
27517         
27518         this.draw();
27519         
27520         this.resize();
27521         
27522         this.canvasLoaded = true;
27523         
27524         if(this.loadMask){
27525             this.maskEl.unmask();
27526         }
27527         
27528     },
27529     
27530     setCanvasPosition : function()
27531     {   
27532         if(!this.canvasEl){
27533             return;
27534         }
27535         
27536         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27537         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27538         
27539         this.previewEl.setLeft(pw);
27540         this.previewEl.setTop(ph);
27541         
27542     },
27543     
27544     onMouseDown : function(e)
27545     {   
27546         e.stopEvent();
27547         
27548         this.dragable = true;
27549         this.pinching = false;
27550         
27551         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27552             this.dragable = false;
27553             return;
27554         }
27555         
27556         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27557         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27558         
27559     },
27560     
27561     onMouseMove : function(e)
27562     {   
27563         e.stopEvent();
27564         
27565         if(!this.canvasLoaded){
27566             return;
27567         }
27568         
27569         if (!this.dragable){
27570             return;
27571         }
27572         
27573         var minX = Math.ceil(this.thumbEl.getLeft(true));
27574         var minY = Math.ceil(this.thumbEl.getTop(true));
27575         
27576         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27577         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27578         
27579         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27580         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27581         
27582         x = x - this.mouseX;
27583         y = y - this.mouseY;
27584         
27585         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27586         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27587         
27588         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27589         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27590         
27591         this.previewEl.setLeft(bgX);
27592         this.previewEl.setTop(bgY);
27593         
27594         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27595         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27596     },
27597     
27598     onMouseUp : function(e)
27599     {   
27600         e.stopEvent();
27601         
27602         this.dragable = false;
27603     },
27604     
27605     onMouseWheel : function(e)
27606     {   
27607         e.stopEvent();
27608         
27609         this.startScale = this.scale;
27610         
27611         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27612         
27613         if(!this.zoomable()){
27614             this.scale = this.startScale;
27615             return;
27616         }
27617         
27618         this.draw();
27619         
27620         return;
27621     },
27622     
27623     zoomable : function()
27624     {
27625         var minScale = this.thumbEl.getWidth() / this.minWidth;
27626         
27627         if(this.minWidth < this.minHeight){
27628             minScale = this.thumbEl.getHeight() / this.minHeight;
27629         }
27630         
27631         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27632         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27633         
27634         if(
27635                 this.isDocument &&
27636                 (this.rotate == 0 || this.rotate == 180) && 
27637                 (
27638                     width > this.imageEl.OriginWidth || 
27639                     height > this.imageEl.OriginHeight ||
27640                     (width < this.minWidth && height < this.minHeight)
27641                 )
27642         ){
27643             return false;
27644         }
27645         
27646         if(
27647                 this.isDocument &&
27648                 (this.rotate == 90 || this.rotate == 270) && 
27649                 (
27650                     width > this.imageEl.OriginWidth || 
27651                     height > this.imageEl.OriginHeight ||
27652                     (width < this.minHeight && height < this.minWidth)
27653                 )
27654         ){
27655             return false;
27656         }
27657         
27658         if(
27659                 !this.isDocument &&
27660                 (this.rotate == 0 || this.rotate == 180) && 
27661                 (
27662                     width < this.minWidth || 
27663                     width > this.imageEl.OriginWidth || 
27664                     height < this.minHeight || 
27665                     height > this.imageEl.OriginHeight
27666                 )
27667         ){
27668             return false;
27669         }
27670         
27671         if(
27672                 !this.isDocument &&
27673                 (this.rotate == 90 || this.rotate == 270) && 
27674                 (
27675                     width < this.minHeight || 
27676                     width > this.imageEl.OriginWidth || 
27677                     height < this.minWidth || 
27678                     height > this.imageEl.OriginHeight
27679                 )
27680         ){
27681             return false;
27682         }
27683         
27684         return true;
27685         
27686     },
27687     
27688     onRotateLeft : function(e)
27689     {   
27690         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27691             
27692             var minScale = this.thumbEl.getWidth() / this.minWidth;
27693             
27694             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27695             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27696             
27697             this.startScale = this.scale;
27698             
27699             while (this.getScaleLevel() < minScale){
27700             
27701                 this.scale = this.scale + 1;
27702                 
27703                 if(!this.zoomable()){
27704                     break;
27705                 }
27706                 
27707                 if(
27708                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27709                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27710                 ){
27711                     continue;
27712                 }
27713                 
27714                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27715
27716                 this.draw();
27717                 
27718                 return;
27719             }
27720             
27721             this.scale = this.startScale;
27722             
27723             this.onRotateFail();
27724             
27725             return false;
27726         }
27727         
27728         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27729
27730         if(this.isDocument){
27731             this.setThumbBoxSize();
27732             this.setThumbBoxPosition();
27733             this.setCanvasPosition();
27734         }
27735         
27736         this.draw();
27737         
27738         this.fireEvent('rotate', this, 'left');
27739         
27740     },
27741     
27742     onRotateRight : function(e)
27743     {
27744         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27745             
27746             var minScale = this.thumbEl.getWidth() / this.minWidth;
27747         
27748             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27749             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27750             
27751             this.startScale = this.scale;
27752             
27753             while (this.getScaleLevel() < minScale){
27754             
27755                 this.scale = this.scale + 1;
27756                 
27757                 if(!this.zoomable()){
27758                     break;
27759                 }
27760                 
27761                 if(
27762                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27763                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27764                 ){
27765                     continue;
27766                 }
27767                 
27768                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27769
27770                 this.draw();
27771                 
27772                 return;
27773             }
27774             
27775             this.scale = this.startScale;
27776             
27777             this.onRotateFail();
27778             
27779             return false;
27780         }
27781         
27782         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27783
27784         if(this.isDocument){
27785             this.setThumbBoxSize();
27786             this.setThumbBoxPosition();
27787             this.setCanvasPosition();
27788         }
27789         
27790         this.draw();
27791         
27792         this.fireEvent('rotate', this, 'right');
27793     },
27794     
27795     onRotateFail : function()
27796     {
27797         this.errorEl.show(true);
27798         
27799         var _this = this;
27800         
27801         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27802     },
27803     
27804     draw : function()
27805     {
27806         this.previewEl.dom.innerHTML = '';
27807         
27808         var canvasEl = document.createElement("canvas");
27809         
27810         var contextEl = canvasEl.getContext("2d");
27811         
27812         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27813         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27814         var center = this.imageEl.OriginWidth / 2;
27815         
27816         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27817             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27818             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27819             center = this.imageEl.OriginHeight / 2;
27820         }
27821         
27822         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27823         
27824         contextEl.translate(center, center);
27825         contextEl.rotate(this.rotate * Math.PI / 180);
27826
27827         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27828         
27829         this.canvasEl = document.createElement("canvas");
27830         
27831         this.contextEl = this.canvasEl.getContext("2d");
27832         
27833         switch (this.rotate) {
27834             case 0 :
27835                 
27836                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27837                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27838                 
27839                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27840                 
27841                 break;
27842             case 90 : 
27843                 
27844                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27845                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27846                 
27847                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27848                     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);
27849                     break;
27850                 }
27851                 
27852                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27853                 
27854                 break;
27855             case 180 :
27856                 
27857                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27858                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27859                 
27860                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27861                     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);
27862                     break;
27863                 }
27864                 
27865                 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);
27866                 
27867                 break;
27868             case 270 :
27869                 
27870                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27871                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27872         
27873                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27874                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27875                     break;
27876                 }
27877                 
27878                 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);
27879                 
27880                 break;
27881             default : 
27882                 break;
27883         }
27884         
27885         this.previewEl.appendChild(this.canvasEl);
27886         
27887         this.setCanvasPosition();
27888     },
27889     
27890     crop : function()
27891     {
27892         if(!this.canvasLoaded){
27893             return;
27894         }
27895         
27896         var imageCanvas = document.createElement("canvas");
27897         
27898         var imageContext = imageCanvas.getContext("2d");
27899         
27900         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27901         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27902         
27903         var center = imageCanvas.width / 2;
27904         
27905         imageContext.translate(center, center);
27906         
27907         imageContext.rotate(this.rotate * Math.PI / 180);
27908         
27909         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27910         
27911         var canvas = document.createElement("canvas");
27912         
27913         var context = canvas.getContext("2d");
27914                 
27915         canvas.width = this.minWidth;
27916         canvas.height = this.minHeight;
27917
27918         switch (this.rotate) {
27919             case 0 :
27920                 
27921                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27922                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27923                 
27924                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27925                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27926                 
27927                 var targetWidth = this.minWidth - 2 * x;
27928                 var targetHeight = this.minHeight - 2 * y;
27929                 
27930                 var scale = 1;
27931                 
27932                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27933                     scale = targetWidth / width;
27934                 }
27935                 
27936                 if(x > 0 && y == 0){
27937                     scale = targetHeight / height;
27938                 }
27939                 
27940                 if(x > 0 && y > 0){
27941                     scale = targetWidth / width;
27942                     
27943                     if(width < height){
27944                         scale = targetHeight / height;
27945                     }
27946                 }
27947                 
27948                 context.scale(scale, scale);
27949                 
27950                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27951                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27952
27953                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27954                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27955
27956                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27957                 
27958                 break;
27959             case 90 : 
27960                 
27961                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27962                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27963                 
27964                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27965                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27966                 
27967                 var targetWidth = this.minWidth - 2 * x;
27968                 var targetHeight = this.minHeight - 2 * y;
27969                 
27970                 var scale = 1;
27971                 
27972                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27973                     scale = targetWidth / width;
27974                 }
27975                 
27976                 if(x > 0 && y == 0){
27977                     scale = targetHeight / height;
27978                 }
27979                 
27980                 if(x > 0 && y > 0){
27981                     scale = targetWidth / width;
27982                     
27983                     if(width < height){
27984                         scale = targetHeight / height;
27985                     }
27986                 }
27987                 
27988                 context.scale(scale, scale);
27989                 
27990                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27991                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27992
27993                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27994                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27995                 
27996                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27997                 
27998                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27999                 
28000                 break;
28001             case 180 :
28002                 
28003                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28004                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28005                 
28006                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28007                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28008                 
28009                 var targetWidth = this.minWidth - 2 * x;
28010                 var targetHeight = this.minHeight - 2 * y;
28011                 
28012                 var scale = 1;
28013                 
28014                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28015                     scale = targetWidth / width;
28016                 }
28017                 
28018                 if(x > 0 && y == 0){
28019                     scale = targetHeight / height;
28020                 }
28021                 
28022                 if(x > 0 && y > 0){
28023                     scale = targetWidth / width;
28024                     
28025                     if(width < height){
28026                         scale = targetHeight / height;
28027                     }
28028                 }
28029                 
28030                 context.scale(scale, scale);
28031                 
28032                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28033                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28034
28035                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28036                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28037
28038                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28039                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28040                 
28041                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28042                 
28043                 break;
28044             case 270 :
28045                 
28046                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28047                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28048                 
28049                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28050                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28051                 
28052                 var targetWidth = this.minWidth - 2 * x;
28053                 var targetHeight = this.minHeight - 2 * y;
28054                 
28055                 var scale = 1;
28056                 
28057                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28058                     scale = targetWidth / width;
28059                 }
28060                 
28061                 if(x > 0 && y == 0){
28062                     scale = targetHeight / height;
28063                 }
28064                 
28065                 if(x > 0 && y > 0){
28066                     scale = targetWidth / width;
28067                     
28068                     if(width < height){
28069                         scale = targetHeight / height;
28070                     }
28071                 }
28072                 
28073                 context.scale(scale, scale);
28074                 
28075                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28076                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28077
28078                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28079                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28080                 
28081                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28082                 
28083                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28084                 
28085                 break;
28086             default : 
28087                 break;
28088         }
28089         
28090         this.cropData = canvas.toDataURL(this.cropType);
28091         
28092         if(this.fireEvent('crop', this, this.cropData) !== false){
28093             this.process(this.file, this.cropData);
28094         }
28095         
28096         return;
28097         
28098     },
28099     
28100     setThumbBoxSize : function()
28101     {
28102         var width, height;
28103         
28104         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28105             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28106             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28107             
28108             this.minWidth = width;
28109             this.minHeight = height;
28110             
28111             if(this.rotate == 90 || this.rotate == 270){
28112                 this.minWidth = height;
28113                 this.minHeight = width;
28114             }
28115         }
28116         
28117         height = 300;
28118         width = Math.ceil(this.minWidth * height / this.minHeight);
28119         
28120         if(this.minWidth > this.minHeight){
28121             width = 300;
28122             height = Math.ceil(this.minHeight * width / this.minWidth);
28123         }
28124         
28125         this.thumbEl.setStyle({
28126             width : width + 'px',
28127             height : height + 'px'
28128         });
28129
28130         return;
28131             
28132     },
28133     
28134     setThumbBoxPosition : function()
28135     {
28136         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28137         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28138         
28139         this.thumbEl.setLeft(x);
28140         this.thumbEl.setTop(y);
28141         
28142     },
28143     
28144     baseRotateLevel : function()
28145     {
28146         this.baseRotate = 1;
28147         
28148         if(
28149                 typeof(this.exif) != 'undefined' &&
28150                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28151                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28152         ){
28153             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28154         }
28155         
28156         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28157         
28158     },
28159     
28160     baseScaleLevel : function()
28161     {
28162         var width, height;
28163         
28164         if(this.isDocument){
28165             
28166             if(this.baseRotate == 6 || this.baseRotate == 8){
28167             
28168                 height = this.thumbEl.getHeight();
28169                 this.baseScale = height / this.imageEl.OriginWidth;
28170
28171                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28172                     width = this.thumbEl.getWidth();
28173                     this.baseScale = width / this.imageEl.OriginHeight;
28174                 }
28175
28176                 return;
28177             }
28178
28179             height = this.thumbEl.getHeight();
28180             this.baseScale = height / this.imageEl.OriginHeight;
28181
28182             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28183                 width = this.thumbEl.getWidth();
28184                 this.baseScale = width / this.imageEl.OriginWidth;
28185             }
28186
28187             return;
28188         }
28189         
28190         if(this.baseRotate == 6 || this.baseRotate == 8){
28191             
28192             width = this.thumbEl.getHeight();
28193             this.baseScale = width / this.imageEl.OriginHeight;
28194             
28195             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28196                 height = this.thumbEl.getWidth();
28197                 this.baseScale = height / this.imageEl.OriginHeight;
28198             }
28199             
28200             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28201                 height = this.thumbEl.getWidth();
28202                 this.baseScale = height / this.imageEl.OriginHeight;
28203                 
28204                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28205                     width = this.thumbEl.getHeight();
28206                     this.baseScale = width / this.imageEl.OriginWidth;
28207                 }
28208             }
28209             
28210             return;
28211         }
28212         
28213         width = this.thumbEl.getWidth();
28214         this.baseScale = width / this.imageEl.OriginWidth;
28215         
28216         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28217             height = this.thumbEl.getHeight();
28218             this.baseScale = height / this.imageEl.OriginHeight;
28219         }
28220         
28221         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28222             
28223             height = this.thumbEl.getHeight();
28224             this.baseScale = height / this.imageEl.OriginHeight;
28225             
28226             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28227                 width = this.thumbEl.getWidth();
28228                 this.baseScale = width / this.imageEl.OriginWidth;
28229             }
28230             
28231         }
28232         
28233         return;
28234     },
28235     
28236     getScaleLevel : function()
28237     {
28238         return this.baseScale * Math.pow(1.1, this.scale);
28239     },
28240     
28241     onTouchStart : function(e)
28242     {
28243         if(!this.canvasLoaded){
28244             this.beforeSelectFile(e);
28245             return;
28246         }
28247         
28248         var touches = e.browserEvent.touches;
28249         
28250         if(!touches){
28251             return;
28252         }
28253         
28254         if(touches.length == 1){
28255             this.onMouseDown(e);
28256             return;
28257         }
28258         
28259         if(touches.length != 2){
28260             return;
28261         }
28262         
28263         var coords = [];
28264         
28265         for(var i = 0, finger; finger = touches[i]; i++){
28266             coords.push(finger.pageX, finger.pageY);
28267         }
28268         
28269         var x = Math.pow(coords[0] - coords[2], 2);
28270         var y = Math.pow(coords[1] - coords[3], 2);
28271         
28272         this.startDistance = Math.sqrt(x + y);
28273         
28274         this.startScale = this.scale;
28275         
28276         this.pinching = true;
28277         this.dragable = false;
28278         
28279     },
28280     
28281     onTouchMove : function(e)
28282     {
28283         if(!this.pinching && !this.dragable){
28284             return;
28285         }
28286         
28287         var touches = e.browserEvent.touches;
28288         
28289         if(!touches){
28290             return;
28291         }
28292         
28293         if(this.dragable){
28294             this.onMouseMove(e);
28295             return;
28296         }
28297         
28298         var coords = [];
28299         
28300         for(var i = 0, finger; finger = touches[i]; i++){
28301             coords.push(finger.pageX, finger.pageY);
28302         }
28303         
28304         var x = Math.pow(coords[0] - coords[2], 2);
28305         var y = Math.pow(coords[1] - coords[3], 2);
28306         
28307         this.endDistance = Math.sqrt(x + y);
28308         
28309         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28310         
28311         if(!this.zoomable()){
28312             this.scale = this.startScale;
28313             return;
28314         }
28315         
28316         this.draw();
28317         
28318     },
28319     
28320     onTouchEnd : function(e)
28321     {
28322         this.pinching = false;
28323         this.dragable = false;
28324         
28325     },
28326     
28327     process : function(file, crop)
28328     {
28329         if(this.loadMask){
28330             this.maskEl.mask(this.loadingText);
28331         }
28332         
28333         this.xhr = new XMLHttpRequest();
28334         
28335         file.xhr = this.xhr;
28336
28337         this.xhr.open(this.method, this.url, true);
28338         
28339         var headers = {
28340             "Accept": "application/json",
28341             "Cache-Control": "no-cache",
28342             "X-Requested-With": "XMLHttpRequest"
28343         };
28344         
28345         for (var headerName in headers) {
28346             var headerValue = headers[headerName];
28347             if (headerValue) {
28348                 this.xhr.setRequestHeader(headerName, headerValue);
28349             }
28350         }
28351         
28352         var _this = this;
28353         
28354         this.xhr.onload = function()
28355         {
28356             _this.xhrOnLoad(_this.xhr);
28357         }
28358         
28359         this.xhr.onerror = function()
28360         {
28361             _this.xhrOnError(_this.xhr);
28362         }
28363         
28364         var formData = new FormData();
28365
28366         formData.append('returnHTML', 'NO');
28367         
28368         if(crop){
28369             formData.append('crop', crop);
28370         }
28371         
28372         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28373             formData.append(this.paramName, file, file.name);
28374         }
28375         
28376         if(typeof(file.filename) != 'undefined'){
28377             formData.append('filename', file.filename);
28378         }
28379         
28380         if(typeof(file.mimetype) != 'undefined'){
28381             formData.append('mimetype', file.mimetype);
28382         }
28383         
28384         if(this.fireEvent('arrange', this, formData) != false){
28385             this.xhr.send(formData);
28386         };
28387     },
28388     
28389     xhrOnLoad : function(xhr)
28390     {
28391         if(this.loadMask){
28392             this.maskEl.unmask();
28393         }
28394         
28395         if (xhr.readyState !== 4) {
28396             this.fireEvent('exception', this, xhr);
28397             return;
28398         }
28399
28400         var response = Roo.decode(xhr.responseText);
28401         
28402         if(!response.success){
28403             this.fireEvent('exception', this, xhr);
28404             return;
28405         }
28406         
28407         var response = Roo.decode(xhr.responseText);
28408         
28409         this.fireEvent('upload', this, response);
28410         
28411     },
28412     
28413     xhrOnError : function()
28414     {
28415         if(this.loadMask){
28416             this.maskEl.unmask();
28417         }
28418         
28419         Roo.log('xhr on error');
28420         
28421         var response = Roo.decode(xhr.responseText);
28422           
28423         Roo.log(response);
28424         
28425     },
28426     
28427     prepare : function(file)
28428     {   
28429         if(this.loadMask){
28430             this.maskEl.mask(this.loadingText);
28431         }
28432         
28433         this.file = false;
28434         this.exif = {};
28435         
28436         if(typeof(file) === 'string'){
28437             this.loadCanvas(file);
28438             return;
28439         }
28440         
28441         if(!file || !this.urlAPI){
28442             return;
28443         }
28444         
28445         this.file = file;
28446         this.cropType = file.type;
28447         
28448         var _this = this;
28449         
28450         if(this.fireEvent('prepare', this, this.file) != false){
28451             
28452             var reader = new FileReader();
28453             
28454             reader.onload = function (e) {
28455                 if (e.target.error) {
28456                     Roo.log(e.target.error);
28457                     return;
28458                 }
28459                 
28460                 var buffer = e.target.result,
28461                     dataView = new DataView(buffer),
28462                     offset = 2,
28463                     maxOffset = dataView.byteLength - 4,
28464                     markerBytes,
28465                     markerLength;
28466                 
28467                 if (dataView.getUint16(0) === 0xffd8) {
28468                     while (offset < maxOffset) {
28469                         markerBytes = dataView.getUint16(offset);
28470                         
28471                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28472                             markerLength = dataView.getUint16(offset + 2) + 2;
28473                             if (offset + markerLength > dataView.byteLength) {
28474                                 Roo.log('Invalid meta data: Invalid segment size.');
28475                                 break;
28476                             }
28477                             
28478                             if(markerBytes == 0xffe1){
28479                                 _this.parseExifData(
28480                                     dataView,
28481                                     offset,
28482                                     markerLength
28483                                 );
28484                             }
28485                             
28486                             offset += markerLength;
28487                             
28488                             continue;
28489                         }
28490                         
28491                         break;
28492                     }
28493                     
28494                 }
28495                 
28496                 var url = _this.urlAPI.createObjectURL(_this.file);
28497                 
28498                 _this.loadCanvas(url);
28499                 
28500                 return;
28501             }
28502             
28503             reader.readAsArrayBuffer(this.file);
28504             
28505         }
28506         
28507     },
28508     
28509     parseExifData : function(dataView, offset, length)
28510     {
28511         var tiffOffset = offset + 10,
28512             littleEndian,
28513             dirOffset;
28514     
28515         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28516             // No Exif data, might be XMP data instead
28517             return;
28518         }
28519         
28520         // Check for the ASCII code for "Exif" (0x45786966):
28521         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28522             // No Exif data, might be XMP data instead
28523             return;
28524         }
28525         if (tiffOffset + 8 > dataView.byteLength) {
28526             Roo.log('Invalid Exif data: Invalid segment size.');
28527             return;
28528         }
28529         // Check for the two null bytes:
28530         if (dataView.getUint16(offset + 8) !== 0x0000) {
28531             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28532             return;
28533         }
28534         // Check the byte alignment:
28535         switch (dataView.getUint16(tiffOffset)) {
28536         case 0x4949:
28537             littleEndian = true;
28538             break;
28539         case 0x4D4D:
28540             littleEndian = false;
28541             break;
28542         default:
28543             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28544             return;
28545         }
28546         // Check for the TIFF tag marker (0x002A):
28547         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28548             Roo.log('Invalid Exif data: Missing TIFF marker.');
28549             return;
28550         }
28551         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28552         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28553         
28554         this.parseExifTags(
28555             dataView,
28556             tiffOffset,
28557             tiffOffset + dirOffset,
28558             littleEndian
28559         );
28560     },
28561     
28562     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28563     {
28564         var tagsNumber,
28565             dirEndOffset,
28566             i;
28567         if (dirOffset + 6 > dataView.byteLength) {
28568             Roo.log('Invalid Exif data: Invalid directory offset.');
28569             return;
28570         }
28571         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28572         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28573         if (dirEndOffset + 4 > dataView.byteLength) {
28574             Roo.log('Invalid Exif data: Invalid directory size.');
28575             return;
28576         }
28577         for (i = 0; i < tagsNumber; i += 1) {
28578             this.parseExifTag(
28579                 dataView,
28580                 tiffOffset,
28581                 dirOffset + 2 + 12 * i, // tag offset
28582                 littleEndian
28583             );
28584         }
28585         // Return the offset to the next directory:
28586         return dataView.getUint32(dirEndOffset, littleEndian);
28587     },
28588     
28589     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28590     {
28591         var tag = dataView.getUint16(offset, littleEndian);
28592         
28593         this.exif[tag] = this.getExifValue(
28594             dataView,
28595             tiffOffset,
28596             offset,
28597             dataView.getUint16(offset + 2, littleEndian), // tag type
28598             dataView.getUint32(offset + 4, littleEndian), // tag length
28599             littleEndian
28600         );
28601     },
28602     
28603     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28604     {
28605         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28606             tagSize,
28607             dataOffset,
28608             values,
28609             i,
28610             str,
28611             c;
28612     
28613         if (!tagType) {
28614             Roo.log('Invalid Exif data: Invalid tag type.');
28615             return;
28616         }
28617         
28618         tagSize = tagType.size * length;
28619         // Determine if the value is contained in the dataOffset bytes,
28620         // or if the value at the dataOffset is a pointer to the actual data:
28621         dataOffset = tagSize > 4 ?
28622                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28623         if (dataOffset + tagSize > dataView.byteLength) {
28624             Roo.log('Invalid Exif data: Invalid data offset.');
28625             return;
28626         }
28627         if (length === 1) {
28628             return tagType.getValue(dataView, dataOffset, littleEndian);
28629         }
28630         values = [];
28631         for (i = 0; i < length; i += 1) {
28632             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28633         }
28634         
28635         if (tagType.ascii) {
28636             str = '';
28637             // Concatenate the chars:
28638             for (i = 0; i < values.length; i += 1) {
28639                 c = values[i];
28640                 // Ignore the terminating NULL byte(s):
28641                 if (c === '\u0000') {
28642                     break;
28643                 }
28644                 str += c;
28645             }
28646             return str;
28647         }
28648         return values;
28649     }
28650     
28651 });
28652
28653 Roo.apply(Roo.bootstrap.UploadCropbox, {
28654     tags : {
28655         'Orientation': 0x0112
28656     },
28657     
28658     Orientation: {
28659             1: 0, //'top-left',
28660 //            2: 'top-right',
28661             3: 180, //'bottom-right',
28662 //            4: 'bottom-left',
28663 //            5: 'left-top',
28664             6: 90, //'right-top',
28665 //            7: 'right-bottom',
28666             8: 270 //'left-bottom'
28667     },
28668     
28669     exifTagTypes : {
28670         // byte, 8-bit unsigned int:
28671         1: {
28672             getValue: function (dataView, dataOffset) {
28673                 return dataView.getUint8(dataOffset);
28674             },
28675             size: 1
28676         },
28677         // ascii, 8-bit byte:
28678         2: {
28679             getValue: function (dataView, dataOffset) {
28680                 return String.fromCharCode(dataView.getUint8(dataOffset));
28681             },
28682             size: 1,
28683             ascii: true
28684         },
28685         // short, 16 bit int:
28686         3: {
28687             getValue: function (dataView, dataOffset, littleEndian) {
28688                 return dataView.getUint16(dataOffset, littleEndian);
28689             },
28690             size: 2
28691         },
28692         // long, 32 bit int:
28693         4: {
28694             getValue: function (dataView, dataOffset, littleEndian) {
28695                 return dataView.getUint32(dataOffset, littleEndian);
28696             },
28697             size: 4
28698         },
28699         // rational = two long values, first is numerator, second is denominator:
28700         5: {
28701             getValue: function (dataView, dataOffset, littleEndian) {
28702                 return dataView.getUint32(dataOffset, littleEndian) /
28703                     dataView.getUint32(dataOffset + 4, littleEndian);
28704             },
28705             size: 8
28706         },
28707         // slong, 32 bit signed int:
28708         9: {
28709             getValue: function (dataView, dataOffset, littleEndian) {
28710                 return dataView.getInt32(dataOffset, littleEndian);
28711             },
28712             size: 4
28713         },
28714         // srational, two slongs, first is numerator, second is denominator:
28715         10: {
28716             getValue: function (dataView, dataOffset, littleEndian) {
28717                 return dataView.getInt32(dataOffset, littleEndian) /
28718                     dataView.getInt32(dataOffset + 4, littleEndian);
28719             },
28720             size: 8
28721         }
28722     },
28723     
28724     footer : {
28725         STANDARD : [
28726             {
28727                 tag : 'div',
28728                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28729                 action : 'rotate-left',
28730                 cn : [
28731                     {
28732                         tag : 'button',
28733                         cls : 'btn btn-default',
28734                         html : '<i class="fa fa-undo"></i>'
28735                     }
28736                 ]
28737             },
28738             {
28739                 tag : 'div',
28740                 cls : 'btn-group roo-upload-cropbox-picture',
28741                 action : 'picture',
28742                 cn : [
28743                     {
28744                         tag : 'button',
28745                         cls : 'btn btn-default',
28746                         html : '<i class="fa fa-picture-o"></i>'
28747                     }
28748                 ]
28749             },
28750             {
28751                 tag : 'div',
28752                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28753                 action : 'rotate-right',
28754                 cn : [
28755                     {
28756                         tag : 'button',
28757                         cls : 'btn btn-default',
28758                         html : '<i class="fa fa-repeat"></i>'
28759                     }
28760                 ]
28761             }
28762         ],
28763         DOCUMENT : [
28764             {
28765                 tag : 'div',
28766                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28767                 action : 'rotate-left',
28768                 cn : [
28769                     {
28770                         tag : 'button',
28771                         cls : 'btn btn-default',
28772                         html : '<i class="fa fa-undo"></i>'
28773                     }
28774                 ]
28775             },
28776             {
28777                 tag : 'div',
28778                 cls : 'btn-group roo-upload-cropbox-download',
28779                 action : 'download',
28780                 cn : [
28781                     {
28782                         tag : 'button',
28783                         cls : 'btn btn-default',
28784                         html : '<i class="fa fa-download"></i>'
28785                     }
28786                 ]
28787             },
28788             {
28789                 tag : 'div',
28790                 cls : 'btn-group roo-upload-cropbox-crop',
28791                 action : 'crop',
28792                 cn : [
28793                     {
28794                         tag : 'button',
28795                         cls : 'btn btn-default',
28796                         html : '<i class="fa fa-crop"></i>'
28797                     }
28798                 ]
28799             },
28800             {
28801                 tag : 'div',
28802                 cls : 'btn-group roo-upload-cropbox-trash',
28803                 action : 'trash',
28804                 cn : [
28805                     {
28806                         tag : 'button',
28807                         cls : 'btn btn-default',
28808                         html : '<i class="fa fa-trash"></i>'
28809                     }
28810                 ]
28811             },
28812             {
28813                 tag : 'div',
28814                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28815                 action : 'rotate-right',
28816                 cn : [
28817                     {
28818                         tag : 'button',
28819                         cls : 'btn btn-default',
28820                         html : '<i class="fa fa-repeat"></i>'
28821                     }
28822                 ]
28823             }
28824         ],
28825         ROTATOR : [
28826             {
28827                 tag : 'div',
28828                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28829                 action : 'rotate-left',
28830                 cn : [
28831                     {
28832                         tag : 'button',
28833                         cls : 'btn btn-default',
28834                         html : '<i class="fa fa-undo"></i>'
28835                     }
28836                 ]
28837             },
28838             {
28839                 tag : 'div',
28840                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28841                 action : 'rotate-right',
28842                 cn : [
28843                     {
28844                         tag : 'button',
28845                         cls : 'btn btn-default',
28846                         html : '<i class="fa fa-repeat"></i>'
28847                     }
28848                 ]
28849             }
28850         ]
28851     }
28852 });
28853
28854 /*
28855 * Licence: LGPL
28856 */
28857
28858 /**
28859  * @class Roo.bootstrap.DocumentManager
28860  * @extends Roo.bootstrap.Component
28861  * Bootstrap DocumentManager class
28862  * @cfg {String} paramName default 'imageUpload'
28863  * @cfg {String} toolTipName default 'filename'
28864  * @cfg {String} method default POST
28865  * @cfg {String} url action url
28866  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28867  * @cfg {Boolean} multiple multiple upload default true
28868  * @cfg {Number} thumbSize default 300
28869  * @cfg {String} fieldLabel
28870  * @cfg {Number} labelWidth default 4
28871  * @cfg {String} labelAlign (left|top) default left
28872  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28873 * @cfg {Number} labellg set the width of label (1-12)
28874  * @cfg {Number} labelmd set the width of label (1-12)
28875  * @cfg {Number} labelsm set the width of label (1-12)
28876  * @cfg {Number} labelxs set the width of label (1-12)
28877  * 
28878  * @constructor
28879  * Create a new DocumentManager
28880  * @param {Object} config The config object
28881  */
28882
28883 Roo.bootstrap.DocumentManager = function(config){
28884     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28885     
28886     this.files = [];
28887     this.delegates = [];
28888     
28889     this.addEvents({
28890         /**
28891          * @event initial
28892          * Fire when initial the DocumentManager
28893          * @param {Roo.bootstrap.DocumentManager} this
28894          */
28895         "initial" : true,
28896         /**
28897          * @event inspect
28898          * inspect selected file
28899          * @param {Roo.bootstrap.DocumentManager} this
28900          * @param {File} file
28901          */
28902         "inspect" : true,
28903         /**
28904          * @event exception
28905          * Fire when xhr load exception
28906          * @param {Roo.bootstrap.DocumentManager} this
28907          * @param {XMLHttpRequest} xhr
28908          */
28909         "exception" : true,
28910         /**
28911          * @event afterupload
28912          * Fire when xhr load exception
28913          * @param {Roo.bootstrap.DocumentManager} this
28914          * @param {XMLHttpRequest} xhr
28915          */
28916         "afterupload" : true,
28917         /**
28918          * @event prepare
28919          * prepare the form data
28920          * @param {Roo.bootstrap.DocumentManager} this
28921          * @param {Object} formData
28922          */
28923         "prepare" : true,
28924         /**
28925          * @event remove
28926          * Fire when remove the file
28927          * @param {Roo.bootstrap.DocumentManager} this
28928          * @param {Object} file
28929          */
28930         "remove" : true,
28931         /**
28932          * @event refresh
28933          * Fire after refresh the file
28934          * @param {Roo.bootstrap.DocumentManager} this
28935          */
28936         "refresh" : true,
28937         /**
28938          * @event click
28939          * Fire after click the image
28940          * @param {Roo.bootstrap.DocumentManager} this
28941          * @param {Object} file
28942          */
28943         "click" : true,
28944         /**
28945          * @event edit
28946          * Fire when upload a image and editable set to true
28947          * @param {Roo.bootstrap.DocumentManager} this
28948          * @param {Object} file
28949          */
28950         "edit" : true,
28951         /**
28952          * @event beforeselectfile
28953          * Fire before select file
28954          * @param {Roo.bootstrap.DocumentManager} this
28955          */
28956         "beforeselectfile" : true,
28957         /**
28958          * @event process
28959          * Fire before process file
28960          * @param {Roo.bootstrap.DocumentManager} this
28961          * @param {Object} file
28962          */
28963         "process" : true,
28964         /**
28965          * @event previewrendered
28966          * Fire when preview rendered
28967          * @param {Roo.bootstrap.DocumentManager} this
28968          * @param {Object} file
28969          */
28970         "previewrendered" : true,
28971         /**
28972          */
28973         "previewResize" : true
28974         
28975     });
28976 };
28977
28978 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28979     
28980     boxes : 0,
28981     inputName : '',
28982     thumbSize : 300,
28983     multiple : true,
28984     files : false,
28985     method : 'POST',
28986     url : '',
28987     paramName : 'imageUpload',
28988     toolTipName : 'filename',
28989     fieldLabel : '',
28990     labelWidth : 4,
28991     labelAlign : 'left',
28992     editable : true,
28993     delegates : false,
28994     xhr : false, 
28995     
28996     labellg : 0,
28997     labelmd : 0,
28998     labelsm : 0,
28999     labelxs : 0,
29000     
29001     getAutoCreate : function()
29002     {   
29003         var managerWidget = {
29004             tag : 'div',
29005             cls : 'roo-document-manager',
29006             cn : [
29007                 {
29008                     tag : 'input',
29009                     cls : 'roo-document-manager-selector',
29010                     type : 'file'
29011                 },
29012                 {
29013                     tag : 'div',
29014                     cls : 'roo-document-manager-uploader',
29015                     cn : [
29016                         {
29017                             tag : 'div',
29018                             cls : 'roo-document-manager-upload-btn',
29019                             html : '<i class="fa fa-plus"></i>'
29020                         }
29021                     ]
29022                     
29023                 }
29024             ]
29025         };
29026         
29027         var content = [
29028             {
29029                 tag : 'div',
29030                 cls : 'column col-md-12',
29031                 cn : managerWidget
29032             }
29033         ];
29034         
29035         if(this.fieldLabel.length){
29036             
29037             content = [
29038                 {
29039                     tag : 'div',
29040                     cls : 'column col-md-12',
29041                     html : this.fieldLabel
29042                 },
29043                 {
29044                     tag : 'div',
29045                     cls : 'column col-md-12',
29046                     cn : managerWidget
29047                 }
29048             ];
29049
29050             if(this.labelAlign == 'left'){
29051                 content = [
29052                     {
29053                         tag : 'div',
29054                         cls : 'column',
29055                         html : this.fieldLabel
29056                     },
29057                     {
29058                         tag : 'div',
29059                         cls : 'column',
29060                         cn : managerWidget
29061                     }
29062                 ];
29063                 
29064                 if(this.labelWidth > 12){
29065                     content[0].style = "width: " + this.labelWidth + 'px';
29066                 }
29067
29068                 if(this.labelWidth < 13 && this.labelmd == 0){
29069                     this.labelmd = this.labelWidth;
29070                 }
29071
29072                 if(this.labellg > 0){
29073                     content[0].cls += ' col-lg-' + this.labellg;
29074                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29075                 }
29076
29077                 if(this.labelmd > 0){
29078                     content[0].cls += ' col-md-' + this.labelmd;
29079                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29080                 }
29081
29082                 if(this.labelsm > 0){
29083                     content[0].cls += ' col-sm-' + this.labelsm;
29084                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29085                 }
29086
29087                 if(this.labelxs > 0){
29088                     content[0].cls += ' col-xs-' + this.labelxs;
29089                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29090                 }
29091                 
29092             }
29093         }
29094         
29095         var cfg = {
29096             tag : 'div',
29097             cls : 'row clearfix',
29098             cn : content
29099         };
29100         
29101         return cfg;
29102         
29103     },
29104     
29105     initEvents : function()
29106     {
29107         this.managerEl = this.el.select('.roo-document-manager', true).first();
29108         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29109         
29110         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29111         this.selectorEl.hide();
29112         
29113         if(this.multiple){
29114             this.selectorEl.attr('multiple', 'multiple');
29115         }
29116         
29117         this.selectorEl.on('change', this.onFileSelected, this);
29118         
29119         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29120         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29121         
29122         this.uploader.on('click', this.onUploaderClick, this);
29123         
29124         this.renderProgressDialog();
29125         
29126         var _this = this;
29127         
29128         window.addEventListener("resize", function() { _this.refresh(); } );
29129         
29130         this.fireEvent('initial', this);
29131     },
29132     
29133     renderProgressDialog : function()
29134     {
29135         var _this = this;
29136         
29137         this.progressDialog = new Roo.bootstrap.Modal({
29138             cls : 'roo-document-manager-progress-dialog',
29139             allow_close : false,
29140             title : '',
29141             buttons : [
29142                 {
29143                     name  :'cancel',
29144                     weight : 'danger',
29145                     html : 'Cancel'
29146                 }
29147             ], 
29148             listeners : { 
29149                 btnclick : function() {
29150                     _this.uploadCancel();
29151                     this.hide();
29152                 }
29153             }
29154         });
29155          
29156         this.progressDialog.render(Roo.get(document.body));
29157          
29158         this.progress = new Roo.bootstrap.Progress({
29159             cls : 'roo-document-manager-progress',
29160             active : true,
29161             striped : true
29162         });
29163         
29164         this.progress.render(this.progressDialog.getChildContainer());
29165         
29166         this.progressBar = new Roo.bootstrap.ProgressBar({
29167             cls : 'roo-document-manager-progress-bar',
29168             aria_valuenow : 0,
29169             aria_valuemin : 0,
29170             aria_valuemax : 12,
29171             panel : 'success'
29172         });
29173         
29174         this.progressBar.render(this.progress.getChildContainer());
29175     },
29176     
29177     onUploaderClick : function(e)
29178     {
29179         e.preventDefault();
29180      
29181         if(this.fireEvent('beforeselectfile', this) != false){
29182             this.selectorEl.dom.click();
29183         }
29184         
29185     },
29186     
29187     onFileSelected : function(e)
29188     {
29189         e.preventDefault();
29190         
29191         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29192             return;
29193         }
29194         
29195         Roo.each(this.selectorEl.dom.files, function(file){
29196             if(this.fireEvent('inspect', this, file) != false){
29197                 this.files.push(file);
29198             }
29199         }, this);
29200         
29201         this.queue();
29202         
29203     },
29204     
29205     queue : function()
29206     {
29207         this.selectorEl.dom.value = '';
29208         
29209         if(!this.files || !this.files.length){
29210             return;
29211         }
29212         
29213         if(this.boxes > 0 && this.files.length > this.boxes){
29214             this.files = this.files.slice(0, this.boxes);
29215         }
29216         
29217         this.uploader.show();
29218         
29219         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29220             this.uploader.hide();
29221         }
29222         
29223         var _this = this;
29224         
29225         var files = [];
29226         
29227         var docs = [];
29228         
29229         Roo.each(this.files, function(file){
29230             
29231             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29232                 var f = this.renderPreview(file);
29233                 files.push(f);
29234                 return;
29235             }
29236             
29237             if(file.type.indexOf('image') != -1){
29238                 this.delegates.push(
29239                     (function(){
29240                         _this.process(file);
29241                     }).createDelegate(this)
29242                 );
29243         
29244                 return;
29245             }
29246             
29247             docs.push(
29248                 (function(){
29249                     _this.process(file);
29250                 }).createDelegate(this)
29251             );
29252             
29253         }, this);
29254         
29255         this.files = files;
29256         
29257         this.delegates = this.delegates.concat(docs);
29258         
29259         if(!this.delegates.length){
29260             this.refresh();
29261             return;
29262         }
29263         
29264         this.progressBar.aria_valuemax = this.delegates.length;
29265         
29266         this.arrange();
29267         
29268         return;
29269     },
29270     
29271     arrange : function()
29272     {
29273         if(!this.delegates.length){
29274             this.progressDialog.hide();
29275             this.refresh();
29276             return;
29277         }
29278         
29279         var delegate = this.delegates.shift();
29280         
29281         this.progressDialog.show();
29282         
29283         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29284         
29285         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29286         
29287         delegate();
29288     },
29289     
29290     refresh : function()
29291     {
29292         this.uploader.show();
29293         
29294         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29295             this.uploader.hide();
29296         }
29297         
29298         Roo.isTouch ? this.closable(false) : this.closable(true);
29299         
29300         this.fireEvent('refresh', this);
29301     },
29302     
29303     onRemove : function(e, el, o)
29304     {
29305         e.preventDefault();
29306         
29307         this.fireEvent('remove', this, o);
29308         
29309     },
29310     
29311     remove : function(o)
29312     {
29313         var files = [];
29314         
29315         Roo.each(this.files, function(file){
29316             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29317                 files.push(file);
29318                 return;
29319             }
29320
29321             o.target.remove();
29322
29323         }, this);
29324         
29325         this.files = files;
29326         
29327         this.refresh();
29328     },
29329     
29330     clear : function()
29331     {
29332         Roo.each(this.files, function(file){
29333             if(!file.target){
29334                 return;
29335             }
29336             
29337             file.target.remove();
29338
29339         }, this);
29340         
29341         this.files = [];
29342         
29343         this.refresh();
29344     },
29345     
29346     onClick : function(e, el, o)
29347     {
29348         e.preventDefault();
29349         
29350         this.fireEvent('click', this, o);
29351         
29352     },
29353     
29354     closable : function(closable)
29355     {
29356         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29357             
29358             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29359             
29360             if(closable){
29361                 el.show();
29362                 return;
29363             }
29364             
29365             el.hide();
29366             
29367         }, this);
29368     },
29369     
29370     xhrOnLoad : function(xhr)
29371     {
29372         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29373             el.remove();
29374         }, this);
29375         
29376         if (xhr.readyState !== 4) {
29377             this.arrange();
29378             this.fireEvent('exception', this, xhr);
29379             return;
29380         }
29381
29382         var response = Roo.decode(xhr.responseText);
29383         
29384         if(!response.success){
29385             this.arrange();
29386             this.fireEvent('exception', this, xhr);
29387             return;
29388         }
29389         
29390         var file = this.renderPreview(response.data);
29391         
29392         this.files.push(file);
29393         
29394         this.arrange();
29395         
29396         this.fireEvent('afterupload', this, xhr);
29397         
29398     },
29399     
29400     xhrOnError : function(xhr)
29401     {
29402         Roo.log('xhr on error');
29403         
29404         var response = Roo.decode(xhr.responseText);
29405           
29406         Roo.log(response);
29407         
29408         this.arrange();
29409     },
29410     
29411     process : function(file)
29412     {
29413         if(this.fireEvent('process', this, file) !== false){
29414             if(this.editable && file.type.indexOf('image') != -1){
29415                 this.fireEvent('edit', this, file);
29416                 return;
29417             }
29418
29419             this.uploadStart(file, false);
29420
29421             return;
29422         }
29423         
29424     },
29425     
29426     uploadStart : function(file, crop)
29427     {
29428         this.xhr = new XMLHttpRequest();
29429         
29430         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29431             this.arrange();
29432             return;
29433         }
29434         
29435         file.xhr = this.xhr;
29436             
29437         this.managerEl.createChild({
29438             tag : 'div',
29439             cls : 'roo-document-manager-loading',
29440             cn : [
29441                 {
29442                     tag : 'div',
29443                     tooltip : file.name,
29444                     cls : 'roo-document-manager-thumb',
29445                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29446                 }
29447             ]
29448
29449         });
29450
29451         this.xhr.open(this.method, this.url, true);
29452         
29453         var headers = {
29454             "Accept": "application/json",
29455             "Cache-Control": "no-cache",
29456             "X-Requested-With": "XMLHttpRequest"
29457         };
29458         
29459         for (var headerName in headers) {
29460             var headerValue = headers[headerName];
29461             if (headerValue) {
29462                 this.xhr.setRequestHeader(headerName, headerValue);
29463             }
29464         }
29465         
29466         var _this = this;
29467         
29468         this.xhr.onload = function()
29469         {
29470             _this.xhrOnLoad(_this.xhr);
29471         }
29472         
29473         this.xhr.onerror = function()
29474         {
29475             _this.xhrOnError(_this.xhr);
29476         }
29477         
29478         var formData = new FormData();
29479
29480         formData.append('returnHTML', 'NO');
29481         
29482         if(crop){
29483             formData.append('crop', crop);
29484         }
29485         
29486         formData.append(this.paramName, file, file.name);
29487         
29488         var options = {
29489             file : file, 
29490             manually : false
29491         };
29492         
29493         if(this.fireEvent('prepare', this, formData, options) != false){
29494             
29495             if(options.manually){
29496                 return;
29497             }
29498             
29499             this.xhr.send(formData);
29500             return;
29501         };
29502         
29503         this.uploadCancel();
29504     },
29505     
29506     uploadCancel : function()
29507     {
29508         if (this.xhr) {
29509             this.xhr.abort();
29510         }
29511         
29512         this.delegates = [];
29513         
29514         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29515             el.remove();
29516         }, this);
29517         
29518         this.arrange();
29519     },
29520     
29521     renderPreview : function(file)
29522     {
29523         if(typeof(file.target) != 'undefined' && file.target){
29524             return file;
29525         }
29526         
29527         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29528         
29529         var previewEl = this.managerEl.createChild({
29530             tag : 'div',
29531             cls : 'roo-document-manager-preview',
29532             cn : [
29533                 {
29534                     tag : 'div',
29535                     tooltip : file[this.toolTipName],
29536                     cls : 'roo-document-manager-thumb',
29537                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29538                 },
29539                 {
29540                     tag : 'button',
29541                     cls : 'close',
29542                     html : '<i class="fa fa-times-circle"></i>'
29543                 }
29544             ]
29545         });
29546
29547         var close = previewEl.select('button.close', true).first();
29548
29549         close.on('click', this.onRemove, this, file);
29550
29551         file.target = previewEl;
29552
29553         var image = previewEl.select('img', true).first();
29554         
29555         var _this = this;
29556         
29557         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29558         
29559         image.on('click', this.onClick, this, file);
29560         
29561         this.fireEvent('previewrendered', this, file);
29562         
29563         return file;
29564         
29565     },
29566     
29567     onPreviewLoad : function(file, image)
29568     {
29569         if(typeof(file.target) == 'undefined' || !file.target){
29570             return;
29571         }
29572         
29573         var width = image.dom.naturalWidth || image.dom.width;
29574         var height = image.dom.naturalHeight || image.dom.height;
29575         
29576         if(!this.previewResize) {
29577             return;
29578         }
29579         
29580         if(width > height){
29581             file.target.addClass('wide');
29582             return;
29583         }
29584         
29585         file.target.addClass('tall');
29586         return;
29587         
29588     },
29589     
29590     uploadFromSource : function(file, crop)
29591     {
29592         this.xhr = new XMLHttpRequest();
29593         
29594         this.managerEl.createChild({
29595             tag : 'div',
29596             cls : 'roo-document-manager-loading',
29597             cn : [
29598                 {
29599                     tag : 'div',
29600                     tooltip : file.name,
29601                     cls : 'roo-document-manager-thumb',
29602                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29603                 }
29604             ]
29605
29606         });
29607
29608         this.xhr.open(this.method, this.url, true);
29609         
29610         var headers = {
29611             "Accept": "application/json",
29612             "Cache-Control": "no-cache",
29613             "X-Requested-With": "XMLHttpRequest"
29614         };
29615         
29616         for (var headerName in headers) {
29617             var headerValue = headers[headerName];
29618             if (headerValue) {
29619                 this.xhr.setRequestHeader(headerName, headerValue);
29620             }
29621         }
29622         
29623         var _this = this;
29624         
29625         this.xhr.onload = function()
29626         {
29627             _this.xhrOnLoad(_this.xhr);
29628         }
29629         
29630         this.xhr.onerror = function()
29631         {
29632             _this.xhrOnError(_this.xhr);
29633         }
29634         
29635         var formData = new FormData();
29636
29637         formData.append('returnHTML', 'NO');
29638         
29639         formData.append('crop', crop);
29640         
29641         if(typeof(file.filename) != 'undefined'){
29642             formData.append('filename', file.filename);
29643         }
29644         
29645         if(typeof(file.mimetype) != 'undefined'){
29646             formData.append('mimetype', file.mimetype);
29647         }
29648         
29649         Roo.log(formData);
29650         
29651         if(this.fireEvent('prepare', this, formData) != false){
29652             this.xhr.send(formData);
29653         };
29654     }
29655 });
29656
29657 /*
29658 * Licence: LGPL
29659 */
29660
29661 /**
29662  * @class Roo.bootstrap.DocumentViewer
29663  * @extends Roo.bootstrap.Component
29664  * Bootstrap DocumentViewer class
29665  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29666  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29667  * 
29668  * @constructor
29669  * Create a new DocumentViewer
29670  * @param {Object} config The config object
29671  */
29672
29673 Roo.bootstrap.DocumentViewer = function(config){
29674     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29675     
29676     this.addEvents({
29677         /**
29678          * @event initial
29679          * Fire after initEvent
29680          * @param {Roo.bootstrap.DocumentViewer} this
29681          */
29682         "initial" : true,
29683         /**
29684          * @event click
29685          * Fire after click
29686          * @param {Roo.bootstrap.DocumentViewer} this
29687          */
29688         "click" : true,
29689         /**
29690          * @event download
29691          * Fire after download button
29692          * @param {Roo.bootstrap.DocumentViewer} this
29693          */
29694         "download" : true,
29695         /**
29696          * @event trash
29697          * Fire after trash button
29698          * @param {Roo.bootstrap.DocumentViewer} this
29699          */
29700         "trash" : true
29701         
29702     });
29703 };
29704
29705 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29706     
29707     showDownload : true,
29708     
29709     showTrash : true,
29710     
29711     getAutoCreate : function()
29712     {
29713         var cfg = {
29714             tag : 'div',
29715             cls : 'roo-document-viewer',
29716             cn : [
29717                 {
29718                     tag : 'div',
29719                     cls : 'roo-document-viewer-body',
29720                     cn : [
29721                         {
29722                             tag : 'div',
29723                             cls : 'roo-document-viewer-thumb',
29724                             cn : [
29725                                 {
29726                                     tag : 'img',
29727                                     cls : 'roo-document-viewer-image'
29728                                 }
29729                             ]
29730                         }
29731                     ]
29732                 },
29733                 {
29734                     tag : 'div',
29735                     cls : 'roo-document-viewer-footer',
29736                     cn : {
29737                         tag : 'div',
29738                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29739                         cn : [
29740                             {
29741                                 tag : 'div',
29742                                 cls : 'btn-group roo-document-viewer-download',
29743                                 cn : [
29744                                     {
29745                                         tag : 'button',
29746                                         cls : 'btn btn-default',
29747                                         html : '<i class="fa fa-download"></i>'
29748                                     }
29749                                 ]
29750                             },
29751                             {
29752                                 tag : 'div',
29753                                 cls : 'btn-group roo-document-viewer-trash',
29754                                 cn : [
29755                                     {
29756                                         tag : 'button',
29757                                         cls : 'btn btn-default',
29758                                         html : '<i class="fa fa-trash"></i>'
29759                                     }
29760                                 ]
29761                             }
29762                         ]
29763                     }
29764                 }
29765             ]
29766         };
29767         
29768         return cfg;
29769     },
29770     
29771     initEvents : function()
29772     {
29773         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29774         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29775         
29776         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29777         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29778         
29779         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29780         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29781         
29782         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29783         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29784         
29785         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29786         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29787         
29788         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29789         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29790         
29791         this.bodyEl.on('click', this.onClick, this);
29792         this.downloadBtn.on('click', this.onDownload, this);
29793         this.trashBtn.on('click', this.onTrash, this);
29794         
29795         this.downloadBtn.hide();
29796         this.trashBtn.hide();
29797         
29798         if(this.showDownload){
29799             this.downloadBtn.show();
29800         }
29801         
29802         if(this.showTrash){
29803             this.trashBtn.show();
29804         }
29805         
29806         if(!this.showDownload && !this.showTrash) {
29807             this.footerEl.hide();
29808         }
29809         
29810     },
29811     
29812     initial : function()
29813     {
29814         this.fireEvent('initial', this);
29815         
29816     },
29817     
29818     onClick : function(e)
29819     {
29820         e.preventDefault();
29821         
29822         this.fireEvent('click', this);
29823     },
29824     
29825     onDownload : function(e)
29826     {
29827         e.preventDefault();
29828         
29829         this.fireEvent('download', this);
29830     },
29831     
29832     onTrash : function(e)
29833     {
29834         e.preventDefault();
29835         
29836         this.fireEvent('trash', this);
29837     }
29838     
29839 });
29840 /*
29841  * - LGPL
29842  *
29843  * nav progress bar
29844  * 
29845  */
29846
29847 /**
29848  * @class Roo.bootstrap.NavProgressBar
29849  * @extends Roo.bootstrap.Component
29850  * Bootstrap NavProgressBar class
29851  * 
29852  * @constructor
29853  * Create a new nav progress bar
29854  * @param {Object} config The config object
29855  */
29856
29857 Roo.bootstrap.NavProgressBar = function(config){
29858     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29859
29860     this.bullets = this.bullets || [];
29861    
29862 //    Roo.bootstrap.NavProgressBar.register(this);
29863      this.addEvents({
29864         /**
29865              * @event changed
29866              * Fires when the active item changes
29867              * @param {Roo.bootstrap.NavProgressBar} this
29868              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29869              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29870          */
29871         'changed': true
29872      });
29873     
29874 };
29875
29876 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29877     
29878     bullets : [],
29879     barItems : [],
29880     
29881     getAutoCreate : function()
29882     {
29883         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29884         
29885         cfg = {
29886             tag : 'div',
29887             cls : 'roo-navigation-bar-group',
29888             cn : [
29889                 {
29890                     tag : 'div',
29891                     cls : 'roo-navigation-top-bar'
29892                 },
29893                 {
29894                     tag : 'div',
29895                     cls : 'roo-navigation-bullets-bar',
29896                     cn : [
29897                         {
29898                             tag : 'ul',
29899                             cls : 'roo-navigation-bar'
29900                         }
29901                     ]
29902                 },
29903                 
29904                 {
29905                     tag : 'div',
29906                     cls : 'roo-navigation-bottom-bar'
29907                 }
29908             ]
29909             
29910         };
29911         
29912         return cfg;
29913         
29914     },
29915     
29916     initEvents: function() 
29917     {
29918         
29919     },
29920     
29921     onRender : function(ct, position) 
29922     {
29923         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29924         
29925         if(this.bullets.length){
29926             Roo.each(this.bullets, function(b){
29927                this.addItem(b);
29928             }, this);
29929         }
29930         
29931         this.format();
29932         
29933     },
29934     
29935     addItem : function(cfg)
29936     {
29937         var item = new Roo.bootstrap.NavProgressItem(cfg);
29938         
29939         item.parentId = this.id;
29940         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29941         
29942         if(cfg.html){
29943             var top = new Roo.bootstrap.Element({
29944                 tag : 'div',
29945                 cls : 'roo-navigation-bar-text'
29946             });
29947             
29948             var bottom = new Roo.bootstrap.Element({
29949                 tag : 'div',
29950                 cls : 'roo-navigation-bar-text'
29951             });
29952             
29953             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29954             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29955             
29956             var topText = new Roo.bootstrap.Element({
29957                 tag : 'span',
29958                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29959             });
29960             
29961             var bottomText = new Roo.bootstrap.Element({
29962                 tag : 'span',
29963                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29964             });
29965             
29966             topText.onRender(top.el, null);
29967             bottomText.onRender(bottom.el, null);
29968             
29969             item.topEl = top;
29970             item.bottomEl = bottom;
29971         }
29972         
29973         this.barItems.push(item);
29974         
29975         return item;
29976     },
29977     
29978     getActive : function()
29979     {
29980         var active = false;
29981         
29982         Roo.each(this.barItems, function(v){
29983             
29984             if (!v.isActive()) {
29985                 return;
29986             }
29987             
29988             active = v;
29989             return false;
29990             
29991         });
29992         
29993         return active;
29994     },
29995     
29996     setActiveItem : function(item)
29997     {
29998         var prev = false;
29999         
30000         Roo.each(this.barItems, function(v){
30001             if (v.rid == item.rid) {
30002                 return ;
30003             }
30004             
30005             if (v.isActive()) {
30006                 v.setActive(false);
30007                 prev = v;
30008             }
30009         });
30010
30011         item.setActive(true);
30012         
30013         this.fireEvent('changed', this, item, prev);
30014     },
30015     
30016     getBarItem: function(rid)
30017     {
30018         var ret = false;
30019         
30020         Roo.each(this.barItems, function(e) {
30021             if (e.rid != rid) {
30022                 return;
30023             }
30024             
30025             ret =  e;
30026             return false;
30027         });
30028         
30029         return ret;
30030     },
30031     
30032     indexOfItem : function(item)
30033     {
30034         var index = false;
30035         
30036         Roo.each(this.barItems, function(v, i){
30037             
30038             if (v.rid != item.rid) {
30039                 return;
30040             }
30041             
30042             index = i;
30043             return false
30044         });
30045         
30046         return index;
30047     },
30048     
30049     setActiveNext : function()
30050     {
30051         var i = this.indexOfItem(this.getActive());
30052         
30053         if (i > this.barItems.length) {
30054             return;
30055         }
30056         
30057         this.setActiveItem(this.barItems[i+1]);
30058     },
30059     
30060     setActivePrev : function()
30061     {
30062         var i = this.indexOfItem(this.getActive());
30063         
30064         if (i  < 1) {
30065             return;
30066         }
30067         
30068         this.setActiveItem(this.barItems[i-1]);
30069     },
30070     
30071     format : function()
30072     {
30073         if(!this.barItems.length){
30074             return;
30075         }
30076      
30077         var width = 100 / this.barItems.length;
30078         
30079         Roo.each(this.barItems, function(i){
30080             i.el.setStyle('width', width + '%');
30081             i.topEl.el.setStyle('width', width + '%');
30082             i.bottomEl.el.setStyle('width', width + '%');
30083         }, this);
30084         
30085     }
30086     
30087 });
30088 /*
30089  * - LGPL
30090  *
30091  * Nav Progress Item
30092  * 
30093  */
30094
30095 /**
30096  * @class Roo.bootstrap.NavProgressItem
30097  * @extends Roo.bootstrap.Component
30098  * Bootstrap NavProgressItem class
30099  * @cfg {String} rid the reference id
30100  * @cfg {Boolean} active (true|false) Is item active default false
30101  * @cfg {Boolean} disabled (true|false) Is item active default false
30102  * @cfg {String} html
30103  * @cfg {String} position (top|bottom) text position default bottom
30104  * @cfg {String} icon show icon instead of number
30105  * 
30106  * @constructor
30107  * Create a new NavProgressItem
30108  * @param {Object} config The config object
30109  */
30110 Roo.bootstrap.NavProgressItem = function(config){
30111     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30112     this.addEvents({
30113         // raw events
30114         /**
30115          * @event click
30116          * The raw click event for the entire grid.
30117          * @param {Roo.bootstrap.NavProgressItem} this
30118          * @param {Roo.EventObject} e
30119          */
30120         "click" : true
30121     });
30122    
30123 };
30124
30125 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30126     
30127     rid : '',
30128     active : false,
30129     disabled : false,
30130     html : '',
30131     position : 'bottom',
30132     icon : false,
30133     
30134     getAutoCreate : function()
30135     {
30136         var iconCls = 'roo-navigation-bar-item-icon';
30137         
30138         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30139         
30140         var cfg = {
30141             tag: 'li',
30142             cls: 'roo-navigation-bar-item',
30143             cn : [
30144                 {
30145                     tag : 'i',
30146                     cls : iconCls
30147                 }
30148             ]
30149         };
30150         
30151         if(this.active){
30152             cfg.cls += ' active';
30153         }
30154         if(this.disabled){
30155             cfg.cls += ' disabled';
30156         }
30157         
30158         return cfg;
30159     },
30160     
30161     disable : function()
30162     {
30163         this.setDisabled(true);
30164     },
30165     
30166     enable : function()
30167     {
30168         this.setDisabled(false);
30169     },
30170     
30171     initEvents: function() 
30172     {
30173         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30174         
30175         this.iconEl.on('click', this.onClick, this);
30176     },
30177     
30178     onClick : function(e)
30179     {
30180         e.preventDefault();
30181         
30182         if(this.disabled){
30183             return;
30184         }
30185         
30186         if(this.fireEvent('click', this, e) === false){
30187             return;
30188         };
30189         
30190         this.parent().setActiveItem(this);
30191     },
30192     
30193     isActive: function () 
30194     {
30195         return this.active;
30196     },
30197     
30198     setActive : function(state)
30199     {
30200         if(this.active == state){
30201             return;
30202         }
30203         
30204         this.active = state;
30205         
30206         if (state) {
30207             this.el.addClass('active');
30208             return;
30209         }
30210         
30211         this.el.removeClass('active');
30212         
30213         return;
30214     },
30215     
30216     setDisabled : function(state)
30217     {
30218         if(this.disabled == state){
30219             return;
30220         }
30221         
30222         this.disabled = state;
30223         
30224         if (state) {
30225             this.el.addClass('disabled');
30226             return;
30227         }
30228         
30229         this.el.removeClass('disabled');
30230     },
30231     
30232     tooltipEl : function()
30233     {
30234         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30235     }
30236 });
30237  
30238
30239  /*
30240  * - LGPL
30241  *
30242  * FieldLabel
30243  * 
30244  */
30245
30246 /**
30247  * @class Roo.bootstrap.FieldLabel
30248  * @extends Roo.bootstrap.Component
30249  * Bootstrap FieldLabel class
30250  * @cfg {String} html contents of the element
30251  * @cfg {String} tag tag of the element default label
30252  * @cfg {String} cls class of the element
30253  * @cfg {String} target label target 
30254  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30255  * @cfg {String} invalidClass default "text-warning"
30256  * @cfg {String} validClass default "text-success"
30257  * @cfg {String} iconTooltip default "This field is required"
30258  * @cfg {String} indicatorpos (left|right) default left
30259  * 
30260  * @constructor
30261  * Create a new FieldLabel
30262  * @param {Object} config The config object
30263  */
30264
30265 Roo.bootstrap.FieldLabel = function(config){
30266     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30267     
30268     this.addEvents({
30269             /**
30270              * @event invalid
30271              * Fires after the field has been marked as invalid.
30272              * @param {Roo.form.FieldLabel} this
30273              * @param {String} msg The validation message
30274              */
30275             invalid : true,
30276             /**
30277              * @event valid
30278              * Fires after the field has been validated with no errors.
30279              * @param {Roo.form.FieldLabel} this
30280              */
30281             valid : true
30282         });
30283 };
30284
30285 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30286     
30287     tag: 'label',
30288     cls: '',
30289     html: '',
30290     target: '',
30291     allowBlank : true,
30292     invalidClass : 'has-warning',
30293     validClass : 'has-success',
30294     iconTooltip : 'This field is required',
30295     indicatorpos : 'left',
30296     
30297     getAutoCreate : function(){
30298         
30299         var cls = "";
30300         if (!this.allowBlank) {
30301             cls  = "visible";
30302         }
30303         
30304         var cfg = {
30305             tag : this.tag,
30306             cls : 'roo-bootstrap-field-label ' + this.cls,
30307             for : this.target,
30308             cn : [
30309                 {
30310                     tag : 'i',
30311                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30312                     tooltip : this.iconTooltip
30313                 },
30314                 {
30315                     tag : 'span',
30316                     html : this.html
30317                 }
30318             ] 
30319         };
30320         
30321         if(this.indicatorpos == 'right'){
30322             var cfg = {
30323                 tag : this.tag,
30324                 cls : 'roo-bootstrap-field-label ' + this.cls,
30325                 for : this.target,
30326                 cn : [
30327                     {
30328                         tag : 'span',
30329                         html : this.html
30330                     },
30331                     {
30332                         tag : 'i',
30333                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30334                         tooltip : this.iconTooltip
30335                     }
30336                 ] 
30337             };
30338         }
30339         
30340         return cfg;
30341     },
30342     
30343     initEvents: function() 
30344     {
30345         Roo.bootstrap.Element.superclass.initEvents.call(this);
30346         
30347         this.indicator = this.indicatorEl();
30348         
30349         if(this.indicator){
30350             this.indicator.removeClass('visible');
30351             this.indicator.addClass('invisible');
30352         }
30353         
30354         Roo.bootstrap.FieldLabel.register(this);
30355     },
30356     
30357     indicatorEl : function()
30358     {
30359         var indicator = this.el.select('i.roo-required-indicator',true).first();
30360         
30361         if(!indicator){
30362             return false;
30363         }
30364         
30365         return indicator;
30366         
30367     },
30368     
30369     /**
30370      * Mark this field as valid
30371      */
30372     markValid : function()
30373     {
30374         if(this.indicator){
30375             this.indicator.removeClass('visible');
30376             this.indicator.addClass('invisible');
30377         }
30378         
30379         this.el.removeClass(this.invalidClass);
30380         
30381         this.el.addClass(this.validClass);
30382         
30383         this.fireEvent('valid', this);
30384     },
30385     
30386     /**
30387      * Mark this field as invalid
30388      * @param {String} msg The validation message
30389      */
30390     markInvalid : function(msg)
30391     {
30392         if(this.indicator){
30393             this.indicator.removeClass('invisible');
30394             this.indicator.addClass('visible');
30395         }
30396         
30397         this.el.removeClass(this.validClass);
30398         
30399         this.el.addClass(this.invalidClass);
30400         
30401         this.fireEvent('invalid', this, msg);
30402     }
30403     
30404    
30405 });
30406
30407 Roo.apply(Roo.bootstrap.FieldLabel, {
30408     
30409     groups: {},
30410     
30411      /**
30412     * register a FieldLabel Group
30413     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30414     */
30415     register : function(label)
30416     {
30417         if(this.groups.hasOwnProperty(label.target)){
30418             return;
30419         }
30420      
30421         this.groups[label.target] = label;
30422         
30423     },
30424     /**
30425     * fetch a FieldLabel Group based on the target
30426     * @param {string} target
30427     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30428     */
30429     get: function(target) {
30430         if (typeof(this.groups[target]) == 'undefined') {
30431             return false;
30432         }
30433         
30434         return this.groups[target] ;
30435     }
30436 });
30437
30438  
30439
30440  /*
30441  * - LGPL
30442  *
30443  * page DateSplitField.
30444  * 
30445  */
30446
30447
30448 /**
30449  * @class Roo.bootstrap.DateSplitField
30450  * @extends Roo.bootstrap.Component
30451  * Bootstrap DateSplitField class
30452  * @cfg {string} fieldLabel - the label associated
30453  * @cfg {Number} labelWidth set the width of label (0-12)
30454  * @cfg {String} labelAlign (top|left)
30455  * @cfg {Boolean} dayAllowBlank (true|false) default false
30456  * @cfg {Boolean} monthAllowBlank (true|false) default false
30457  * @cfg {Boolean} yearAllowBlank (true|false) default false
30458  * @cfg {string} dayPlaceholder 
30459  * @cfg {string} monthPlaceholder
30460  * @cfg {string} yearPlaceholder
30461  * @cfg {string} dayFormat default 'd'
30462  * @cfg {string} monthFormat default 'm'
30463  * @cfg {string} yearFormat default 'Y'
30464  * @cfg {Number} labellg set the width of label (1-12)
30465  * @cfg {Number} labelmd set the width of label (1-12)
30466  * @cfg {Number} labelsm set the width of label (1-12)
30467  * @cfg {Number} labelxs set the width of label (1-12)
30468
30469  *     
30470  * @constructor
30471  * Create a new DateSplitField
30472  * @param {Object} config The config object
30473  */
30474
30475 Roo.bootstrap.DateSplitField = function(config){
30476     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30477     
30478     this.addEvents({
30479         // raw events
30480          /**
30481          * @event years
30482          * getting the data of years
30483          * @param {Roo.bootstrap.DateSplitField} this
30484          * @param {Object} years
30485          */
30486         "years" : true,
30487         /**
30488          * @event days
30489          * getting the data of days
30490          * @param {Roo.bootstrap.DateSplitField} this
30491          * @param {Object} days
30492          */
30493         "days" : true,
30494         /**
30495          * @event invalid
30496          * Fires after the field has been marked as invalid.
30497          * @param {Roo.form.Field} this
30498          * @param {String} msg The validation message
30499          */
30500         invalid : true,
30501        /**
30502          * @event valid
30503          * Fires after the field has been validated with no errors.
30504          * @param {Roo.form.Field} this
30505          */
30506         valid : true
30507     });
30508 };
30509
30510 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30511     
30512     fieldLabel : '',
30513     labelAlign : 'top',
30514     labelWidth : 3,
30515     dayAllowBlank : false,
30516     monthAllowBlank : false,
30517     yearAllowBlank : false,
30518     dayPlaceholder : '',
30519     monthPlaceholder : '',
30520     yearPlaceholder : '',
30521     dayFormat : 'd',
30522     monthFormat : 'm',
30523     yearFormat : 'Y',
30524     isFormField : true,
30525     labellg : 0,
30526     labelmd : 0,
30527     labelsm : 0,
30528     labelxs : 0,
30529     
30530     getAutoCreate : function()
30531     {
30532         var cfg = {
30533             tag : 'div',
30534             cls : 'row roo-date-split-field-group',
30535             cn : [
30536                 {
30537                     tag : 'input',
30538                     type : 'hidden',
30539                     cls : 'form-hidden-field roo-date-split-field-group-value',
30540                     name : this.name
30541                 }
30542             ]
30543         };
30544         
30545         var labelCls = 'col-md-12';
30546         var contentCls = 'col-md-4';
30547         
30548         if(this.fieldLabel){
30549             
30550             var label = {
30551                 tag : 'div',
30552                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30553                 cn : [
30554                     {
30555                         tag : 'label',
30556                         html : this.fieldLabel
30557                     }
30558                 ]
30559             };
30560             
30561             if(this.labelAlign == 'left'){
30562             
30563                 if(this.labelWidth > 12){
30564                     label.style = "width: " + this.labelWidth + 'px';
30565                 }
30566
30567                 if(this.labelWidth < 13 && this.labelmd == 0){
30568                     this.labelmd = this.labelWidth;
30569                 }
30570
30571                 if(this.labellg > 0){
30572                     labelCls = ' col-lg-' + this.labellg;
30573                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30574                 }
30575
30576                 if(this.labelmd > 0){
30577                     labelCls = ' col-md-' + this.labelmd;
30578                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30579                 }
30580
30581                 if(this.labelsm > 0){
30582                     labelCls = ' col-sm-' + this.labelsm;
30583                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30584                 }
30585
30586                 if(this.labelxs > 0){
30587                     labelCls = ' col-xs-' + this.labelxs;
30588                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30589                 }
30590             }
30591             
30592             label.cls += ' ' + labelCls;
30593             
30594             cfg.cn.push(label);
30595         }
30596         
30597         Roo.each(['day', 'month', 'year'], function(t){
30598             cfg.cn.push({
30599                 tag : 'div',
30600                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30601             });
30602         }, this);
30603         
30604         return cfg;
30605     },
30606     
30607     inputEl: function ()
30608     {
30609         return this.el.select('.roo-date-split-field-group-value', true).first();
30610     },
30611     
30612     onRender : function(ct, position) 
30613     {
30614         var _this = this;
30615         
30616         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30617         
30618         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30619         
30620         this.dayField = new Roo.bootstrap.ComboBox({
30621             allowBlank : this.dayAllowBlank,
30622             alwaysQuery : true,
30623             displayField : 'value',
30624             editable : false,
30625             fieldLabel : '',
30626             forceSelection : true,
30627             mode : 'local',
30628             placeholder : this.dayPlaceholder,
30629             selectOnFocus : true,
30630             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30631             triggerAction : 'all',
30632             typeAhead : true,
30633             valueField : 'value',
30634             store : new Roo.data.SimpleStore({
30635                 data : (function() {    
30636                     var days = [];
30637                     _this.fireEvent('days', _this, days);
30638                     return days;
30639                 })(),
30640                 fields : [ 'value' ]
30641             }),
30642             listeners : {
30643                 select : function (_self, record, index)
30644                 {
30645                     _this.setValue(_this.getValue());
30646                 }
30647             }
30648         });
30649
30650         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30651         
30652         this.monthField = new Roo.bootstrap.MonthField({
30653             after : '<i class=\"fa fa-calendar\"></i>',
30654             allowBlank : this.monthAllowBlank,
30655             placeholder : this.monthPlaceholder,
30656             readOnly : true,
30657             listeners : {
30658                 render : function (_self)
30659                 {
30660                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30661                         e.preventDefault();
30662                         _self.focus();
30663                     });
30664                 },
30665                 select : function (_self, oldvalue, newvalue)
30666                 {
30667                     _this.setValue(_this.getValue());
30668                 }
30669             }
30670         });
30671         
30672         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30673         
30674         this.yearField = new Roo.bootstrap.ComboBox({
30675             allowBlank : this.yearAllowBlank,
30676             alwaysQuery : true,
30677             displayField : 'value',
30678             editable : false,
30679             fieldLabel : '',
30680             forceSelection : true,
30681             mode : 'local',
30682             placeholder : this.yearPlaceholder,
30683             selectOnFocus : true,
30684             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30685             triggerAction : 'all',
30686             typeAhead : true,
30687             valueField : 'value',
30688             store : new Roo.data.SimpleStore({
30689                 data : (function() {
30690                     var years = [];
30691                     _this.fireEvent('years', _this, years);
30692                     return years;
30693                 })(),
30694                 fields : [ 'value' ]
30695             }),
30696             listeners : {
30697                 select : function (_self, record, index)
30698                 {
30699                     _this.setValue(_this.getValue());
30700                 }
30701             }
30702         });
30703
30704         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30705     },
30706     
30707     setValue : function(v, format)
30708     {
30709         this.inputEl.dom.value = v;
30710         
30711         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30712         
30713         var d = Date.parseDate(v, f);
30714         
30715         if(!d){
30716             this.validate();
30717             return;
30718         }
30719         
30720         this.setDay(d.format(this.dayFormat));
30721         this.setMonth(d.format(this.monthFormat));
30722         this.setYear(d.format(this.yearFormat));
30723         
30724         this.validate();
30725         
30726         return;
30727     },
30728     
30729     setDay : function(v)
30730     {
30731         this.dayField.setValue(v);
30732         this.inputEl.dom.value = this.getValue();
30733         this.validate();
30734         return;
30735     },
30736     
30737     setMonth : function(v)
30738     {
30739         this.monthField.setValue(v, true);
30740         this.inputEl.dom.value = this.getValue();
30741         this.validate();
30742         return;
30743     },
30744     
30745     setYear : function(v)
30746     {
30747         this.yearField.setValue(v);
30748         this.inputEl.dom.value = this.getValue();
30749         this.validate();
30750         return;
30751     },
30752     
30753     getDay : function()
30754     {
30755         return this.dayField.getValue();
30756     },
30757     
30758     getMonth : function()
30759     {
30760         return this.monthField.getValue();
30761     },
30762     
30763     getYear : function()
30764     {
30765         return this.yearField.getValue();
30766     },
30767     
30768     getValue : function()
30769     {
30770         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30771         
30772         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30773         
30774         return date;
30775     },
30776     
30777     reset : function()
30778     {
30779         this.setDay('');
30780         this.setMonth('');
30781         this.setYear('');
30782         this.inputEl.dom.value = '';
30783         this.validate();
30784         return;
30785     },
30786     
30787     validate : function()
30788     {
30789         var d = this.dayField.validate();
30790         var m = this.monthField.validate();
30791         var y = this.yearField.validate();
30792         
30793         var valid = true;
30794         
30795         if(
30796                 (!this.dayAllowBlank && !d) ||
30797                 (!this.monthAllowBlank && !m) ||
30798                 (!this.yearAllowBlank && !y)
30799         ){
30800             valid = false;
30801         }
30802         
30803         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30804             return valid;
30805         }
30806         
30807         if(valid){
30808             this.markValid();
30809             return valid;
30810         }
30811         
30812         this.markInvalid();
30813         
30814         return valid;
30815     },
30816     
30817     markValid : function()
30818     {
30819         
30820         var label = this.el.select('label', true).first();
30821         var icon = this.el.select('i.fa-star', true).first();
30822
30823         if(label && icon){
30824             icon.remove();
30825         }
30826         
30827         this.fireEvent('valid', this);
30828     },
30829     
30830      /**
30831      * Mark this field as invalid
30832      * @param {String} msg The validation message
30833      */
30834     markInvalid : function(msg)
30835     {
30836         
30837         var label = this.el.select('label', true).first();
30838         var icon = this.el.select('i.fa-star', true).first();
30839
30840         if(label && !icon){
30841             this.el.select('.roo-date-split-field-label', true).createChild({
30842                 tag : 'i',
30843                 cls : 'text-danger fa fa-lg fa-star',
30844                 tooltip : 'This field is required',
30845                 style : 'margin-right:5px;'
30846             }, label, true);
30847         }
30848         
30849         this.fireEvent('invalid', this, msg);
30850     },
30851     
30852     clearInvalid : function()
30853     {
30854         var label = this.el.select('label', true).first();
30855         var icon = this.el.select('i.fa-star', true).first();
30856
30857         if(label && icon){
30858             icon.remove();
30859         }
30860         
30861         this.fireEvent('valid', this);
30862     },
30863     
30864     getName: function()
30865     {
30866         return this.name;
30867     }
30868     
30869 });
30870
30871  /**
30872  *
30873  * This is based on 
30874  * http://masonry.desandro.com
30875  *
30876  * The idea is to render all the bricks based on vertical width...
30877  *
30878  * The original code extends 'outlayer' - we might need to use that....
30879  * 
30880  */
30881
30882
30883 /**
30884  * @class Roo.bootstrap.LayoutMasonry
30885  * @extends Roo.bootstrap.Component
30886  * Bootstrap Layout Masonry class
30887  * 
30888  * @constructor
30889  * Create a new Element
30890  * @param {Object} config The config object
30891  */
30892
30893 Roo.bootstrap.LayoutMasonry = function(config){
30894     
30895     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30896     
30897     this.bricks = [];
30898     
30899     Roo.bootstrap.LayoutMasonry.register(this);
30900     
30901     this.addEvents({
30902         // raw events
30903         /**
30904          * @event layout
30905          * Fire after layout the items
30906          * @param {Roo.bootstrap.LayoutMasonry} this
30907          * @param {Roo.EventObject} e
30908          */
30909         "layout" : true
30910     });
30911     
30912 };
30913
30914 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30915     
30916     /**
30917      * @cfg {Boolean} isLayoutInstant = no animation?
30918      */   
30919     isLayoutInstant : false, // needed?
30920    
30921     /**
30922      * @cfg {Number} boxWidth  width of the columns
30923      */   
30924     boxWidth : 450,
30925     
30926       /**
30927      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30928      */   
30929     boxHeight : 0,
30930     
30931     /**
30932      * @cfg {Number} padWidth padding below box..
30933      */   
30934     padWidth : 10, 
30935     
30936     /**
30937      * @cfg {Number} gutter gutter width..
30938      */   
30939     gutter : 10,
30940     
30941      /**
30942      * @cfg {Number} maxCols maximum number of columns
30943      */   
30944     
30945     maxCols: 0,
30946     
30947     /**
30948      * @cfg {Boolean} isAutoInitial defalut true
30949      */   
30950     isAutoInitial : true, 
30951     
30952     containerWidth: 0,
30953     
30954     /**
30955      * @cfg {Boolean} isHorizontal defalut false
30956      */   
30957     isHorizontal : false, 
30958
30959     currentSize : null,
30960     
30961     tag: 'div',
30962     
30963     cls: '',
30964     
30965     bricks: null, //CompositeElement
30966     
30967     cols : 1,
30968     
30969     _isLayoutInited : false,
30970     
30971 //    isAlternative : false, // only use for vertical layout...
30972     
30973     /**
30974      * @cfg {Number} alternativePadWidth padding below box..
30975      */   
30976     alternativePadWidth : 50,
30977     
30978     selectedBrick : [],
30979     
30980     getAutoCreate : function(){
30981         
30982         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30983         
30984         var cfg = {
30985             tag: this.tag,
30986             cls: 'blog-masonary-wrapper ' + this.cls,
30987             cn : {
30988                 cls : 'mas-boxes masonary'
30989             }
30990         };
30991         
30992         return cfg;
30993     },
30994     
30995     getChildContainer: function( )
30996     {
30997         if (this.boxesEl) {
30998             return this.boxesEl;
30999         }
31000         
31001         this.boxesEl = this.el.select('.mas-boxes').first();
31002         
31003         return this.boxesEl;
31004     },
31005     
31006     
31007     initEvents : function()
31008     {
31009         var _this = this;
31010         
31011         if(this.isAutoInitial){
31012             Roo.log('hook children rendered');
31013             this.on('childrenrendered', function() {
31014                 Roo.log('children rendered');
31015                 _this.initial();
31016             } ,this);
31017         }
31018     },
31019     
31020     initial : function()
31021     {
31022         this.selectedBrick = [];
31023         
31024         this.currentSize = this.el.getBox(true);
31025         
31026         Roo.EventManager.onWindowResize(this.resize, this); 
31027
31028         if(!this.isAutoInitial){
31029             this.layout();
31030             return;
31031         }
31032         
31033         this.layout();
31034         
31035         return;
31036         //this.layout.defer(500,this);
31037         
31038     },
31039     
31040     resize : function()
31041     {
31042         var cs = this.el.getBox(true);
31043         
31044         if (
31045                 this.currentSize.width == cs.width && 
31046                 this.currentSize.x == cs.x && 
31047                 this.currentSize.height == cs.height && 
31048                 this.currentSize.y == cs.y 
31049         ) {
31050             Roo.log("no change in with or X or Y");
31051             return;
31052         }
31053         
31054         this.currentSize = cs;
31055         
31056         this.layout();
31057         
31058     },
31059     
31060     layout : function()
31061     {   
31062         this._resetLayout();
31063         
31064         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31065         
31066         this.layoutItems( isInstant );
31067       
31068         this._isLayoutInited = true;
31069         
31070         this.fireEvent('layout', this);
31071         
31072     },
31073     
31074     _resetLayout : function()
31075     {
31076         if(this.isHorizontal){
31077             this.horizontalMeasureColumns();
31078             return;
31079         }
31080         
31081         this.verticalMeasureColumns();
31082         
31083     },
31084     
31085     verticalMeasureColumns : function()
31086     {
31087         this.getContainerWidth();
31088         
31089 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31090 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31091 //            return;
31092 //        }
31093         
31094         var boxWidth = this.boxWidth + this.padWidth;
31095         
31096         if(this.containerWidth < this.boxWidth){
31097             boxWidth = this.containerWidth
31098         }
31099         
31100         var containerWidth = this.containerWidth;
31101         
31102         var cols = Math.floor(containerWidth / boxWidth);
31103         
31104         this.cols = Math.max( cols, 1 );
31105         
31106         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31107         
31108         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31109         
31110         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31111         
31112         this.colWidth = boxWidth + avail - this.padWidth;
31113         
31114         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31115         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31116     },
31117     
31118     horizontalMeasureColumns : function()
31119     {
31120         this.getContainerWidth();
31121         
31122         var boxWidth = this.boxWidth;
31123         
31124         if(this.containerWidth < boxWidth){
31125             boxWidth = this.containerWidth;
31126         }
31127         
31128         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31129         
31130         this.el.setHeight(boxWidth);
31131         
31132     },
31133     
31134     getContainerWidth : function()
31135     {
31136         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31137     },
31138     
31139     layoutItems : function( isInstant )
31140     {
31141         Roo.log(this.bricks);
31142         
31143         var items = Roo.apply([], this.bricks);
31144         
31145         if(this.isHorizontal){
31146             this._horizontalLayoutItems( items , isInstant );
31147             return;
31148         }
31149         
31150 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31151 //            this._verticalAlternativeLayoutItems( items , isInstant );
31152 //            return;
31153 //        }
31154         
31155         this._verticalLayoutItems( items , isInstant );
31156         
31157     },
31158     
31159     _verticalLayoutItems : function ( items , isInstant)
31160     {
31161         if ( !items || !items.length ) {
31162             return;
31163         }
31164         
31165         var standard = [
31166             ['xs', 'xs', 'xs', 'tall'],
31167             ['xs', 'xs', 'tall'],
31168             ['xs', 'xs', 'sm'],
31169             ['xs', 'xs', 'xs'],
31170             ['xs', 'tall'],
31171             ['xs', 'sm'],
31172             ['xs', 'xs'],
31173             ['xs'],
31174             
31175             ['sm', 'xs', 'xs'],
31176             ['sm', 'xs'],
31177             ['sm'],
31178             
31179             ['tall', 'xs', 'xs', 'xs'],
31180             ['tall', 'xs', 'xs'],
31181             ['tall', 'xs'],
31182             ['tall']
31183             
31184         ];
31185         
31186         var queue = [];
31187         
31188         var boxes = [];
31189         
31190         var box = [];
31191         
31192         Roo.each(items, function(item, k){
31193             
31194             switch (item.size) {
31195                 // these layouts take up a full box,
31196                 case 'md' :
31197                 case 'md-left' :
31198                 case 'md-right' :
31199                 case 'wide' :
31200                     
31201                     if(box.length){
31202                         boxes.push(box);
31203                         box = [];
31204                     }
31205                     
31206                     boxes.push([item]);
31207                     
31208                     break;
31209                     
31210                 case 'xs' :
31211                 case 'sm' :
31212                 case 'tall' :
31213                     
31214                     box.push(item);
31215                     
31216                     break;
31217                 default :
31218                     break;
31219                     
31220             }
31221             
31222         }, this);
31223         
31224         if(box.length){
31225             boxes.push(box);
31226             box = [];
31227         }
31228         
31229         var filterPattern = function(box, length)
31230         {
31231             if(!box.length){
31232                 return;
31233             }
31234             
31235             var match = false;
31236             
31237             var pattern = box.slice(0, length);
31238             
31239             var format = [];
31240             
31241             Roo.each(pattern, function(i){
31242                 format.push(i.size);
31243             }, this);
31244             
31245             Roo.each(standard, function(s){
31246                 
31247                 if(String(s) != String(format)){
31248                     return;
31249                 }
31250                 
31251                 match = true;
31252                 return false;
31253                 
31254             }, this);
31255             
31256             if(!match && length == 1){
31257                 return;
31258             }
31259             
31260             if(!match){
31261                 filterPattern(box, length - 1);
31262                 return;
31263             }
31264                 
31265             queue.push(pattern);
31266
31267             box = box.slice(length, box.length);
31268
31269             filterPattern(box, 4);
31270
31271             return;
31272             
31273         }
31274         
31275         Roo.each(boxes, function(box, k){
31276             
31277             if(!box.length){
31278                 return;
31279             }
31280             
31281             if(box.length == 1){
31282                 queue.push(box);
31283                 return;
31284             }
31285             
31286             filterPattern(box, 4);
31287             
31288         }, this);
31289         
31290         this._processVerticalLayoutQueue( queue, isInstant );
31291         
31292     },
31293     
31294 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31295 //    {
31296 //        if ( !items || !items.length ) {
31297 //            return;
31298 //        }
31299 //
31300 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31301 //        
31302 //    },
31303     
31304     _horizontalLayoutItems : function ( items , isInstant)
31305     {
31306         if ( !items || !items.length || items.length < 3) {
31307             return;
31308         }
31309         
31310         items.reverse();
31311         
31312         var eItems = items.slice(0, 3);
31313         
31314         items = items.slice(3, items.length);
31315         
31316         var standard = [
31317             ['xs', 'xs', 'xs', 'wide'],
31318             ['xs', 'xs', 'wide'],
31319             ['xs', 'xs', 'sm'],
31320             ['xs', 'xs', 'xs'],
31321             ['xs', 'wide'],
31322             ['xs', 'sm'],
31323             ['xs', 'xs'],
31324             ['xs'],
31325             
31326             ['sm', 'xs', 'xs'],
31327             ['sm', 'xs'],
31328             ['sm'],
31329             
31330             ['wide', 'xs', 'xs', 'xs'],
31331             ['wide', 'xs', 'xs'],
31332             ['wide', 'xs'],
31333             ['wide'],
31334             
31335             ['wide-thin']
31336         ];
31337         
31338         var queue = [];
31339         
31340         var boxes = [];
31341         
31342         var box = [];
31343         
31344         Roo.each(items, function(item, k){
31345             
31346             switch (item.size) {
31347                 case 'md' :
31348                 case 'md-left' :
31349                 case 'md-right' :
31350                 case 'tall' :
31351                     
31352                     if(box.length){
31353                         boxes.push(box);
31354                         box = [];
31355                     }
31356                     
31357                     boxes.push([item]);
31358                     
31359                     break;
31360                     
31361                 case 'xs' :
31362                 case 'sm' :
31363                 case 'wide' :
31364                 case 'wide-thin' :
31365                     
31366                     box.push(item);
31367                     
31368                     break;
31369                 default :
31370                     break;
31371                     
31372             }
31373             
31374         }, this);
31375         
31376         if(box.length){
31377             boxes.push(box);
31378             box = [];
31379         }
31380         
31381         var filterPattern = function(box, length)
31382         {
31383             if(!box.length){
31384                 return;
31385             }
31386             
31387             var match = false;
31388             
31389             var pattern = box.slice(0, length);
31390             
31391             var format = [];
31392             
31393             Roo.each(pattern, function(i){
31394                 format.push(i.size);
31395             }, this);
31396             
31397             Roo.each(standard, function(s){
31398                 
31399                 if(String(s) != String(format)){
31400                     return;
31401                 }
31402                 
31403                 match = true;
31404                 return false;
31405                 
31406             }, this);
31407             
31408             if(!match && length == 1){
31409                 return;
31410             }
31411             
31412             if(!match){
31413                 filterPattern(box, length - 1);
31414                 return;
31415             }
31416                 
31417             queue.push(pattern);
31418
31419             box = box.slice(length, box.length);
31420
31421             filterPattern(box, 4);
31422
31423             return;
31424             
31425         }
31426         
31427         Roo.each(boxes, function(box, k){
31428             
31429             if(!box.length){
31430                 return;
31431             }
31432             
31433             if(box.length == 1){
31434                 queue.push(box);
31435                 return;
31436             }
31437             
31438             filterPattern(box, 4);
31439             
31440         }, this);
31441         
31442         
31443         var prune = [];
31444         
31445         var pos = this.el.getBox(true);
31446         
31447         var minX = pos.x;
31448         
31449         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31450         
31451         var hit_end = false;
31452         
31453         Roo.each(queue, function(box){
31454             
31455             if(hit_end){
31456                 
31457                 Roo.each(box, function(b){
31458                 
31459                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31460                     b.el.hide();
31461
31462                 }, this);
31463
31464                 return;
31465             }
31466             
31467             var mx = 0;
31468             
31469             Roo.each(box, function(b){
31470                 
31471                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31472                 b.el.show();
31473
31474                 mx = Math.max(mx, b.x);
31475                 
31476             }, this);
31477             
31478             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31479             
31480             if(maxX < minX){
31481                 
31482                 Roo.each(box, function(b){
31483                 
31484                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31485                     b.el.hide();
31486                     
31487                 }, this);
31488                 
31489                 hit_end = true;
31490                 
31491                 return;
31492             }
31493             
31494             prune.push(box);
31495             
31496         }, this);
31497         
31498         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31499     },
31500     
31501     /** Sets position of item in DOM
31502     * @param {Element} item
31503     * @param {Number} x - horizontal position
31504     * @param {Number} y - vertical position
31505     * @param {Boolean} isInstant - disables transitions
31506     */
31507     _processVerticalLayoutQueue : function( queue, isInstant )
31508     {
31509         var pos = this.el.getBox(true);
31510         var x = pos.x;
31511         var y = pos.y;
31512         var maxY = [];
31513         
31514         for (var i = 0; i < this.cols; i++){
31515             maxY[i] = pos.y;
31516         }
31517         
31518         Roo.each(queue, function(box, k){
31519             
31520             var col = k % this.cols;
31521             
31522             Roo.each(box, function(b,kk){
31523                 
31524                 b.el.position('absolute');
31525                 
31526                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31527                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31528                 
31529                 if(b.size == 'md-left' || b.size == 'md-right'){
31530                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31531                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31532                 }
31533                 
31534                 b.el.setWidth(width);
31535                 b.el.setHeight(height);
31536                 // iframe?
31537                 b.el.select('iframe',true).setSize(width,height);
31538                 
31539             }, this);
31540             
31541             for (var i = 0; i < this.cols; i++){
31542                 
31543                 if(maxY[i] < maxY[col]){
31544                     col = i;
31545                     continue;
31546                 }
31547                 
31548                 col = Math.min(col, i);
31549                 
31550             }
31551             
31552             x = pos.x + col * (this.colWidth + this.padWidth);
31553             
31554             y = maxY[col];
31555             
31556             var positions = [];
31557             
31558             switch (box.length){
31559                 case 1 :
31560                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31561                     break;
31562                 case 2 :
31563                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31564                     break;
31565                 case 3 :
31566                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31567                     break;
31568                 case 4 :
31569                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31570                     break;
31571                 default :
31572                     break;
31573             }
31574             
31575             Roo.each(box, function(b,kk){
31576                 
31577                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31578                 
31579                 var sz = b.el.getSize();
31580                 
31581                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31582                 
31583             }, this);
31584             
31585         }, this);
31586         
31587         var mY = 0;
31588         
31589         for (var i = 0; i < this.cols; i++){
31590             mY = Math.max(mY, maxY[i]);
31591         }
31592         
31593         this.el.setHeight(mY - pos.y);
31594         
31595     },
31596     
31597 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31598 //    {
31599 //        var pos = this.el.getBox(true);
31600 //        var x = pos.x;
31601 //        var y = pos.y;
31602 //        var maxX = pos.right;
31603 //        
31604 //        var maxHeight = 0;
31605 //        
31606 //        Roo.each(items, function(item, k){
31607 //            
31608 //            var c = k % 2;
31609 //            
31610 //            item.el.position('absolute');
31611 //                
31612 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31613 //
31614 //            item.el.setWidth(width);
31615 //
31616 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31617 //
31618 //            item.el.setHeight(height);
31619 //            
31620 //            if(c == 0){
31621 //                item.el.setXY([x, y], isInstant ? false : true);
31622 //            } else {
31623 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31624 //            }
31625 //            
31626 //            y = y + height + this.alternativePadWidth;
31627 //            
31628 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31629 //            
31630 //        }, this);
31631 //        
31632 //        this.el.setHeight(maxHeight);
31633 //        
31634 //    },
31635     
31636     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31637     {
31638         var pos = this.el.getBox(true);
31639         
31640         var minX = pos.x;
31641         var minY = pos.y;
31642         
31643         var maxX = pos.right;
31644         
31645         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31646         
31647         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31648         
31649         Roo.each(queue, function(box, k){
31650             
31651             Roo.each(box, function(b, kk){
31652                 
31653                 b.el.position('absolute');
31654                 
31655                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31656                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31657                 
31658                 if(b.size == 'md-left' || b.size == 'md-right'){
31659                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31660                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31661                 }
31662                 
31663                 b.el.setWidth(width);
31664                 b.el.setHeight(height);
31665                 
31666             }, this);
31667             
31668             if(!box.length){
31669                 return;
31670             }
31671             
31672             var positions = [];
31673             
31674             switch (box.length){
31675                 case 1 :
31676                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31677                     break;
31678                 case 2 :
31679                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31680                     break;
31681                 case 3 :
31682                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31683                     break;
31684                 case 4 :
31685                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31686                     break;
31687                 default :
31688                     break;
31689             }
31690             
31691             Roo.each(box, function(b,kk){
31692                 
31693                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31694                 
31695                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31696                 
31697             }, this);
31698             
31699         }, this);
31700         
31701     },
31702     
31703     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31704     {
31705         Roo.each(eItems, function(b,k){
31706             
31707             b.size = (k == 0) ? 'sm' : 'xs';
31708             b.x = (k == 0) ? 2 : 1;
31709             b.y = (k == 0) ? 2 : 1;
31710             
31711             b.el.position('absolute');
31712             
31713             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31714                 
31715             b.el.setWidth(width);
31716             
31717             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31718             
31719             b.el.setHeight(height);
31720             
31721         }, this);
31722
31723         var positions = [];
31724         
31725         positions.push({
31726             x : maxX - this.unitWidth * 2 - this.gutter,
31727             y : minY
31728         });
31729         
31730         positions.push({
31731             x : maxX - this.unitWidth,
31732             y : minY + (this.unitWidth + this.gutter) * 2
31733         });
31734         
31735         positions.push({
31736             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31737             y : minY
31738         });
31739         
31740         Roo.each(eItems, function(b,k){
31741             
31742             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31743
31744         }, this);
31745         
31746     },
31747     
31748     getVerticalOneBoxColPositions : function(x, y, box)
31749     {
31750         var pos = [];
31751         
31752         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31753         
31754         if(box[0].size == 'md-left'){
31755             rand = 0;
31756         }
31757         
31758         if(box[0].size == 'md-right'){
31759             rand = 1;
31760         }
31761         
31762         pos.push({
31763             x : x + (this.unitWidth + this.gutter) * rand,
31764             y : y
31765         });
31766         
31767         return pos;
31768     },
31769     
31770     getVerticalTwoBoxColPositions : function(x, y, box)
31771     {
31772         var pos = [];
31773         
31774         if(box[0].size == 'xs'){
31775             
31776             pos.push({
31777                 x : x,
31778                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31779             });
31780
31781             pos.push({
31782                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31783                 y : y
31784             });
31785             
31786             return pos;
31787             
31788         }
31789         
31790         pos.push({
31791             x : x,
31792             y : y
31793         });
31794
31795         pos.push({
31796             x : x + (this.unitWidth + this.gutter) * 2,
31797             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31798         });
31799         
31800         return pos;
31801         
31802     },
31803     
31804     getVerticalThreeBoxColPositions : function(x, y, box)
31805     {
31806         var pos = [];
31807         
31808         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31809             
31810             pos.push({
31811                 x : x,
31812                 y : y
31813             });
31814
31815             pos.push({
31816                 x : x + (this.unitWidth + this.gutter) * 1,
31817                 y : y
31818             });
31819             
31820             pos.push({
31821                 x : x + (this.unitWidth + this.gutter) * 2,
31822                 y : y
31823             });
31824             
31825             return pos;
31826             
31827         }
31828         
31829         if(box[0].size == 'xs' && box[1].size == 'xs'){
31830             
31831             pos.push({
31832                 x : x,
31833                 y : y
31834             });
31835
31836             pos.push({
31837                 x : x,
31838                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31839             });
31840             
31841             pos.push({
31842                 x : x + (this.unitWidth + this.gutter) * 1,
31843                 y : y
31844             });
31845             
31846             return pos;
31847             
31848         }
31849         
31850         pos.push({
31851             x : x,
31852             y : y
31853         });
31854
31855         pos.push({
31856             x : x + (this.unitWidth + this.gutter) * 2,
31857             y : y
31858         });
31859
31860         pos.push({
31861             x : x + (this.unitWidth + this.gutter) * 2,
31862             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31863         });
31864             
31865         return pos;
31866         
31867     },
31868     
31869     getVerticalFourBoxColPositions : function(x, y, box)
31870     {
31871         var pos = [];
31872         
31873         if(box[0].size == 'xs'){
31874             
31875             pos.push({
31876                 x : x,
31877                 y : y
31878             });
31879
31880             pos.push({
31881                 x : x,
31882                 y : y + (this.unitHeight + this.gutter) * 1
31883             });
31884             
31885             pos.push({
31886                 x : x,
31887                 y : y + (this.unitHeight + this.gutter) * 2
31888             });
31889             
31890             pos.push({
31891                 x : x + (this.unitWidth + this.gutter) * 1,
31892                 y : y
31893             });
31894             
31895             return pos;
31896             
31897         }
31898         
31899         pos.push({
31900             x : x,
31901             y : y
31902         });
31903
31904         pos.push({
31905             x : x + (this.unitWidth + this.gutter) * 2,
31906             y : y
31907         });
31908
31909         pos.push({
31910             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31911             y : y + (this.unitHeight + this.gutter) * 1
31912         });
31913
31914         pos.push({
31915             x : x + (this.unitWidth + this.gutter) * 2,
31916             y : y + (this.unitWidth + this.gutter) * 2
31917         });
31918
31919         return pos;
31920         
31921     },
31922     
31923     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31924     {
31925         var pos = [];
31926         
31927         if(box[0].size == 'md-left'){
31928             pos.push({
31929                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31930                 y : minY
31931             });
31932             
31933             return pos;
31934         }
31935         
31936         if(box[0].size == 'md-right'){
31937             pos.push({
31938                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31939                 y : minY + (this.unitWidth + this.gutter) * 1
31940             });
31941             
31942             return pos;
31943         }
31944         
31945         var rand = Math.floor(Math.random() * (4 - box[0].y));
31946         
31947         pos.push({
31948             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31949             y : minY + (this.unitWidth + this.gutter) * rand
31950         });
31951         
31952         return pos;
31953         
31954     },
31955     
31956     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31957     {
31958         var pos = [];
31959         
31960         if(box[0].size == 'xs'){
31961             
31962             pos.push({
31963                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31964                 y : minY
31965             });
31966
31967             pos.push({
31968                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31969                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31970             });
31971             
31972             return pos;
31973             
31974         }
31975         
31976         pos.push({
31977             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31978             y : minY
31979         });
31980
31981         pos.push({
31982             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31983             y : minY + (this.unitWidth + this.gutter) * 2
31984         });
31985         
31986         return pos;
31987         
31988     },
31989     
31990     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31991     {
31992         var pos = [];
31993         
31994         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31995             
31996             pos.push({
31997                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31998                 y : minY
31999             });
32000
32001             pos.push({
32002                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32003                 y : minY + (this.unitWidth + this.gutter) * 1
32004             });
32005             
32006             pos.push({
32007                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32008                 y : minY + (this.unitWidth + this.gutter) * 2
32009             });
32010             
32011             return pos;
32012             
32013         }
32014         
32015         if(box[0].size == 'xs' && box[1].size == 'xs'){
32016             
32017             pos.push({
32018                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32019                 y : minY
32020             });
32021
32022             pos.push({
32023                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32024                 y : minY
32025             });
32026             
32027             pos.push({
32028                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32029                 y : minY + (this.unitWidth + this.gutter) * 1
32030             });
32031             
32032             return pos;
32033             
32034         }
32035         
32036         pos.push({
32037             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32038             y : minY
32039         });
32040
32041         pos.push({
32042             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32043             y : minY + (this.unitWidth + this.gutter) * 2
32044         });
32045
32046         pos.push({
32047             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32048             y : minY + (this.unitWidth + this.gutter) * 2
32049         });
32050             
32051         return pos;
32052         
32053     },
32054     
32055     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32056     {
32057         var pos = [];
32058         
32059         if(box[0].size == 'xs'){
32060             
32061             pos.push({
32062                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32063                 y : minY
32064             });
32065
32066             pos.push({
32067                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32068                 y : minY
32069             });
32070             
32071             pos.push({
32072                 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),
32073                 y : minY
32074             });
32075             
32076             pos.push({
32077                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32078                 y : minY + (this.unitWidth + this.gutter) * 1
32079             });
32080             
32081             return pos;
32082             
32083         }
32084         
32085         pos.push({
32086             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32087             y : minY
32088         });
32089         
32090         pos.push({
32091             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32092             y : minY + (this.unitWidth + this.gutter) * 2
32093         });
32094         
32095         pos.push({
32096             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32097             y : minY + (this.unitWidth + this.gutter) * 2
32098         });
32099         
32100         pos.push({
32101             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),
32102             y : minY + (this.unitWidth + this.gutter) * 2
32103         });
32104
32105         return pos;
32106         
32107     },
32108     
32109     /**
32110     * remove a Masonry Brick
32111     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32112     */
32113     removeBrick : function(brick_id)
32114     {
32115         if (!brick_id) {
32116             return;
32117         }
32118         
32119         for (var i = 0; i<this.bricks.length; i++) {
32120             if (this.bricks[i].id == brick_id) {
32121                 this.bricks.splice(i,1);
32122                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32123                 this.initial();
32124             }
32125         }
32126     },
32127     
32128     /**
32129     * adds a Masonry Brick
32130     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32131     */
32132     addBrick : function(cfg)
32133     {
32134         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32135         //this.register(cn);
32136         cn.parentId = this.id;
32137         cn.render(this.el);
32138         return cn;
32139     },
32140     
32141     /**
32142     * register a Masonry Brick
32143     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32144     */
32145     
32146     register : function(brick)
32147     {
32148         this.bricks.push(brick);
32149         brick.masonryId = this.id;
32150     },
32151     
32152     /**
32153     * clear all the Masonry Brick
32154     */
32155     clearAll : function()
32156     {
32157         this.bricks = [];
32158         //this.getChildContainer().dom.innerHTML = "";
32159         this.el.dom.innerHTML = '';
32160     },
32161     
32162     getSelected : function()
32163     {
32164         if (!this.selectedBrick) {
32165             return false;
32166         }
32167         
32168         return this.selectedBrick;
32169     }
32170 });
32171
32172 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32173     
32174     groups: {},
32175      /**
32176     * register a Masonry Layout
32177     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32178     */
32179     
32180     register : function(layout)
32181     {
32182         this.groups[layout.id] = layout;
32183     },
32184     /**
32185     * fetch a  Masonry Layout based on the masonry layout ID
32186     * @param {string} the masonry layout to add
32187     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32188     */
32189     
32190     get: function(layout_id) {
32191         if (typeof(this.groups[layout_id]) == 'undefined') {
32192             return false;
32193         }
32194         return this.groups[layout_id] ;
32195     }
32196     
32197     
32198     
32199 });
32200
32201  
32202
32203  /**
32204  *
32205  * This is based on 
32206  * http://masonry.desandro.com
32207  *
32208  * The idea is to render all the bricks based on vertical width...
32209  *
32210  * The original code extends 'outlayer' - we might need to use that....
32211  * 
32212  */
32213
32214
32215 /**
32216  * @class Roo.bootstrap.LayoutMasonryAuto
32217  * @extends Roo.bootstrap.Component
32218  * Bootstrap Layout Masonry class
32219  * 
32220  * @constructor
32221  * Create a new Element
32222  * @param {Object} config The config object
32223  */
32224
32225 Roo.bootstrap.LayoutMasonryAuto = function(config){
32226     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32227 };
32228
32229 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32230     
32231       /**
32232      * @cfg {Boolean} isFitWidth  - resize the width..
32233      */   
32234     isFitWidth : false,  // options..
32235     /**
32236      * @cfg {Boolean} isOriginLeft = left align?
32237      */   
32238     isOriginLeft : true,
32239     /**
32240      * @cfg {Boolean} isOriginTop = top align?
32241      */   
32242     isOriginTop : false,
32243     /**
32244      * @cfg {Boolean} isLayoutInstant = no animation?
32245      */   
32246     isLayoutInstant : false, // needed?
32247     /**
32248      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32249      */   
32250     isResizingContainer : true,
32251     /**
32252      * @cfg {Number} columnWidth  width of the columns 
32253      */   
32254     
32255     columnWidth : 0,
32256     
32257     /**
32258      * @cfg {Number} maxCols maximum number of columns
32259      */   
32260     
32261     maxCols: 0,
32262     /**
32263      * @cfg {Number} padHeight padding below box..
32264      */   
32265     
32266     padHeight : 10, 
32267     
32268     /**
32269      * @cfg {Boolean} isAutoInitial defalut true
32270      */   
32271     
32272     isAutoInitial : true, 
32273     
32274     // private?
32275     gutter : 0,
32276     
32277     containerWidth: 0,
32278     initialColumnWidth : 0,
32279     currentSize : null,
32280     
32281     colYs : null, // array.
32282     maxY : 0,
32283     padWidth: 10,
32284     
32285     
32286     tag: 'div',
32287     cls: '',
32288     bricks: null, //CompositeElement
32289     cols : 0, // array?
32290     // element : null, // wrapped now this.el
32291     _isLayoutInited : null, 
32292     
32293     
32294     getAutoCreate : function(){
32295         
32296         var cfg = {
32297             tag: this.tag,
32298             cls: 'blog-masonary-wrapper ' + this.cls,
32299             cn : {
32300                 cls : 'mas-boxes masonary'
32301             }
32302         };
32303         
32304         return cfg;
32305     },
32306     
32307     getChildContainer: function( )
32308     {
32309         if (this.boxesEl) {
32310             return this.boxesEl;
32311         }
32312         
32313         this.boxesEl = this.el.select('.mas-boxes').first();
32314         
32315         return this.boxesEl;
32316     },
32317     
32318     
32319     initEvents : function()
32320     {
32321         var _this = this;
32322         
32323         if(this.isAutoInitial){
32324             Roo.log('hook children rendered');
32325             this.on('childrenrendered', function() {
32326                 Roo.log('children rendered');
32327                 _this.initial();
32328             } ,this);
32329         }
32330         
32331     },
32332     
32333     initial : function()
32334     {
32335         this.reloadItems();
32336
32337         this.currentSize = this.el.getBox(true);
32338
32339         /// was window resize... - let's see if this works..
32340         Roo.EventManager.onWindowResize(this.resize, this); 
32341
32342         if(!this.isAutoInitial){
32343             this.layout();
32344             return;
32345         }
32346         
32347         this.layout.defer(500,this);
32348     },
32349     
32350     reloadItems: function()
32351     {
32352         this.bricks = this.el.select('.masonry-brick', true);
32353         
32354         this.bricks.each(function(b) {
32355             //Roo.log(b.getSize());
32356             if (!b.attr('originalwidth')) {
32357                 b.attr('originalwidth',  b.getSize().width);
32358             }
32359             
32360         });
32361         
32362         Roo.log(this.bricks.elements.length);
32363     },
32364     
32365     resize : function()
32366     {
32367         Roo.log('resize');
32368         var cs = this.el.getBox(true);
32369         
32370         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32371             Roo.log("no change in with or X");
32372             return;
32373         }
32374         this.currentSize = cs;
32375         this.layout();
32376     },
32377     
32378     layout : function()
32379     {
32380          Roo.log('layout');
32381         this._resetLayout();
32382         //this._manageStamps();
32383       
32384         // don't animate first layout
32385         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32386         this.layoutItems( isInstant );
32387       
32388         // flag for initalized
32389         this._isLayoutInited = true;
32390     },
32391     
32392     layoutItems : function( isInstant )
32393     {
32394         //var items = this._getItemsForLayout( this.items );
32395         // original code supports filtering layout items.. we just ignore it..
32396         
32397         this._layoutItems( this.bricks , isInstant );
32398       
32399         this._postLayout();
32400     },
32401     _layoutItems : function ( items , isInstant)
32402     {
32403        //this.fireEvent( 'layout', this, items );
32404     
32405
32406         if ( !items || !items.elements.length ) {
32407           // no items, emit event with empty array
32408             return;
32409         }
32410
32411         var queue = [];
32412         items.each(function(item) {
32413             Roo.log("layout item");
32414             Roo.log(item);
32415             // get x/y object from method
32416             var position = this._getItemLayoutPosition( item );
32417             // enqueue
32418             position.item = item;
32419             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32420             queue.push( position );
32421         }, this);
32422       
32423         this._processLayoutQueue( queue );
32424     },
32425     /** Sets position of item in DOM
32426     * @param {Element} item
32427     * @param {Number} x - horizontal position
32428     * @param {Number} y - vertical position
32429     * @param {Boolean} isInstant - disables transitions
32430     */
32431     _processLayoutQueue : function( queue )
32432     {
32433         for ( var i=0, len = queue.length; i < len; i++ ) {
32434             var obj = queue[i];
32435             obj.item.position('absolute');
32436             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32437         }
32438     },
32439       
32440     
32441     /**
32442     * Any logic you want to do after each layout,
32443     * i.e. size the container
32444     */
32445     _postLayout : function()
32446     {
32447         this.resizeContainer();
32448     },
32449     
32450     resizeContainer : function()
32451     {
32452         if ( !this.isResizingContainer ) {
32453             return;
32454         }
32455         var size = this._getContainerSize();
32456         if ( size ) {
32457             this.el.setSize(size.width,size.height);
32458             this.boxesEl.setSize(size.width,size.height);
32459         }
32460     },
32461     
32462     
32463     
32464     _resetLayout : function()
32465     {
32466         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32467         this.colWidth = this.el.getWidth();
32468         //this.gutter = this.el.getWidth(); 
32469         
32470         this.measureColumns();
32471
32472         // reset column Y
32473         var i = this.cols;
32474         this.colYs = [];
32475         while (i--) {
32476             this.colYs.push( 0 );
32477         }
32478     
32479         this.maxY = 0;
32480     },
32481
32482     measureColumns : function()
32483     {
32484         this.getContainerWidth();
32485       // if columnWidth is 0, default to outerWidth of first item
32486         if ( !this.columnWidth ) {
32487             var firstItem = this.bricks.first();
32488             Roo.log(firstItem);
32489             this.columnWidth  = this.containerWidth;
32490             if (firstItem && firstItem.attr('originalwidth') ) {
32491                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32492             }
32493             // columnWidth fall back to item of first element
32494             Roo.log("set column width?");
32495                         this.initialColumnWidth = this.columnWidth  ;
32496
32497             // if first elem has no width, default to size of container
32498             
32499         }
32500         
32501         
32502         if (this.initialColumnWidth) {
32503             this.columnWidth = this.initialColumnWidth;
32504         }
32505         
32506         
32507             
32508         // column width is fixed at the top - however if container width get's smaller we should
32509         // reduce it...
32510         
32511         // this bit calcs how man columns..
32512             
32513         var columnWidth = this.columnWidth += this.gutter;
32514       
32515         // calculate columns
32516         var containerWidth = this.containerWidth + this.gutter;
32517         
32518         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32519         // fix rounding errors, typically with gutters
32520         var excess = columnWidth - containerWidth % columnWidth;
32521         
32522         
32523         // if overshoot is less than a pixel, round up, otherwise floor it
32524         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32525         cols = Math[ mathMethod ]( cols );
32526         this.cols = Math.max( cols, 1 );
32527         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32528         
32529          // padding positioning..
32530         var totalColWidth = this.cols * this.columnWidth;
32531         var padavail = this.containerWidth - totalColWidth;
32532         // so for 2 columns - we need 3 'pads'
32533         
32534         var padNeeded = (1+this.cols) * this.padWidth;
32535         
32536         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32537         
32538         this.columnWidth += padExtra
32539         //this.padWidth = Math.floor(padavail /  ( this.cols));
32540         
32541         // adjust colum width so that padding is fixed??
32542         
32543         // we have 3 columns ... total = width * 3
32544         // we have X left over... that should be used by 
32545         
32546         //if (this.expandC) {
32547             
32548         //}
32549         
32550         
32551         
32552     },
32553     
32554     getContainerWidth : function()
32555     {
32556        /* // container is parent if fit width
32557         var container = this.isFitWidth ? this.element.parentNode : this.element;
32558         // check that this.size and size are there
32559         // IE8 triggers resize on body size change, so they might not be
32560         
32561         var size = getSize( container );  //FIXME
32562         this.containerWidth = size && size.innerWidth; //FIXME
32563         */
32564          
32565         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32566         
32567     },
32568     
32569     _getItemLayoutPosition : function( item )  // what is item?
32570     {
32571         // we resize the item to our columnWidth..
32572       
32573         item.setWidth(this.columnWidth);
32574         item.autoBoxAdjust  = false;
32575         
32576         var sz = item.getSize();
32577  
32578         // how many columns does this brick span
32579         var remainder = this.containerWidth % this.columnWidth;
32580         
32581         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32582         // round if off by 1 pixel, otherwise use ceil
32583         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32584         colSpan = Math.min( colSpan, this.cols );
32585         
32586         // normally this should be '1' as we dont' currently allow multi width columns..
32587         
32588         var colGroup = this._getColGroup( colSpan );
32589         // get the minimum Y value from the columns
32590         var minimumY = Math.min.apply( Math, colGroup );
32591         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32592         
32593         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32594          
32595         // position the brick
32596         var position = {
32597             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32598             y: this.currentSize.y + minimumY + this.padHeight
32599         };
32600         
32601         Roo.log(position);
32602         // apply setHeight to necessary columns
32603         var setHeight = minimumY + sz.height + this.padHeight;
32604         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32605         
32606         var setSpan = this.cols + 1 - colGroup.length;
32607         for ( var i = 0; i < setSpan; i++ ) {
32608           this.colYs[ shortColIndex + i ] = setHeight ;
32609         }
32610       
32611         return position;
32612     },
32613     
32614     /**
32615      * @param {Number} colSpan - number of columns the element spans
32616      * @returns {Array} colGroup
32617      */
32618     _getColGroup : function( colSpan )
32619     {
32620         if ( colSpan < 2 ) {
32621           // if brick spans only one column, use all the column Ys
32622           return this.colYs;
32623         }
32624       
32625         var colGroup = [];
32626         // how many different places could this brick fit horizontally
32627         var groupCount = this.cols + 1 - colSpan;
32628         // for each group potential horizontal position
32629         for ( var i = 0; i < groupCount; i++ ) {
32630           // make an array of colY values for that one group
32631           var groupColYs = this.colYs.slice( i, i + colSpan );
32632           // and get the max value of the array
32633           colGroup[i] = Math.max.apply( Math, groupColYs );
32634         }
32635         return colGroup;
32636     },
32637     /*
32638     _manageStamp : function( stamp )
32639     {
32640         var stampSize =  stamp.getSize();
32641         var offset = stamp.getBox();
32642         // get the columns that this stamp affects
32643         var firstX = this.isOriginLeft ? offset.x : offset.right;
32644         var lastX = firstX + stampSize.width;
32645         var firstCol = Math.floor( firstX / this.columnWidth );
32646         firstCol = Math.max( 0, firstCol );
32647         
32648         var lastCol = Math.floor( lastX / this.columnWidth );
32649         // lastCol should not go over if multiple of columnWidth #425
32650         lastCol -= lastX % this.columnWidth ? 0 : 1;
32651         lastCol = Math.min( this.cols - 1, lastCol );
32652         
32653         // set colYs to bottom of the stamp
32654         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32655             stampSize.height;
32656             
32657         for ( var i = firstCol; i <= lastCol; i++ ) {
32658           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32659         }
32660     },
32661     */
32662     
32663     _getContainerSize : function()
32664     {
32665         this.maxY = Math.max.apply( Math, this.colYs );
32666         var size = {
32667             height: this.maxY
32668         };
32669       
32670         if ( this.isFitWidth ) {
32671             size.width = this._getContainerFitWidth();
32672         }
32673       
32674         return size;
32675     },
32676     
32677     _getContainerFitWidth : function()
32678     {
32679         var unusedCols = 0;
32680         // count unused columns
32681         var i = this.cols;
32682         while ( --i ) {
32683           if ( this.colYs[i] !== 0 ) {
32684             break;
32685           }
32686           unusedCols++;
32687         }
32688         // fit container to columns that have been used
32689         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32690     },
32691     
32692     needsResizeLayout : function()
32693     {
32694         var previousWidth = this.containerWidth;
32695         this.getContainerWidth();
32696         return previousWidth !== this.containerWidth;
32697     }
32698  
32699 });
32700
32701  
32702
32703  /*
32704  * - LGPL
32705  *
32706  * element
32707  * 
32708  */
32709
32710 /**
32711  * @class Roo.bootstrap.MasonryBrick
32712  * @extends Roo.bootstrap.Component
32713  * Bootstrap MasonryBrick class
32714  * 
32715  * @constructor
32716  * Create a new MasonryBrick
32717  * @param {Object} config The config object
32718  */
32719
32720 Roo.bootstrap.MasonryBrick = function(config){
32721     
32722     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32723     
32724     Roo.bootstrap.MasonryBrick.register(this);
32725     
32726     this.addEvents({
32727         // raw events
32728         /**
32729          * @event click
32730          * When a MasonryBrick is clcik
32731          * @param {Roo.bootstrap.MasonryBrick} this
32732          * @param {Roo.EventObject} e
32733          */
32734         "click" : true
32735     });
32736 };
32737
32738 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32739     
32740     /**
32741      * @cfg {String} title
32742      */   
32743     title : '',
32744     /**
32745      * @cfg {String} html
32746      */   
32747     html : '',
32748     /**
32749      * @cfg {String} bgimage
32750      */   
32751     bgimage : '',
32752     /**
32753      * @cfg {String} videourl
32754      */   
32755     videourl : '',
32756     /**
32757      * @cfg {String} cls
32758      */   
32759     cls : '',
32760     /**
32761      * @cfg {String} href
32762      */   
32763     href : '',
32764     /**
32765      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32766      */   
32767     size : 'xs',
32768     
32769     /**
32770      * @cfg {String} placetitle (center|bottom)
32771      */   
32772     placetitle : '',
32773     
32774     /**
32775      * @cfg {Boolean} isFitContainer defalut true
32776      */   
32777     isFitContainer : true, 
32778     
32779     /**
32780      * @cfg {Boolean} preventDefault defalut false
32781      */   
32782     preventDefault : false, 
32783     
32784     /**
32785      * @cfg {Boolean} inverse defalut false
32786      */   
32787     maskInverse : false, 
32788     
32789     getAutoCreate : function()
32790     {
32791         if(!this.isFitContainer){
32792             return this.getSplitAutoCreate();
32793         }
32794         
32795         var cls = 'masonry-brick masonry-brick-full';
32796         
32797         if(this.href.length){
32798             cls += ' masonry-brick-link';
32799         }
32800         
32801         if(this.bgimage.length){
32802             cls += ' masonry-brick-image';
32803         }
32804         
32805         if(this.maskInverse){
32806             cls += ' mask-inverse';
32807         }
32808         
32809         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32810             cls += ' enable-mask';
32811         }
32812         
32813         if(this.size){
32814             cls += ' masonry-' + this.size + '-brick';
32815         }
32816         
32817         if(this.placetitle.length){
32818             
32819             switch (this.placetitle) {
32820                 case 'center' :
32821                     cls += ' masonry-center-title';
32822                     break;
32823                 case 'bottom' :
32824                     cls += ' masonry-bottom-title';
32825                     break;
32826                 default:
32827                     break;
32828             }
32829             
32830         } else {
32831             if(!this.html.length && !this.bgimage.length){
32832                 cls += ' masonry-center-title';
32833             }
32834
32835             if(!this.html.length && this.bgimage.length){
32836                 cls += ' masonry-bottom-title';
32837             }
32838         }
32839         
32840         if(this.cls){
32841             cls += ' ' + this.cls;
32842         }
32843         
32844         var cfg = {
32845             tag: (this.href.length) ? 'a' : 'div',
32846             cls: cls,
32847             cn: [
32848                 {
32849                     tag: 'div',
32850                     cls: 'masonry-brick-mask'
32851                 },
32852                 {
32853                     tag: 'div',
32854                     cls: 'masonry-brick-paragraph',
32855                     cn: []
32856                 }
32857             ]
32858         };
32859         
32860         if(this.href.length){
32861             cfg.href = this.href;
32862         }
32863         
32864         var cn = cfg.cn[1].cn;
32865         
32866         if(this.title.length){
32867             cn.push({
32868                 tag: 'h4',
32869                 cls: 'masonry-brick-title',
32870                 html: this.title
32871             });
32872         }
32873         
32874         if(this.html.length){
32875             cn.push({
32876                 tag: 'p',
32877                 cls: 'masonry-brick-text',
32878                 html: this.html
32879             });
32880         }
32881         
32882         if (!this.title.length && !this.html.length) {
32883             cfg.cn[1].cls += ' hide';
32884         }
32885         
32886         if(this.bgimage.length){
32887             cfg.cn.push({
32888                 tag: 'img',
32889                 cls: 'masonry-brick-image-view',
32890                 src: this.bgimage
32891             });
32892         }
32893         
32894         if(this.videourl.length){
32895             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32896             // youtube support only?
32897             cfg.cn.push({
32898                 tag: 'iframe',
32899                 cls: 'masonry-brick-image-view',
32900                 src: vurl,
32901                 frameborder : 0,
32902                 allowfullscreen : true
32903             });
32904         }
32905         
32906         return cfg;
32907         
32908     },
32909     
32910     getSplitAutoCreate : function()
32911     {
32912         var cls = 'masonry-brick masonry-brick-split';
32913         
32914         if(this.href.length){
32915             cls += ' masonry-brick-link';
32916         }
32917         
32918         if(this.bgimage.length){
32919             cls += ' masonry-brick-image';
32920         }
32921         
32922         if(this.size){
32923             cls += ' masonry-' + this.size + '-brick';
32924         }
32925         
32926         switch (this.placetitle) {
32927             case 'center' :
32928                 cls += ' masonry-center-title';
32929                 break;
32930             case 'bottom' :
32931                 cls += ' masonry-bottom-title';
32932                 break;
32933             default:
32934                 if(!this.bgimage.length){
32935                     cls += ' masonry-center-title';
32936                 }
32937
32938                 if(this.bgimage.length){
32939                     cls += ' masonry-bottom-title';
32940                 }
32941                 break;
32942         }
32943         
32944         if(this.cls){
32945             cls += ' ' + this.cls;
32946         }
32947         
32948         var cfg = {
32949             tag: (this.href.length) ? 'a' : 'div',
32950             cls: cls,
32951             cn: [
32952                 {
32953                     tag: 'div',
32954                     cls: 'masonry-brick-split-head',
32955                     cn: [
32956                         {
32957                             tag: 'div',
32958                             cls: 'masonry-brick-paragraph',
32959                             cn: []
32960                         }
32961                     ]
32962                 },
32963                 {
32964                     tag: 'div',
32965                     cls: 'masonry-brick-split-body',
32966                     cn: []
32967                 }
32968             ]
32969         };
32970         
32971         if(this.href.length){
32972             cfg.href = this.href;
32973         }
32974         
32975         if(this.title.length){
32976             cfg.cn[0].cn[0].cn.push({
32977                 tag: 'h4',
32978                 cls: 'masonry-brick-title',
32979                 html: this.title
32980             });
32981         }
32982         
32983         if(this.html.length){
32984             cfg.cn[1].cn.push({
32985                 tag: 'p',
32986                 cls: 'masonry-brick-text',
32987                 html: this.html
32988             });
32989         }
32990
32991         if(this.bgimage.length){
32992             cfg.cn[0].cn.push({
32993                 tag: 'img',
32994                 cls: 'masonry-brick-image-view',
32995                 src: this.bgimage
32996             });
32997         }
32998         
32999         if(this.videourl.length){
33000             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33001             // youtube support only?
33002             cfg.cn[0].cn.cn.push({
33003                 tag: 'iframe',
33004                 cls: 'masonry-brick-image-view',
33005                 src: vurl,
33006                 frameborder : 0,
33007                 allowfullscreen : true
33008             });
33009         }
33010         
33011         return cfg;
33012     },
33013     
33014     initEvents: function() 
33015     {
33016         switch (this.size) {
33017             case 'xs' :
33018                 this.x = 1;
33019                 this.y = 1;
33020                 break;
33021             case 'sm' :
33022                 this.x = 2;
33023                 this.y = 2;
33024                 break;
33025             case 'md' :
33026             case 'md-left' :
33027             case 'md-right' :
33028                 this.x = 3;
33029                 this.y = 3;
33030                 break;
33031             case 'tall' :
33032                 this.x = 2;
33033                 this.y = 3;
33034                 break;
33035             case 'wide' :
33036                 this.x = 3;
33037                 this.y = 2;
33038                 break;
33039             case 'wide-thin' :
33040                 this.x = 3;
33041                 this.y = 1;
33042                 break;
33043                         
33044             default :
33045                 break;
33046         }
33047         
33048         if(Roo.isTouch){
33049             this.el.on('touchstart', this.onTouchStart, this);
33050             this.el.on('touchmove', this.onTouchMove, this);
33051             this.el.on('touchend', this.onTouchEnd, this);
33052             this.el.on('contextmenu', this.onContextMenu, this);
33053         } else {
33054             this.el.on('mouseenter'  ,this.enter, this);
33055             this.el.on('mouseleave', this.leave, this);
33056             this.el.on('click', this.onClick, this);
33057         }
33058         
33059         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33060             this.parent().bricks.push(this);   
33061         }
33062         
33063     },
33064     
33065     onClick: function(e, el)
33066     {
33067         var time = this.endTimer - this.startTimer;
33068         // Roo.log(e.preventDefault());
33069         if(Roo.isTouch){
33070             if(time > 1000){
33071                 e.preventDefault();
33072                 return;
33073             }
33074         }
33075         
33076         if(!this.preventDefault){
33077             return;
33078         }
33079         
33080         e.preventDefault();
33081         
33082         if (this.activeClass != '') {
33083             this.selectBrick();
33084         }
33085         
33086         this.fireEvent('click', this, e);
33087     },
33088     
33089     enter: function(e, el)
33090     {
33091         e.preventDefault();
33092         
33093         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33094             return;
33095         }
33096         
33097         if(this.bgimage.length && this.html.length){
33098             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33099         }
33100     },
33101     
33102     leave: function(e, el)
33103     {
33104         e.preventDefault();
33105         
33106         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33107             return;
33108         }
33109         
33110         if(this.bgimage.length && this.html.length){
33111             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33112         }
33113     },
33114     
33115     onTouchStart: function(e, el)
33116     {
33117 //        e.preventDefault();
33118         
33119         this.touchmoved = false;
33120         
33121         if(!this.isFitContainer){
33122             return;
33123         }
33124         
33125         if(!this.bgimage.length || !this.html.length){
33126             return;
33127         }
33128         
33129         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33130         
33131         this.timer = new Date().getTime();
33132         
33133     },
33134     
33135     onTouchMove: function(e, el)
33136     {
33137         this.touchmoved = true;
33138     },
33139     
33140     onContextMenu : function(e,el)
33141     {
33142         e.preventDefault();
33143         e.stopPropagation();
33144         return false;
33145     },
33146     
33147     onTouchEnd: function(e, el)
33148     {
33149 //        e.preventDefault();
33150         
33151         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33152         
33153             this.leave(e,el);
33154             
33155             return;
33156         }
33157         
33158         if(!this.bgimage.length || !this.html.length){
33159             
33160             if(this.href.length){
33161                 window.location.href = this.href;
33162             }
33163             
33164             return;
33165         }
33166         
33167         if(!this.isFitContainer){
33168             return;
33169         }
33170         
33171         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33172         
33173         window.location.href = this.href;
33174     },
33175     
33176     //selection on single brick only
33177     selectBrick : function() {
33178         
33179         if (!this.parentId) {
33180             return;
33181         }
33182         
33183         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33184         var index = m.selectedBrick.indexOf(this.id);
33185         
33186         if ( index > -1) {
33187             m.selectedBrick.splice(index,1);
33188             this.el.removeClass(this.activeClass);
33189             return;
33190         }
33191         
33192         for(var i = 0; i < m.selectedBrick.length; i++) {
33193             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33194             b.el.removeClass(b.activeClass);
33195         }
33196         
33197         m.selectedBrick = [];
33198         
33199         m.selectedBrick.push(this.id);
33200         this.el.addClass(this.activeClass);
33201         return;
33202     },
33203     
33204     isSelected : function(){
33205         return this.el.hasClass(this.activeClass);
33206         
33207     }
33208 });
33209
33210 Roo.apply(Roo.bootstrap.MasonryBrick, {
33211     
33212     //groups: {},
33213     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33214      /**
33215     * register a Masonry Brick
33216     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33217     */
33218     
33219     register : function(brick)
33220     {
33221         //this.groups[brick.id] = brick;
33222         this.groups.add(brick.id, brick);
33223     },
33224     /**
33225     * fetch a  masonry brick based on the masonry brick ID
33226     * @param {string} the masonry brick to add
33227     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33228     */
33229     
33230     get: function(brick_id) 
33231     {
33232         // if (typeof(this.groups[brick_id]) == 'undefined') {
33233         //     return false;
33234         // }
33235         // return this.groups[brick_id] ;
33236         
33237         if(this.groups.key(brick_id)) {
33238             return this.groups.key(brick_id);
33239         }
33240         
33241         return false;
33242     }
33243     
33244     
33245     
33246 });
33247
33248  /*
33249  * - LGPL
33250  *
33251  * element
33252  * 
33253  */
33254
33255 /**
33256  * @class Roo.bootstrap.Brick
33257  * @extends Roo.bootstrap.Component
33258  * Bootstrap Brick class
33259  * 
33260  * @constructor
33261  * Create a new Brick
33262  * @param {Object} config The config object
33263  */
33264
33265 Roo.bootstrap.Brick = function(config){
33266     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33267     
33268     this.addEvents({
33269         // raw events
33270         /**
33271          * @event click
33272          * When a Brick is click
33273          * @param {Roo.bootstrap.Brick} this
33274          * @param {Roo.EventObject} e
33275          */
33276         "click" : true
33277     });
33278 };
33279
33280 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33281     
33282     /**
33283      * @cfg {String} title
33284      */   
33285     title : '',
33286     /**
33287      * @cfg {String} html
33288      */   
33289     html : '',
33290     /**
33291      * @cfg {String} bgimage
33292      */   
33293     bgimage : '',
33294     /**
33295      * @cfg {String} cls
33296      */   
33297     cls : '',
33298     /**
33299      * @cfg {String} href
33300      */   
33301     href : '',
33302     /**
33303      * @cfg {String} video
33304      */   
33305     video : '',
33306     /**
33307      * @cfg {Boolean} square
33308      */   
33309     square : true,
33310     
33311     getAutoCreate : function()
33312     {
33313         var cls = 'roo-brick';
33314         
33315         if(this.href.length){
33316             cls += ' roo-brick-link';
33317         }
33318         
33319         if(this.bgimage.length){
33320             cls += ' roo-brick-image';
33321         }
33322         
33323         if(!this.html.length && !this.bgimage.length){
33324             cls += ' roo-brick-center-title';
33325         }
33326         
33327         if(!this.html.length && this.bgimage.length){
33328             cls += ' roo-brick-bottom-title';
33329         }
33330         
33331         if(this.cls){
33332             cls += ' ' + this.cls;
33333         }
33334         
33335         var cfg = {
33336             tag: (this.href.length) ? 'a' : 'div',
33337             cls: cls,
33338             cn: [
33339                 {
33340                     tag: 'div',
33341                     cls: 'roo-brick-paragraph',
33342                     cn: []
33343                 }
33344             ]
33345         };
33346         
33347         if(this.href.length){
33348             cfg.href = this.href;
33349         }
33350         
33351         var cn = cfg.cn[0].cn;
33352         
33353         if(this.title.length){
33354             cn.push({
33355                 tag: 'h4',
33356                 cls: 'roo-brick-title',
33357                 html: this.title
33358             });
33359         }
33360         
33361         if(this.html.length){
33362             cn.push({
33363                 tag: 'p',
33364                 cls: 'roo-brick-text',
33365                 html: this.html
33366             });
33367         } else {
33368             cn.cls += ' hide';
33369         }
33370         
33371         if(this.bgimage.length){
33372             cfg.cn.push({
33373                 tag: 'img',
33374                 cls: 'roo-brick-image-view',
33375                 src: this.bgimage
33376             });
33377         }
33378         
33379         return cfg;
33380     },
33381     
33382     initEvents: function() 
33383     {
33384         if(this.title.length || this.html.length){
33385             this.el.on('mouseenter'  ,this.enter, this);
33386             this.el.on('mouseleave', this.leave, this);
33387         }
33388         
33389         Roo.EventManager.onWindowResize(this.resize, this); 
33390         
33391         if(this.bgimage.length){
33392             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33393             this.imageEl.on('load', this.onImageLoad, this);
33394             return;
33395         }
33396         
33397         this.resize();
33398     },
33399     
33400     onImageLoad : function()
33401     {
33402         this.resize();
33403     },
33404     
33405     resize : function()
33406     {
33407         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33408         
33409         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33410         
33411         if(this.bgimage.length){
33412             var image = this.el.select('.roo-brick-image-view', true).first();
33413             
33414             image.setWidth(paragraph.getWidth());
33415             
33416             if(this.square){
33417                 image.setHeight(paragraph.getWidth());
33418             }
33419             
33420             this.el.setHeight(image.getHeight());
33421             paragraph.setHeight(image.getHeight());
33422             
33423         }
33424         
33425     },
33426     
33427     enter: function(e, el)
33428     {
33429         e.preventDefault();
33430         
33431         if(this.bgimage.length){
33432             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33433             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33434         }
33435     },
33436     
33437     leave: function(e, el)
33438     {
33439         e.preventDefault();
33440         
33441         if(this.bgimage.length){
33442             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33443             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33444         }
33445     }
33446     
33447 });
33448
33449  
33450
33451  /*
33452  * - LGPL
33453  *
33454  * Number field 
33455  */
33456
33457 /**
33458  * @class Roo.bootstrap.NumberField
33459  * @extends Roo.bootstrap.Input
33460  * Bootstrap NumberField class
33461  * 
33462  * 
33463  * 
33464  * 
33465  * @constructor
33466  * Create a new NumberField
33467  * @param {Object} config The config object
33468  */
33469
33470 Roo.bootstrap.NumberField = function(config){
33471     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33472 };
33473
33474 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33475     
33476     /**
33477      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33478      */
33479     allowDecimals : true,
33480     /**
33481      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33482      */
33483     decimalSeparator : ".",
33484     /**
33485      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33486      */
33487     decimalPrecision : 2,
33488     /**
33489      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33490      */
33491     allowNegative : true,
33492     
33493     /**
33494      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33495      */
33496     allowZero: true,
33497     /**
33498      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33499      */
33500     minValue : Number.NEGATIVE_INFINITY,
33501     /**
33502      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33503      */
33504     maxValue : Number.MAX_VALUE,
33505     /**
33506      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33507      */
33508     minText : "The minimum value for this field is {0}",
33509     /**
33510      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33511      */
33512     maxText : "The maximum value for this field is {0}",
33513     /**
33514      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33515      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33516      */
33517     nanText : "{0} is not a valid number",
33518     /**
33519      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33520      */
33521     thousandsDelimiter : false,
33522     /**
33523      * @cfg {String} valueAlign alignment of value
33524      */
33525     valueAlign : "left",
33526
33527     getAutoCreate : function()
33528     {
33529         var hiddenInput = {
33530             tag: 'input',
33531             type: 'hidden',
33532             id: Roo.id(),
33533             cls: 'hidden-number-input'
33534         };
33535         
33536         if (this.name) {
33537             hiddenInput.name = this.name;
33538         }
33539         
33540         this.name = '';
33541         
33542         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33543         
33544         this.name = hiddenInput.name;
33545         
33546         if(cfg.cn.length > 0) {
33547             cfg.cn.push(hiddenInput);
33548         }
33549         
33550         return cfg;
33551     },
33552
33553     // private
33554     initEvents : function()
33555     {   
33556         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33557         
33558         var allowed = "0123456789";
33559         
33560         if(this.allowDecimals){
33561             allowed += this.decimalSeparator;
33562         }
33563         
33564         if(this.allowNegative){
33565             allowed += "-";
33566         }
33567         
33568         if(this.thousandsDelimiter) {
33569             allowed += ",";
33570         }
33571         
33572         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33573         
33574         var keyPress = function(e){
33575             
33576             var k = e.getKey();
33577             
33578             var c = e.getCharCode();
33579             
33580             if(
33581                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33582                     allowed.indexOf(String.fromCharCode(c)) === -1
33583             ){
33584                 e.stopEvent();
33585                 return;
33586             }
33587             
33588             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33589                 return;
33590             }
33591             
33592             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33593                 e.stopEvent();
33594             }
33595         };
33596         
33597         this.el.on("keypress", keyPress, this);
33598     },
33599     
33600     validateValue : function(value)
33601     {
33602         
33603         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33604             return false;
33605         }
33606         
33607         var num = this.parseValue(value);
33608         
33609         if(isNaN(num)){
33610             this.markInvalid(String.format(this.nanText, value));
33611             return false;
33612         }
33613         
33614         if(num < this.minValue){
33615             this.markInvalid(String.format(this.minText, this.minValue));
33616             return false;
33617         }
33618         
33619         if(num > this.maxValue){
33620             this.markInvalid(String.format(this.maxText, this.maxValue));
33621             return false;
33622         }
33623         
33624         return true;
33625     },
33626
33627     getValue : function()
33628     {
33629         var v = this.hiddenEl().getValue();
33630         
33631         return this.fixPrecision(this.parseValue(v));
33632     },
33633
33634     parseValue : function(value)
33635     {
33636         if(this.thousandsDelimiter) {
33637             value += "";
33638             r = new RegExp(",", "g");
33639             value = value.replace(r, "");
33640         }
33641         
33642         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33643         return isNaN(value) ? '' : value;
33644     },
33645
33646     fixPrecision : function(value)
33647     {
33648         if(this.thousandsDelimiter) {
33649             value += "";
33650             r = new RegExp(",", "g");
33651             value = value.replace(r, "");
33652         }
33653         
33654         var nan = isNaN(value);
33655         
33656         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33657             return nan ? '' : value;
33658         }
33659         return parseFloat(value).toFixed(this.decimalPrecision);
33660     },
33661
33662     setValue : function(v)
33663     {
33664         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33665         
33666         this.value = v;
33667         
33668         if(this.rendered){
33669             
33670             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33671             
33672             this.inputEl().dom.value = (v == '') ? '' :
33673                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33674             
33675             if(!this.allowZero && v === '0') {
33676                 this.hiddenEl().dom.value = '';
33677                 this.inputEl().dom.value = '';
33678             }
33679             
33680             this.validate();
33681         }
33682     },
33683
33684     decimalPrecisionFcn : function(v)
33685     {
33686         return Math.floor(v);
33687     },
33688
33689     beforeBlur : function()
33690     {
33691         var v = this.parseValue(this.getRawValue());
33692         
33693         if(v || v === 0 || v === ''){
33694             this.setValue(v);
33695         }
33696     },
33697     
33698     hiddenEl : function()
33699     {
33700         return this.el.select('input.hidden-number-input',true).first();
33701     }
33702     
33703 });
33704
33705  
33706
33707 /*
33708 * Licence: LGPL
33709 */
33710
33711 /**
33712  * @class Roo.bootstrap.DocumentSlider
33713  * @extends Roo.bootstrap.Component
33714  * Bootstrap DocumentSlider class
33715  * 
33716  * @constructor
33717  * Create a new DocumentViewer
33718  * @param {Object} config The config object
33719  */
33720
33721 Roo.bootstrap.DocumentSlider = function(config){
33722     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33723     
33724     this.files = [];
33725     
33726     this.addEvents({
33727         /**
33728          * @event initial
33729          * Fire after initEvent
33730          * @param {Roo.bootstrap.DocumentSlider} this
33731          */
33732         "initial" : true,
33733         /**
33734          * @event update
33735          * Fire after update
33736          * @param {Roo.bootstrap.DocumentSlider} this
33737          */
33738         "update" : true,
33739         /**
33740          * @event click
33741          * Fire after click
33742          * @param {Roo.bootstrap.DocumentSlider} this
33743          */
33744         "click" : true
33745     });
33746 };
33747
33748 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33749     
33750     files : false,
33751     
33752     indicator : 0,
33753     
33754     getAutoCreate : function()
33755     {
33756         var cfg = {
33757             tag : 'div',
33758             cls : 'roo-document-slider',
33759             cn : [
33760                 {
33761                     tag : 'div',
33762                     cls : 'roo-document-slider-header',
33763                     cn : [
33764                         {
33765                             tag : 'div',
33766                             cls : 'roo-document-slider-header-title'
33767                         }
33768                     ]
33769                 },
33770                 {
33771                     tag : 'div',
33772                     cls : 'roo-document-slider-body',
33773                     cn : [
33774                         {
33775                             tag : 'div',
33776                             cls : 'roo-document-slider-prev',
33777                             cn : [
33778                                 {
33779                                     tag : 'i',
33780                                     cls : 'fa fa-chevron-left'
33781                                 }
33782                             ]
33783                         },
33784                         {
33785                             tag : 'div',
33786                             cls : 'roo-document-slider-thumb',
33787                             cn : [
33788                                 {
33789                                     tag : 'img',
33790                                     cls : 'roo-document-slider-image'
33791                                 }
33792                             ]
33793                         },
33794                         {
33795                             tag : 'div',
33796                             cls : 'roo-document-slider-next',
33797                             cn : [
33798                                 {
33799                                     tag : 'i',
33800                                     cls : 'fa fa-chevron-right'
33801                                 }
33802                             ]
33803                         }
33804                     ]
33805                 }
33806             ]
33807         };
33808         
33809         return cfg;
33810     },
33811     
33812     initEvents : function()
33813     {
33814         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33815         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33816         
33817         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33818         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33819         
33820         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33821         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33822         
33823         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33824         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33825         
33826         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33827         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33828         
33829         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33830         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33831         
33832         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33833         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33834         
33835         this.thumbEl.on('click', this.onClick, this);
33836         
33837         this.prevIndicator.on('click', this.prev, this);
33838         
33839         this.nextIndicator.on('click', this.next, this);
33840         
33841     },
33842     
33843     initial : function()
33844     {
33845         if(this.files.length){
33846             this.indicator = 1;
33847             this.update()
33848         }
33849         
33850         this.fireEvent('initial', this);
33851     },
33852     
33853     update : function()
33854     {
33855         this.imageEl.attr('src', this.files[this.indicator - 1]);
33856         
33857         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33858         
33859         this.prevIndicator.show();
33860         
33861         if(this.indicator == 1){
33862             this.prevIndicator.hide();
33863         }
33864         
33865         this.nextIndicator.show();
33866         
33867         if(this.indicator == this.files.length){
33868             this.nextIndicator.hide();
33869         }
33870         
33871         this.thumbEl.scrollTo('top');
33872         
33873         this.fireEvent('update', this);
33874     },
33875     
33876     onClick : function(e)
33877     {
33878         e.preventDefault();
33879         
33880         this.fireEvent('click', this);
33881     },
33882     
33883     prev : function(e)
33884     {
33885         e.preventDefault();
33886         
33887         this.indicator = Math.max(1, this.indicator - 1);
33888         
33889         this.update();
33890     },
33891     
33892     next : function(e)
33893     {
33894         e.preventDefault();
33895         
33896         this.indicator = Math.min(this.files.length, this.indicator + 1);
33897         
33898         this.update();
33899     }
33900 });
33901 /*
33902  * - LGPL
33903  *
33904  * RadioSet
33905  *
33906  *
33907  */
33908
33909 /**
33910  * @class Roo.bootstrap.RadioSet
33911  * @extends Roo.bootstrap.Input
33912  * Bootstrap RadioSet class
33913  * @cfg {String} indicatorpos (left|right) default left
33914  * @cfg {Boolean} inline (true|false) inline the element (default true)
33915  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33916  * @constructor
33917  * Create a new RadioSet
33918  * @param {Object} config The config object
33919  */
33920
33921 Roo.bootstrap.RadioSet = function(config){
33922     
33923     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33924     
33925     this.radioes = [];
33926     
33927     Roo.bootstrap.RadioSet.register(this);
33928     
33929     this.addEvents({
33930         /**
33931         * @event check
33932         * Fires when the element is checked or unchecked.
33933         * @param {Roo.bootstrap.RadioSet} this This radio
33934         * @param {Roo.bootstrap.Radio} item The checked item
33935         */
33936        check : true,
33937        /**
33938         * @event click
33939         * Fires when the element is click.
33940         * @param {Roo.bootstrap.RadioSet} this This radio set
33941         * @param {Roo.bootstrap.Radio} item The checked item
33942         * @param {Roo.EventObject} e The event object
33943         */
33944        click : true
33945     });
33946     
33947 };
33948
33949 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33950
33951     radioes : false,
33952     
33953     inline : true,
33954     
33955     weight : '',
33956     
33957     indicatorpos : 'left',
33958     
33959     getAutoCreate : function()
33960     {
33961         var label = {
33962             tag : 'label',
33963             cls : 'roo-radio-set-label',
33964             cn : [
33965                 {
33966                     tag : 'span',
33967                     html : this.fieldLabel
33968                 }
33969             ]
33970         };
33971         
33972         if(this.indicatorpos == 'left'){
33973             label.cn.unshift({
33974                 tag : 'i',
33975                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33976                 tooltip : 'This field is required'
33977             });
33978         } else {
33979             label.cn.push({
33980                 tag : 'i',
33981                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33982                 tooltip : 'This field is required'
33983             });
33984         }
33985         
33986         var items = {
33987             tag : 'div',
33988             cls : 'roo-radio-set-items'
33989         };
33990         
33991         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33992         
33993         if (align === 'left' && this.fieldLabel.length) {
33994             
33995             items = {
33996                 cls : "roo-radio-set-right", 
33997                 cn: [
33998                     items
33999                 ]
34000             };
34001             
34002             if(this.labelWidth > 12){
34003                 label.style = "width: " + this.labelWidth + 'px';
34004             }
34005             
34006             if(this.labelWidth < 13 && this.labelmd == 0){
34007                 this.labelmd = this.labelWidth;
34008             }
34009             
34010             if(this.labellg > 0){
34011                 label.cls += ' col-lg-' + this.labellg;
34012                 items.cls += ' col-lg-' + (12 - this.labellg);
34013             }
34014             
34015             if(this.labelmd > 0){
34016                 label.cls += ' col-md-' + this.labelmd;
34017                 items.cls += ' col-md-' + (12 - this.labelmd);
34018             }
34019             
34020             if(this.labelsm > 0){
34021                 label.cls += ' col-sm-' + this.labelsm;
34022                 items.cls += ' col-sm-' + (12 - this.labelsm);
34023             }
34024             
34025             if(this.labelxs > 0){
34026                 label.cls += ' col-xs-' + this.labelxs;
34027                 items.cls += ' col-xs-' + (12 - this.labelxs);
34028             }
34029         }
34030         
34031         var cfg = {
34032             tag : 'div',
34033             cls : 'roo-radio-set',
34034             cn : [
34035                 {
34036                     tag : 'input',
34037                     cls : 'roo-radio-set-input',
34038                     type : 'hidden',
34039                     name : this.name,
34040                     value : this.value ? this.value :  ''
34041                 },
34042                 label,
34043                 items
34044             ]
34045         };
34046         
34047         if(this.weight.length){
34048             cfg.cls += ' roo-radio-' + this.weight;
34049         }
34050         
34051         if(this.inline) {
34052             cfg.cls += ' roo-radio-set-inline';
34053         }
34054         
34055         var settings=this;
34056         ['xs','sm','md','lg'].map(function(size){
34057             if (settings[size]) {
34058                 cfg.cls += ' col-' + size + '-' + settings[size];
34059             }
34060         });
34061         
34062         return cfg;
34063         
34064     },
34065
34066     initEvents : function()
34067     {
34068         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34069         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34070         
34071         if(!this.fieldLabel.length){
34072             this.labelEl.hide();
34073         }
34074         
34075         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34076         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34077         
34078         this.indicator = this.indicatorEl();
34079         
34080         if(this.indicator){
34081             this.indicator.addClass('invisible');
34082         }
34083         
34084         this.originalValue = this.getValue();
34085         
34086     },
34087     
34088     inputEl: function ()
34089     {
34090         return this.el.select('.roo-radio-set-input', true).first();
34091     },
34092     
34093     getChildContainer : function()
34094     {
34095         return this.itemsEl;
34096     },
34097     
34098     register : function(item)
34099     {
34100         this.radioes.push(item);
34101         
34102     },
34103     
34104     validate : function()
34105     {   
34106         if(this.getVisibilityEl().hasClass('hidden')){
34107             return true;
34108         }
34109         
34110         var valid = false;
34111         
34112         Roo.each(this.radioes, function(i){
34113             if(!i.checked){
34114                 return;
34115             }
34116             
34117             valid = true;
34118             return false;
34119         });
34120         
34121         if(this.allowBlank) {
34122             return true;
34123         }
34124         
34125         if(this.disabled || valid){
34126             this.markValid();
34127             return true;
34128         }
34129         
34130         this.markInvalid();
34131         return false;
34132         
34133     },
34134     
34135     markValid : function()
34136     {
34137         if(this.labelEl.isVisible(true)){
34138             this.indicatorEl().removeClass('visible');
34139             this.indicatorEl().addClass('invisible');
34140         }
34141         
34142         this.el.removeClass([this.invalidClass, this.validClass]);
34143         this.el.addClass(this.validClass);
34144         
34145         this.fireEvent('valid', this);
34146     },
34147     
34148     markInvalid : function(msg)
34149     {
34150         if(this.allowBlank || this.disabled){
34151             return;
34152         }
34153         
34154         if(this.labelEl.isVisible(true)){
34155             this.indicatorEl().removeClass('invisible');
34156             this.indicatorEl().addClass('visible');
34157         }
34158         
34159         this.el.removeClass([this.invalidClass, this.validClass]);
34160         this.el.addClass(this.invalidClass);
34161         
34162         this.fireEvent('invalid', this, msg);
34163         
34164     },
34165     
34166     setValue : function(v, suppressEvent)
34167     {   
34168         if(this.value === v){
34169             return;
34170         }
34171         
34172         this.value = v;
34173         
34174         if(this.rendered){
34175             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34176         }
34177         
34178         Roo.each(this.radioes, function(i){
34179             i.checked = false;
34180             i.el.removeClass('checked');
34181         });
34182         
34183         Roo.each(this.radioes, function(i){
34184             
34185             if(i.value === v || i.value.toString() === v.toString()){
34186                 i.checked = true;
34187                 i.el.addClass('checked');
34188                 
34189                 if(suppressEvent !== true){
34190                     this.fireEvent('check', this, i);
34191                 }
34192                 
34193                 return false;
34194             }
34195             
34196         }, this);
34197         
34198         this.validate();
34199     },
34200     
34201     clearInvalid : function(){
34202         
34203         if(!this.el || this.preventMark){
34204             return;
34205         }
34206         
34207         this.el.removeClass([this.invalidClass]);
34208         
34209         this.fireEvent('valid', this);
34210     }
34211     
34212 });
34213
34214 Roo.apply(Roo.bootstrap.RadioSet, {
34215     
34216     groups: {},
34217     
34218     register : function(set)
34219     {
34220         this.groups[set.name] = set;
34221     },
34222     
34223     get: function(name) 
34224     {
34225         if (typeof(this.groups[name]) == 'undefined') {
34226             return false;
34227         }
34228         
34229         return this.groups[name] ;
34230     }
34231     
34232 });
34233 /*
34234  * Based on:
34235  * Ext JS Library 1.1.1
34236  * Copyright(c) 2006-2007, Ext JS, LLC.
34237  *
34238  * Originally Released Under LGPL - original licence link has changed is not relivant.
34239  *
34240  * Fork - LGPL
34241  * <script type="text/javascript">
34242  */
34243
34244
34245 /**
34246  * @class Roo.bootstrap.SplitBar
34247  * @extends Roo.util.Observable
34248  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34249  * <br><br>
34250  * Usage:
34251  * <pre><code>
34252 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34253                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34254 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34255 split.minSize = 100;
34256 split.maxSize = 600;
34257 split.animate = true;
34258 split.on('moved', splitterMoved);
34259 </code></pre>
34260  * @constructor
34261  * Create a new SplitBar
34262  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34263  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34264  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34265  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34266                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34267                         position of the SplitBar).
34268  */
34269 Roo.bootstrap.SplitBar = function(cfg){
34270     
34271     /** @private */
34272     
34273     //{
34274     //  dragElement : elm
34275     //  resizingElement: el,
34276         // optional..
34277     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34278     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34279         // existingProxy ???
34280     //}
34281     
34282     this.el = Roo.get(cfg.dragElement, true);
34283     this.el.dom.unselectable = "on";
34284     /** @private */
34285     this.resizingEl = Roo.get(cfg.resizingElement, true);
34286
34287     /**
34288      * @private
34289      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34290      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34291      * @type Number
34292      */
34293     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34294     
34295     /**
34296      * The minimum size of the resizing element. (Defaults to 0)
34297      * @type Number
34298      */
34299     this.minSize = 0;
34300     
34301     /**
34302      * The maximum size of the resizing element. (Defaults to 2000)
34303      * @type Number
34304      */
34305     this.maxSize = 2000;
34306     
34307     /**
34308      * Whether to animate the transition to the new size
34309      * @type Boolean
34310      */
34311     this.animate = false;
34312     
34313     /**
34314      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34315      * @type Boolean
34316      */
34317     this.useShim = false;
34318     
34319     /** @private */
34320     this.shim = null;
34321     
34322     if(!cfg.existingProxy){
34323         /** @private */
34324         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34325     }else{
34326         this.proxy = Roo.get(cfg.existingProxy).dom;
34327     }
34328     /** @private */
34329     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34330     
34331     /** @private */
34332     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34333     
34334     /** @private */
34335     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34336     
34337     /** @private */
34338     this.dragSpecs = {};
34339     
34340     /**
34341      * @private The adapter to use to positon and resize elements
34342      */
34343     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34344     this.adapter.init(this);
34345     
34346     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34347         /** @private */
34348         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34349         this.el.addClass("roo-splitbar-h");
34350     }else{
34351         /** @private */
34352         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34353         this.el.addClass("roo-splitbar-v");
34354     }
34355     
34356     this.addEvents({
34357         /**
34358          * @event resize
34359          * Fires when the splitter is moved (alias for {@link #event-moved})
34360          * @param {Roo.bootstrap.SplitBar} this
34361          * @param {Number} newSize the new width or height
34362          */
34363         "resize" : true,
34364         /**
34365          * @event moved
34366          * Fires when the splitter is moved
34367          * @param {Roo.bootstrap.SplitBar} this
34368          * @param {Number} newSize the new width or height
34369          */
34370         "moved" : true,
34371         /**
34372          * @event beforeresize
34373          * Fires before the splitter is dragged
34374          * @param {Roo.bootstrap.SplitBar} this
34375          */
34376         "beforeresize" : true,
34377
34378         "beforeapply" : true
34379     });
34380
34381     Roo.util.Observable.call(this);
34382 };
34383
34384 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34385     onStartProxyDrag : function(x, y){
34386         this.fireEvent("beforeresize", this);
34387         if(!this.overlay){
34388             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34389             o.unselectable();
34390             o.enableDisplayMode("block");
34391             // all splitbars share the same overlay
34392             Roo.bootstrap.SplitBar.prototype.overlay = o;
34393         }
34394         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34395         this.overlay.show();
34396         Roo.get(this.proxy).setDisplayed("block");
34397         var size = this.adapter.getElementSize(this);
34398         this.activeMinSize = this.getMinimumSize();;
34399         this.activeMaxSize = this.getMaximumSize();;
34400         var c1 = size - this.activeMinSize;
34401         var c2 = Math.max(this.activeMaxSize - size, 0);
34402         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34403             this.dd.resetConstraints();
34404             this.dd.setXConstraint(
34405                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34406                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34407             );
34408             this.dd.setYConstraint(0, 0);
34409         }else{
34410             this.dd.resetConstraints();
34411             this.dd.setXConstraint(0, 0);
34412             this.dd.setYConstraint(
34413                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34414                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34415             );
34416          }
34417         this.dragSpecs.startSize = size;
34418         this.dragSpecs.startPoint = [x, y];
34419         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34420     },
34421     
34422     /** 
34423      * @private Called after the drag operation by the DDProxy
34424      */
34425     onEndProxyDrag : function(e){
34426         Roo.get(this.proxy).setDisplayed(false);
34427         var endPoint = Roo.lib.Event.getXY(e);
34428         if(this.overlay){
34429             this.overlay.hide();
34430         }
34431         var newSize;
34432         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34433             newSize = this.dragSpecs.startSize + 
34434                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34435                     endPoint[0] - this.dragSpecs.startPoint[0] :
34436                     this.dragSpecs.startPoint[0] - endPoint[0]
34437                 );
34438         }else{
34439             newSize = this.dragSpecs.startSize + 
34440                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34441                     endPoint[1] - this.dragSpecs.startPoint[1] :
34442                     this.dragSpecs.startPoint[1] - endPoint[1]
34443                 );
34444         }
34445         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34446         if(newSize != this.dragSpecs.startSize){
34447             if(this.fireEvent('beforeapply', this, newSize) !== false){
34448                 this.adapter.setElementSize(this, newSize);
34449                 this.fireEvent("moved", this, newSize);
34450                 this.fireEvent("resize", this, newSize);
34451             }
34452         }
34453     },
34454     
34455     /**
34456      * Get the adapter this SplitBar uses
34457      * @return The adapter object
34458      */
34459     getAdapter : function(){
34460         return this.adapter;
34461     },
34462     
34463     /**
34464      * Set the adapter this SplitBar uses
34465      * @param {Object} adapter A SplitBar adapter object
34466      */
34467     setAdapter : function(adapter){
34468         this.adapter = adapter;
34469         this.adapter.init(this);
34470     },
34471     
34472     /**
34473      * Gets the minimum size for the resizing element
34474      * @return {Number} The minimum size
34475      */
34476     getMinimumSize : function(){
34477         return this.minSize;
34478     },
34479     
34480     /**
34481      * Sets the minimum size for the resizing element
34482      * @param {Number} minSize The minimum size
34483      */
34484     setMinimumSize : function(minSize){
34485         this.minSize = minSize;
34486     },
34487     
34488     /**
34489      * Gets the maximum size for the resizing element
34490      * @return {Number} The maximum size
34491      */
34492     getMaximumSize : function(){
34493         return this.maxSize;
34494     },
34495     
34496     /**
34497      * Sets the maximum size for the resizing element
34498      * @param {Number} maxSize The maximum size
34499      */
34500     setMaximumSize : function(maxSize){
34501         this.maxSize = maxSize;
34502     },
34503     
34504     /**
34505      * Sets the initialize size for the resizing element
34506      * @param {Number} size The initial size
34507      */
34508     setCurrentSize : function(size){
34509         var oldAnimate = this.animate;
34510         this.animate = false;
34511         this.adapter.setElementSize(this, size);
34512         this.animate = oldAnimate;
34513     },
34514     
34515     /**
34516      * Destroy this splitbar. 
34517      * @param {Boolean} removeEl True to remove the element
34518      */
34519     destroy : function(removeEl){
34520         if(this.shim){
34521             this.shim.remove();
34522         }
34523         this.dd.unreg();
34524         this.proxy.parentNode.removeChild(this.proxy);
34525         if(removeEl){
34526             this.el.remove();
34527         }
34528     }
34529 });
34530
34531 /**
34532  * @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.
34533  */
34534 Roo.bootstrap.SplitBar.createProxy = function(dir){
34535     var proxy = new Roo.Element(document.createElement("div"));
34536     proxy.unselectable();
34537     var cls = 'roo-splitbar-proxy';
34538     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34539     document.body.appendChild(proxy.dom);
34540     return proxy.dom;
34541 };
34542
34543 /** 
34544  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34545  * Default Adapter. It assumes the splitter and resizing element are not positioned
34546  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34547  */
34548 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34549 };
34550
34551 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34552     // do nothing for now
34553     init : function(s){
34554     
34555     },
34556     /**
34557      * Called before drag operations to get the current size of the resizing element. 
34558      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34559      */
34560      getElementSize : function(s){
34561         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34562             return s.resizingEl.getWidth();
34563         }else{
34564             return s.resizingEl.getHeight();
34565         }
34566     },
34567     
34568     /**
34569      * Called after drag operations to set the size of the resizing element.
34570      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34571      * @param {Number} newSize The new size to set
34572      * @param {Function} onComplete A function to be invoked when resizing is complete
34573      */
34574     setElementSize : function(s, newSize, onComplete){
34575         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34576             if(!s.animate){
34577                 s.resizingEl.setWidth(newSize);
34578                 if(onComplete){
34579                     onComplete(s, newSize);
34580                 }
34581             }else{
34582                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34583             }
34584         }else{
34585             
34586             if(!s.animate){
34587                 s.resizingEl.setHeight(newSize);
34588                 if(onComplete){
34589                     onComplete(s, newSize);
34590                 }
34591             }else{
34592                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34593             }
34594         }
34595     }
34596 };
34597
34598 /** 
34599  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34600  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34601  * Adapter that  moves the splitter element to align with the resized sizing element. 
34602  * Used with an absolute positioned SplitBar.
34603  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34604  * document.body, make sure you assign an id to the body element.
34605  */
34606 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34607     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34608     this.container = Roo.get(container);
34609 };
34610
34611 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34612     init : function(s){
34613         this.basic.init(s);
34614     },
34615     
34616     getElementSize : function(s){
34617         return this.basic.getElementSize(s);
34618     },
34619     
34620     setElementSize : function(s, newSize, onComplete){
34621         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34622     },
34623     
34624     moveSplitter : function(s){
34625         var yes = Roo.bootstrap.SplitBar;
34626         switch(s.placement){
34627             case yes.LEFT:
34628                 s.el.setX(s.resizingEl.getRight());
34629                 break;
34630             case yes.RIGHT:
34631                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34632                 break;
34633             case yes.TOP:
34634                 s.el.setY(s.resizingEl.getBottom());
34635                 break;
34636             case yes.BOTTOM:
34637                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34638                 break;
34639         }
34640     }
34641 };
34642
34643 /**
34644  * Orientation constant - Create a vertical SplitBar
34645  * @static
34646  * @type Number
34647  */
34648 Roo.bootstrap.SplitBar.VERTICAL = 1;
34649
34650 /**
34651  * Orientation constant - Create a horizontal SplitBar
34652  * @static
34653  * @type Number
34654  */
34655 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34656
34657 /**
34658  * Placement constant - The resizing element is to the left of the splitter element
34659  * @static
34660  * @type Number
34661  */
34662 Roo.bootstrap.SplitBar.LEFT = 1;
34663
34664 /**
34665  * Placement constant - The resizing element is to the right of the splitter element
34666  * @static
34667  * @type Number
34668  */
34669 Roo.bootstrap.SplitBar.RIGHT = 2;
34670
34671 /**
34672  * Placement constant - The resizing element is positioned above the splitter element
34673  * @static
34674  * @type Number
34675  */
34676 Roo.bootstrap.SplitBar.TOP = 3;
34677
34678 /**
34679  * Placement constant - The resizing element is positioned under splitter element
34680  * @static
34681  * @type Number
34682  */
34683 Roo.bootstrap.SplitBar.BOTTOM = 4;
34684 Roo.namespace("Roo.bootstrap.layout");/*
34685  * Based on:
34686  * Ext JS Library 1.1.1
34687  * Copyright(c) 2006-2007, Ext JS, LLC.
34688  *
34689  * Originally Released Under LGPL - original licence link has changed is not relivant.
34690  *
34691  * Fork - LGPL
34692  * <script type="text/javascript">
34693  */
34694
34695 /**
34696  * @class Roo.bootstrap.layout.Manager
34697  * @extends Roo.bootstrap.Component
34698  * Base class for layout managers.
34699  */
34700 Roo.bootstrap.layout.Manager = function(config)
34701 {
34702     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34703
34704
34705
34706
34707
34708     /** false to disable window resize monitoring @type Boolean */
34709     this.monitorWindowResize = true;
34710     this.regions = {};
34711     this.addEvents({
34712         /**
34713          * @event layout
34714          * Fires when a layout is performed.
34715          * @param {Roo.LayoutManager} this
34716          */
34717         "layout" : true,
34718         /**
34719          * @event regionresized
34720          * Fires when the user resizes a region.
34721          * @param {Roo.LayoutRegion} region The resized region
34722          * @param {Number} newSize The new size (width for east/west, height for north/south)
34723          */
34724         "regionresized" : true,
34725         /**
34726          * @event regioncollapsed
34727          * Fires when a region is collapsed.
34728          * @param {Roo.LayoutRegion} region The collapsed region
34729          */
34730         "regioncollapsed" : true,
34731         /**
34732          * @event regionexpanded
34733          * Fires when a region is expanded.
34734          * @param {Roo.LayoutRegion} region The expanded region
34735          */
34736         "regionexpanded" : true
34737     });
34738     this.updating = false;
34739
34740     if (config.el) {
34741         this.el = Roo.get(config.el);
34742         this.initEvents();
34743     }
34744
34745 };
34746
34747 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34748
34749
34750     regions : null,
34751
34752     monitorWindowResize : true,
34753
34754
34755     updating : false,
34756
34757
34758     onRender : function(ct, position)
34759     {
34760         if(!this.el){
34761             this.el = Roo.get(ct);
34762             this.initEvents();
34763         }
34764         //this.fireEvent('render',this);
34765     },
34766
34767
34768     initEvents: function()
34769     {
34770
34771
34772         // ie scrollbar fix
34773         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34774             document.body.scroll = "no";
34775         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34776             this.el.position('relative');
34777         }
34778         this.id = this.el.id;
34779         this.el.addClass("roo-layout-container");
34780         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34781         if(this.el.dom != document.body ) {
34782             this.el.on('resize', this.layout,this);
34783             this.el.on('show', this.layout,this);
34784         }
34785
34786     },
34787
34788     /**
34789      * Returns true if this layout is currently being updated
34790      * @return {Boolean}
34791      */
34792     isUpdating : function(){
34793         return this.updating;
34794     },
34795
34796     /**
34797      * Suspend the LayoutManager from doing auto-layouts while
34798      * making multiple add or remove calls
34799      */
34800     beginUpdate : function(){
34801         this.updating = true;
34802     },
34803
34804     /**
34805      * Restore auto-layouts and optionally disable the manager from performing a layout
34806      * @param {Boolean} noLayout true to disable a layout update
34807      */
34808     endUpdate : function(noLayout){
34809         this.updating = false;
34810         if(!noLayout){
34811             this.layout();
34812         }
34813     },
34814
34815     layout: function(){
34816         // abstract...
34817     },
34818
34819     onRegionResized : function(region, newSize){
34820         this.fireEvent("regionresized", region, newSize);
34821         this.layout();
34822     },
34823
34824     onRegionCollapsed : function(region){
34825         this.fireEvent("regioncollapsed", region);
34826     },
34827
34828     onRegionExpanded : function(region){
34829         this.fireEvent("regionexpanded", region);
34830     },
34831
34832     /**
34833      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34834      * performs box-model adjustments.
34835      * @return {Object} The size as an object {width: (the width), height: (the height)}
34836      */
34837     getViewSize : function()
34838     {
34839         var size;
34840         if(this.el.dom != document.body){
34841             size = this.el.getSize();
34842         }else{
34843             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34844         }
34845         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34846         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34847         return size;
34848     },
34849
34850     /**
34851      * Returns the Element this layout is bound to.
34852      * @return {Roo.Element}
34853      */
34854     getEl : function(){
34855         return this.el;
34856     },
34857
34858     /**
34859      * Returns the specified region.
34860      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34861      * @return {Roo.LayoutRegion}
34862      */
34863     getRegion : function(target){
34864         return this.regions[target.toLowerCase()];
34865     },
34866
34867     onWindowResize : function(){
34868         if(this.monitorWindowResize){
34869             this.layout();
34870         }
34871     }
34872 });
34873 /*
34874  * Based on:
34875  * Ext JS Library 1.1.1
34876  * Copyright(c) 2006-2007, Ext JS, LLC.
34877  *
34878  * Originally Released Under LGPL - original licence link has changed is not relivant.
34879  *
34880  * Fork - LGPL
34881  * <script type="text/javascript">
34882  */
34883 /**
34884  * @class Roo.bootstrap.layout.Border
34885  * @extends Roo.bootstrap.layout.Manager
34886  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34887  * please see: examples/bootstrap/nested.html<br><br>
34888  
34889 <b>The container the layout is rendered into can be either the body element or any other element.
34890 If it is not the body element, the container needs to either be an absolute positioned element,
34891 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34892 the container size if it is not the body element.</b>
34893
34894 * @constructor
34895 * Create a new Border
34896 * @param {Object} config Configuration options
34897  */
34898 Roo.bootstrap.layout.Border = function(config){
34899     config = config || {};
34900     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34901     
34902     
34903     
34904     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34905         if(config[region]){
34906             config[region].region = region;
34907             this.addRegion(config[region]);
34908         }
34909     },this);
34910     
34911 };
34912
34913 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34914
34915 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34916     /**
34917      * Creates and adds a new region if it doesn't already exist.
34918      * @param {String} target The target region key (north, south, east, west or center).
34919      * @param {Object} config The regions config object
34920      * @return {BorderLayoutRegion} The new region
34921      */
34922     addRegion : function(config)
34923     {
34924         if(!this.regions[config.region]){
34925             var r = this.factory(config);
34926             this.bindRegion(r);
34927         }
34928         return this.regions[config.region];
34929     },
34930
34931     // private (kinda)
34932     bindRegion : function(r){
34933         this.regions[r.config.region] = r;
34934         
34935         r.on("visibilitychange",    this.layout, this);
34936         r.on("paneladded",          this.layout, this);
34937         r.on("panelremoved",        this.layout, this);
34938         r.on("invalidated",         this.layout, this);
34939         r.on("resized",             this.onRegionResized, this);
34940         r.on("collapsed",           this.onRegionCollapsed, this);
34941         r.on("expanded",            this.onRegionExpanded, this);
34942     },
34943
34944     /**
34945      * Performs a layout update.
34946      */
34947     layout : function()
34948     {
34949         if(this.updating) {
34950             return;
34951         }
34952         
34953         // render all the rebions if they have not been done alreayd?
34954         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34955             if(this.regions[region] && !this.regions[region].bodyEl){
34956                 this.regions[region].onRender(this.el)
34957             }
34958         },this);
34959         
34960         var size = this.getViewSize();
34961         var w = size.width;
34962         var h = size.height;
34963         var centerW = w;
34964         var centerH = h;
34965         var centerY = 0;
34966         var centerX = 0;
34967         //var x = 0, y = 0;
34968
34969         var rs = this.regions;
34970         var north = rs["north"];
34971         var south = rs["south"]; 
34972         var west = rs["west"];
34973         var east = rs["east"];
34974         var center = rs["center"];
34975         //if(this.hideOnLayout){ // not supported anymore
34976             //c.el.setStyle("display", "none");
34977         //}
34978         if(north && north.isVisible()){
34979             var b = north.getBox();
34980             var m = north.getMargins();
34981             b.width = w - (m.left+m.right);
34982             b.x = m.left;
34983             b.y = m.top;
34984             centerY = b.height + b.y + m.bottom;
34985             centerH -= centerY;
34986             north.updateBox(this.safeBox(b));
34987         }
34988         if(south && south.isVisible()){
34989             var b = south.getBox();
34990             var m = south.getMargins();
34991             b.width = w - (m.left+m.right);
34992             b.x = m.left;
34993             var totalHeight = (b.height + m.top + m.bottom);
34994             b.y = h - totalHeight + m.top;
34995             centerH -= totalHeight;
34996             south.updateBox(this.safeBox(b));
34997         }
34998         if(west && west.isVisible()){
34999             var b = west.getBox();
35000             var m = west.getMargins();
35001             b.height = centerH - (m.top+m.bottom);
35002             b.x = m.left;
35003             b.y = centerY + m.top;
35004             var totalWidth = (b.width + m.left + m.right);
35005             centerX += totalWidth;
35006             centerW -= totalWidth;
35007             west.updateBox(this.safeBox(b));
35008         }
35009         if(east && east.isVisible()){
35010             var b = east.getBox();
35011             var m = east.getMargins();
35012             b.height = centerH - (m.top+m.bottom);
35013             var totalWidth = (b.width + m.left + m.right);
35014             b.x = w - totalWidth + m.left;
35015             b.y = centerY + m.top;
35016             centerW -= totalWidth;
35017             east.updateBox(this.safeBox(b));
35018         }
35019         if(center){
35020             var m = center.getMargins();
35021             var centerBox = {
35022                 x: centerX + m.left,
35023                 y: centerY + m.top,
35024                 width: centerW - (m.left+m.right),
35025                 height: centerH - (m.top+m.bottom)
35026             };
35027             //if(this.hideOnLayout){
35028                 //center.el.setStyle("display", "block");
35029             //}
35030             center.updateBox(this.safeBox(centerBox));
35031         }
35032         this.el.repaint();
35033         this.fireEvent("layout", this);
35034     },
35035
35036     // private
35037     safeBox : function(box){
35038         box.width = Math.max(0, box.width);
35039         box.height = Math.max(0, box.height);
35040         return box;
35041     },
35042
35043     /**
35044      * Adds a ContentPanel (or subclass) to this layout.
35045      * @param {String} target The target region key (north, south, east, west or center).
35046      * @param {Roo.ContentPanel} panel The panel to add
35047      * @return {Roo.ContentPanel} The added panel
35048      */
35049     add : function(target, panel){
35050          
35051         target = target.toLowerCase();
35052         return this.regions[target].add(panel);
35053     },
35054
35055     /**
35056      * Remove a ContentPanel (or subclass) to this layout.
35057      * @param {String} target The target region key (north, south, east, west or center).
35058      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35059      * @return {Roo.ContentPanel} The removed panel
35060      */
35061     remove : function(target, panel){
35062         target = target.toLowerCase();
35063         return this.regions[target].remove(panel);
35064     },
35065
35066     /**
35067      * Searches all regions for a panel with the specified id
35068      * @param {String} panelId
35069      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35070      */
35071     findPanel : function(panelId){
35072         var rs = this.regions;
35073         for(var target in rs){
35074             if(typeof rs[target] != "function"){
35075                 var p = rs[target].getPanel(panelId);
35076                 if(p){
35077                     return p;
35078                 }
35079             }
35080         }
35081         return null;
35082     },
35083
35084     /**
35085      * Searches all regions for a panel with the specified id and activates (shows) it.
35086      * @param {String/ContentPanel} panelId The panels id or the panel itself
35087      * @return {Roo.ContentPanel} The shown panel or null
35088      */
35089     showPanel : function(panelId) {
35090       var rs = this.regions;
35091       for(var target in rs){
35092          var r = rs[target];
35093          if(typeof r != "function"){
35094             if(r.hasPanel(panelId)){
35095                return r.showPanel(panelId);
35096             }
35097          }
35098       }
35099       return null;
35100    },
35101
35102    /**
35103      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35104      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35105      */
35106    /*
35107     restoreState : function(provider){
35108         if(!provider){
35109             provider = Roo.state.Manager;
35110         }
35111         var sm = new Roo.LayoutStateManager();
35112         sm.init(this, provider);
35113     },
35114 */
35115  
35116  
35117     /**
35118      * Adds a xtype elements to the layout.
35119      * <pre><code>
35120
35121 layout.addxtype({
35122        xtype : 'ContentPanel',
35123        region: 'west',
35124        items: [ .... ]
35125    }
35126 );
35127
35128 layout.addxtype({
35129         xtype : 'NestedLayoutPanel',
35130         region: 'west',
35131         layout: {
35132            center: { },
35133            west: { }   
35134         },
35135         items : [ ... list of content panels or nested layout panels.. ]
35136    }
35137 );
35138 </code></pre>
35139      * @param {Object} cfg Xtype definition of item to add.
35140      */
35141     addxtype : function(cfg)
35142     {
35143         // basically accepts a pannel...
35144         // can accept a layout region..!?!?
35145         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35146         
35147         
35148         // theory?  children can only be panels??
35149         
35150         //if (!cfg.xtype.match(/Panel$/)) {
35151         //    return false;
35152         //}
35153         var ret = false;
35154         
35155         if (typeof(cfg.region) == 'undefined') {
35156             Roo.log("Failed to add Panel, region was not set");
35157             Roo.log(cfg);
35158             return false;
35159         }
35160         var region = cfg.region;
35161         delete cfg.region;
35162         
35163           
35164         var xitems = [];
35165         if (cfg.items) {
35166             xitems = cfg.items;
35167             delete cfg.items;
35168         }
35169         var nb = false;
35170         
35171         switch(cfg.xtype) 
35172         {
35173             case 'Content':  // ContentPanel (el, cfg)
35174             case 'Scroll':  // ContentPanel (el, cfg)
35175             case 'View': 
35176                 cfg.autoCreate = true;
35177                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35178                 //} else {
35179                 //    var el = this.el.createChild();
35180                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35181                 //}
35182                 
35183                 this.add(region, ret);
35184                 break;
35185             
35186             /*
35187             case 'TreePanel': // our new panel!
35188                 cfg.el = this.el.createChild();
35189                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35190                 this.add(region, ret);
35191                 break;
35192             */
35193             
35194             case 'Nest': 
35195                 // create a new Layout (which is  a Border Layout...
35196                 
35197                 var clayout = cfg.layout;
35198                 clayout.el  = this.el.createChild();
35199                 clayout.items   = clayout.items  || [];
35200                 
35201                 delete cfg.layout;
35202                 
35203                 // replace this exitems with the clayout ones..
35204                 xitems = clayout.items;
35205                  
35206                 // force background off if it's in center...
35207                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35208                     cfg.background = false;
35209                 }
35210                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35211                 
35212                 
35213                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35214                 //console.log('adding nested layout panel '  + cfg.toSource());
35215                 this.add(region, ret);
35216                 nb = {}; /// find first...
35217                 break;
35218             
35219             case 'Grid':
35220                 
35221                 // needs grid and region
35222                 
35223                 //var el = this.getRegion(region).el.createChild();
35224                 /*
35225                  *var el = this.el.createChild();
35226                 // create the grid first...
35227                 cfg.grid.container = el;
35228                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35229                 */
35230                 
35231                 if (region == 'center' && this.active ) {
35232                     cfg.background = false;
35233                 }
35234                 
35235                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35236                 
35237                 this.add(region, ret);
35238                 /*
35239                 if (cfg.background) {
35240                     // render grid on panel activation (if panel background)
35241                     ret.on('activate', function(gp) {
35242                         if (!gp.grid.rendered) {
35243                     //        gp.grid.render(el);
35244                         }
35245                     });
35246                 } else {
35247                   //  cfg.grid.render(el);
35248                 }
35249                 */
35250                 break;
35251            
35252            
35253             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35254                 // it was the old xcomponent building that caused this before.
35255                 // espeically if border is the top element in the tree.
35256                 ret = this;
35257                 break; 
35258                 
35259                     
35260                 
35261                 
35262                 
35263             default:
35264                 /*
35265                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35266                     
35267                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35268                     this.add(region, ret);
35269                 } else {
35270                 */
35271                     Roo.log(cfg);
35272                     throw "Can not add '" + cfg.xtype + "' to Border";
35273                     return null;
35274              
35275                                 
35276              
35277         }
35278         this.beginUpdate();
35279         // add children..
35280         var region = '';
35281         var abn = {};
35282         Roo.each(xitems, function(i)  {
35283             region = nb && i.region ? i.region : false;
35284             
35285             var add = ret.addxtype(i);
35286            
35287             if (region) {
35288                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35289                 if (!i.background) {
35290                     abn[region] = nb[region] ;
35291                 }
35292             }
35293             
35294         });
35295         this.endUpdate();
35296
35297         // make the last non-background panel active..
35298         //if (nb) { Roo.log(abn); }
35299         if (nb) {
35300             
35301             for(var r in abn) {
35302                 region = this.getRegion(r);
35303                 if (region) {
35304                     // tried using nb[r], but it does not work..
35305                      
35306                     region.showPanel(abn[r]);
35307                    
35308                 }
35309             }
35310         }
35311         return ret;
35312         
35313     },
35314     
35315     
35316 // private
35317     factory : function(cfg)
35318     {
35319         
35320         var validRegions = Roo.bootstrap.layout.Border.regions;
35321
35322         var target = cfg.region;
35323         cfg.mgr = this;
35324         
35325         var r = Roo.bootstrap.layout;
35326         Roo.log(target);
35327         switch(target){
35328             case "north":
35329                 return new r.North(cfg);
35330             case "south":
35331                 return new r.South(cfg);
35332             case "east":
35333                 return new r.East(cfg);
35334             case "west":
35335                 return new r.West(cfg);
35336             case "center":
35337                 return new r.Center(cfg);
35338         }
35339         throw 'Layout region "'+target+'" not supported.';
35340     }
35341     
35342     
35343 });
35344  /*
35345  * Based on:
35346  * Ext JS Library 1.1.1
35347  * Copyright(c) 2006-2007, Ext JS, LLC.
35348  *
35349  * Originally Released Under LGPL - original licence link has changed is not relivant.
35350  *
35351  * Fork - LGPL
35352  * <script type="text/javascript">
35353  */
35354  
35355 /**
35356  * @class Roo.bootstrap.layout.Basic
35357  * @extends Roo.util.Observable
35358  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35359  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35360  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35361  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35362  * @cfg {string}   region  the region that it inhabits..
35363  * @cfg {bool}   skipConfig skip config?
35364  * 
35365
35366  */
35367 Roo.bootstrap.layout.Basic = function(config){
35368     
35369     this.mgr = config.mgr;
35370     
35371     this.position = config.region;
35372     
35373     var skipConfig = config.skipConfig;
35374     
35375     this.events = {
35376         /**
35377          * @scope Roo.BasicLayoutRegion
35378          */
35379         
35380         /**
35381          * @event beforeremove
35382          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35383          * @param {Roo.LayoutRegion} this
35384          * @param {Roo.ContentPanel} panel The panel
35385          * @param {Object} e The cancel event object
35386          */
35387         "beforeremove" : true,
35388         /**
35389          * @event invalidated
35390          * Fires when the layout for this region is changed.
35391          * @param {Roo.LayoutRegion} this
35392          */
35393         "invalidated" : true,
35394         /**
35395          * @event visibilitychange
35396          * Fires when this region is shown or hidden 
35397          * @param {Roo.LayoutRegion} this
35398          * @param {Boolean} visibility true or false
35399          */
35400         "visibilitychange" : true,
35401         /**
35402          * @event paneladded
35403          * Fires when a panel is added. 
35404          * @param {Roo.LayoutRegion} this
35405          * @param {Roo.ContentPanel} panel The panel
35406          */
35407         "paneladded" : true,
35408         /**
35409          * @event panelremoved
35410          * Fires when a panel is removed. 
35411          * @param {Roo.LayoutRegion} this
35412          * @param {Roo.ContentPanel} panel The panel
35413          */
35414         "panelremoved" : true,
35415         /**
35416          * @event beforecollapse
35417          * Fires when this region before collapse.
35418          * @param {Roo.LayoutRegion} this
35419          */
35420         "beforecollapse" : true,
35421         /**
35422          * @event collapsed
35423          * Fires when this region is collapsed.
35424          * @param {Roo.LayoutRegion} this
35425          */
35426         "collapsed" : true,
35427         /**
35428          * @event expanded
35429          * Fires when this region is expanded.
35430          * @param {Roo.LayoutRegion} this
35431          */
35432         "expanded" : true,
35433         /**
35434          * @event slideshow
35435          * Fires when this region is slid into view.
35436          * @param {Roo.LayoutRegion} this
35437          */
35438         "slideshow" : true,
35439         /**
35440          * @event slidehide
35441          * Fires when this region slides out of view. 
35442          * @param {Roo.LayoutRegion} this
35443          */
35444         "slidehide" : true,
35445         /**
35446          * @event panelactivated
35447          * Fires when a panel is activated. 
35448          * @param {Roo.LayoutRegion} this
35449          * @param {Roo.ContentPanel} panel The activated panel
35450          */
35451         "panelactivated" : true,
35452         /**
35453          * @event resized
35454          * Fires when the user resizes this region. 
35455          * @param {Roo.LayoutRegion} this
35456          * @param {Number} newSize The new size (width for east/west, height for north/south)
35457          */
35458         "resized" : true
35459     };
35460     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35461     this.panels = new Roo.util.MixedCollection();
35462     this.panels.getKey = this.getPanelId.createDelegate(this);
35463     this.box = null;
35464     this.activePanel = null;
35465     // ensure listeners are added...
35466     
35467     if (config.listeners || config.events) {
35468         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35469             listeners : config.listeners || {},
35470             events : config.events || {}
35471         });
35472     }
35473     
35474     if(skipConfig !== true){
35475         this.applyConfig(config);
35476     }
35477 };
35478
35479 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35480 {
35481     getPanelId : function(p){
35482         return p.getId();
35483     },
35484     
35485     applyConfig : function(config){
35486         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35487         this.config = config;
35488         
35489     },
35490     
35491     /**
35492      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35493      * the width, for horizontal (north, south) the height.
35494      * @param {Number} newSize The new width or height
35495      */
35496     resizeTo : function(newSize){
35497         var el = this.el ? this.el :
35498                  (this.activePanel ? this.activePanel.getEl() : null);
35499         if(el){
35500             switch(this.position){
35501                 case "east":
35502                 case "west":
35503                     el.setWidth(newSize);
35504                     this.fireEvent("resized", this, newSize);
35505                 break;
35506                 case "north":
35507                 case "south":
35508                     el.setHeight(newSize);
35509                     this.fireEvent("resized", this, newSize);
35510                 break;                
35511             }
35512         }
35513     },
35514     
35515     getBox : function(){
35516         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35517     },
35518     
35519     getMargins : function(){
35520         return this.margins;
35521     },
35522     
35523     updateBox : function(box){
35524         this.box = box;
35525         var el = this.activePanel.getEl();
35526         el.dom.style.left = box.x + "px";
35527         el.dom.style.top = box.y + "px";
35528         this.activePanel.setSize(box.width, box.height);
35529     },
35530     
35531     /**
35532      * Returns the container element for this region.
35533      * @return {Roo.Element}
35534      */
35535     getEl : function(){
35536         return this.activePanel;
35537     },
35538     
35539     /**
35540      * Returns true if this region is currently visible.
35541      * @return {Boolean}
35542      */
35543     isVisible : function(){
35544         return this.activePanel ? true : false;
35545     },
35546     
35547     setActivePanel : function(panel){
35548         panel = this.getPanel(panel);
35549         if(this.activePanel && this.activePanel != panel){
35550             this.activePanel.setActiveState(false);
35551             this.activePanel.getEl().setLeftTop(-10000,-10000);
35552         }
35553         this.activePanel = panel;
35554         panel.setActiveState(true);
35555         if(this.box){
35556             panel.setSize(this.box.width, this.box.height);
35557         }
35558         this.fireEvent("panelactivated", this, panel);
35559         this.fireEvent("invalidated");
35560     },
35561     
35562     /**
35563      * Show the specified panel.
35564      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35565      * @return {Roo.ContentPanel} The shown panel or null
35566      */
35567     showPanel : function(panel){
35568         panel = this.getPanel(panel);
35569         if(panel){
35570             this.setActivePanel(panel);
35571         }
35572         return panel;
35573     },
35574     
35575     /**
35576      * Get the active panel for this region.
35577      * @return {Roo.ContentPanel} The active panel or null
35578      */
35579     getActivePanel : function(){
35580         return this.activePanel;
35581     },
35582     
35583     /**
35584      * Add the passed ContentPanel(s)
35585      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35586      * @return {Roo.ContentPanel} The panel added (if only one was added)
35587      */
35588     add : function(panel){
35589         if(arguments.length > 1){
35590             for(var i = 0, len = arguments.length; i < len; i++) {
35591                 this.add(arguments[i]);
35592             }
35593             return null;
35594         }
35595         if(this.hasPanel(panel)){
35596             this.showPanel(panel);
35597             return panel;
35598         }
35599         var el = panel.getEl();
35600         if(el.dom.parentNode != this.mgr.el.dom){
35601             this.mgr.el.dom.appendChild(el.dom);
35602         }
35603         if(panel.setRegion){
35604             panel.setRegion(this);
35605         }
35606         this.panels.add(panel);
35607         el.setStyle("position", "absolute");
35608         if(!panel.background){
35609             this.setActivePanel(panel);
35610             if(this.config.initialSize && this.panels.getCount()==1){
35611                 this.resizeTo(this.config.initialSize);
35612             }
35613         }
35614         this.fireEvent("paneladded", this, panel);
35615         return panel;
35616     },
35617     
35618     /**
35619      * Returns true if the panel is in this region.
35620      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35621      * @return {Boolean}
35622      */
35623     hasPanel : function(panel){
35624         if(typeof panel == "object"){ // must be panel obj
35625             panel = panel.getId();
35626         }
35627         return this.getPanel(panel) ? true : false;
35628     },
35629     
35630     /**
35631      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35632      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35633      * @param {Boolean} preservePanel Overrides the config preservePanel option
35634      * @return {Roo.ContentPanel} The panel that was removed
35635      */
35636     remove : function(panel, preservePanel){
35637         panel = this.getPanel(panel);
35638         if(!panel){
35639             return null;
35640         }
35641         var e = {};
35642         this.fireEvent("beforeremove", this, panel, e);
35643         if(e.cancel === true){
35644             return null;
35645         }
35646         var panelId = panel.getId();
35647         this.panels.removeKey(panelId);
35648         return panel;
35649     },
35650     
35651     /**
35652      * Returns the panel specified or null if it's not in this region.
35653      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35654      * @return {Roo.ContentPanel}
35655      */
35656     getPanel : function(id){
35657         if(typeof id == "object"){ // must be panel obj
35658             return id;
35659         }
35660         return this.panels.get(id);
35661     },
35662     
35663     /**
35664      * Returns this regions position (north/south/east/west/center).
35665      * @return {String} 
35666      */
35667     getPosition: function(){
35668         return this.position;    
35669     }
35670 });/*
35671  * Based on:
35672  * Ext JS Library 1.1.1
35673  * Copyright(c) 2006-2007, Ext JS, LLC.
35674  *
35675  * Originally Released Under LGPL - original licence link has changed is not relivant.
35676  *
35677  * Fork - LGPL
35678  * <script type="text/javascript">
35679  */
35680  
35681 /**
35682  * @class Roo.bootstrap.layout.Region
35683  * @extends Roo.bootstrap.layout.Basic
35684  * This class represents a region in a layout manager.
35685  
35686  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35687  * @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})
35688  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35689  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35690  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35691  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35692  * @cfg {String}    title           The title for the region (overrides panel titles)
35693  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35694  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35695  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35696  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35697  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35698  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35699  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35700  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35701  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35702  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35703
35704  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35705  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35706  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35707  * @cfg {Number}    width           For East/West panels
35708  * @cfg {Number}    height          For North/South panels
35709  * @cfg {Boolean}   split           To show the splitter
35710  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35711  * 
35712  * @cfg {string}   cls             Extra CSS classes to add to region
35713  * 
35714  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35715  * @cfg {string}   region  the region that it inhabits..
35716  *
35717
35718  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35719  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35720
35721  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35722  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35723  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35724  */
35725 Roo.bootstrap.layout.Region = function(config)
35726 {
35727     this.applyConfig(config);
35728
35729     var mgr = config.mgr;
35730     var pos = config.region;
35731     config.skipConfig = true;
35732     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35733     
35734     if (mgr.el) {
35735         this.onRender(mgr.el);   
35736     }
35737      
35738     this.visible = true;
35739     this.collapsed = false;
35740     this.unrendered_panels = [];
35741 };
35742
35743 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35744
35745     position: '', // set by wrapper (eg. north/south etc..)
35746     unrendered_panels : null,  // unrendered panels.
35747     createBody : function(){
35748         /** This region's body element 
35749         * @type Roo.Element */
35750         this.bodyEl = this.el.createChild({
35751                 tag: "div",
35752                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35753         });
35754     },
35755
35756     onRender: function(ctr, pos)
35757     {
35758         var dh = Roo.DomHelper;
35759         /** This region's container element 
35760         * @type Roo.Element */
35761         this.el = dh.append(ctr.dom, {
35762                 tag: "div",
35763                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35764             }, true);
35765         /** This region's title element 
35766         * @type Roo.Element */
35767     
35768         this.titleEl = dh.append(this.el.dom,
35769             {
35770                     tag: "div",
35771                     unselectable: "on",
35772                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35773                     children:[
35774                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35775                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35776                     ]}, true);
35777         
35778         this.titleEl.enableDisplayMode();
35779         /** This region's title text element 
35780         * @type HTMLElement */
35781         this.titleTextEl = this.titleEl.dom.firstChild;
35782         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35783         /*
35784         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35785         this.closeBtn.enableDisplayMode();
35786         this.closeBtn.on("click", this.closeClicked, this);
35787         this.closeBtn.hide();
35788     */
35789         this.createBody(this.config);
35790         if(this.config.hideWhenEmpty){
35791             this.hide();
35792             this.on("paneladded", this.validateVisibility, this);
35793             this.on("panelremoved", this.validateVisibility, this);
35794         }
35795         if(this.autoScroll){
35796             this.bodyEl.setStyle("overflow", "auto");
35797         }else{
35798             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35799         }
35800         //if(c.titlebar !== false){
35801             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35802                 this.titleEl.hide();
35803             }else{
35804                 this.titleEl.show();
35805                 if(this.config.title){
35806                     this.titleTextEl.innerHTML = this.config.title;
35807                 }
35808             }
35809         //}
35810         if(this.config.collapsed){
35811             this.collapse(true);
35812         }
35813         if(this.config.hidden){
35814             this.hide();
35815         }
35816         
35817         if (this.unrendered_panels && this.unrendered_panels.length) {
35818             for (var i =0;i< this.unrendered_panels.length; i++) {
35819                 this.add(this.unrendered_panels[i]);
35820             }
35821             this.unrendered_panels = null;
35822             
35823         }
35824         
35825     },
35826     
35827     applyConfig : function(c)
35828     {
35829         /*
35830          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35831             var dh = Roo.DomHelper;
35832             if(c.titlebar !== false){
35833                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35834                 this.collapseBtn.on("click", this.collapse, this);
35835                 this.collapseBtn.enableDisplayMode();
35836                 /*
35837                 if(c.showPin === true || this.showPin){
35838                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35839                     this.stickBtn.enableDisplayMode();
35840                     this.stickBtn.on("click", this.expand, this);
35841                     this.stickBtn.hide();
35842                 }
35843                 
35844             }
35845             */
35846             /** This region's collapsed element
35847             * @type Roo.Element */
35848             /*
35849              *
35850             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35851                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35852             ]}, true);
35853             
35854             if(c.floatable !== false){
35855                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35856                this.collapsedEl.on("click", this.collapseClick, this);
35857             }
35858
35859             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35860                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35861                    id: "message", unselectable: "on", style:{"float":"left"}});
35862                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35863              }
35864             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35865             this.expandBtn.on("click", this.expand, this);
35866             
35867         }
35868         
35869         if(this.collapseBtn){
35870             this.collapseBtn.setVisible(c.collapsible == true);
35871         }
35872         
35873         this.cmargins = c.cmargins || this.cmargins ||
35874                          (this.position == "west" || this.position == "east" ?
35875                              {top: 0, left: 2, right:2, bottom: 0} :
35876                              {top: 2, left: 0, right:0, bottom: 2});
35877         */
35878         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35879         
35880         
35881         this.bottomTabs = c.tabPosition != "top";
35882         
35883         this.autoScroll = c.autoScroll || false;
35884         
35885         
35886        
35887         
35888         this.duration = c.duration || .30;
35889         this.slideDuration = c.slideDuration || .45;
35890         this.config = c;
35891        
35892     },
35893     /**
35894      * Returns true if this region is currently visible.
35895      * @return {Boolean}
35896      */
35897     isVisible : function(){
35898         return this.visible;
35899     },
35900
35901     /**
35902      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35903      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35904      */
35905     //setCollapsedTitle : function(title){
35906     //    title = title || "&#160;";
35907      //   if(this.collapsedTitleTextEl){
35908       //      this.collapsedTitleTextEl.innerHTML = title;
35909        // }
35910     //},
35911
35912     getBox : function(){
35913         var b;
35914       //  if(!this.collapsed){
35915             b = this.el.getBox(false, true);
35916        // }else{
35917           //  b = this.collapsedEl.getBox(false, true);
35918         //}
35919         return b;
35920     },
35921
35922     getMargins : function(){
35923         return this.margins;
35924         //return this.collapsed ? this.cmargins : this.margins;
35925     },
35926 /*
35927     highlight : function(){
35928         this.el.addClass("x-layout-panel-dragover");
35929     },
35930
35931     unhighlight : function(){
35932         this.el.removeClass("x-layout-panel-dragover");
35933     },
35934 */
35935     updateBox : function(box)
35936     {
35937         if (!this.bodyEl) {
35938             return; // not rendered yet..
35939         }
35940         
35941         this.box = box;
35942         if(!this.collapsed){
35943             this.el.dom.style.left = box.x + "px";
35944             this.el.dom.style.top = box.y + "px";
35945             this.updateBody(box.width, box.height);
35946         }else{
35947             this.collapsedEl.dom.style.left = box.x + "px";
35948             this.collapsedEl.dom.style.top = box.y + "px";
35949             this.collapsedEl.setSize(box.width, box.height);
35950         }
35951         if(this.tabs){
35952             this.tabs.autoSizeTabs();
35953         }
35954     },
35955
35956     updateBody : function(w, h)
35957     {
35958         if(w !== null){
35959             this.el.setWidth(w);
35960             w -= this.el.getBorderWidth("rl");
35961             if(this.config.adjustments){
35962                 w += this.config.adjustments[0];
35963             }
35964         }
35965         if(h !== null && h > 0){
35966             this.el.setHeight(h);
35967             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35968             h -= this.el.getBorderWidth("tb");
35969             if(this.config.adjustments){
35970                 h += this.config.adjustments[1];
35971             }
35972             this.bodyEl.setHeight(h);
35973             if(this.tabs){
35974                 h = this.tabs.syncHeight(h);
35975             }
35976         }
35977         if(this.panelSize){
35978             w = w !== null ? w : this.panelSize.width;
35979             h = h !== null ? h : this.panelSize.height;
35980         }
35981         if(this.activePanel){
35982             var el = this.activePanel.getEl();
35983             w = w !== null ? w : el.getWidth();
35984             h = h !== null ? h : el.getHeight();
35985             this.panelSize = {width: w, height: h};
35986             this.activePanel.setSize(w, h);
35987         }
35988         if(Roo.isIE && this.tabs){
35989             this.tabs.el.repaint();
35990         }
35991     },
35992
35993     /**
35994      * Returns the container element for this region.
35995      * @return {Roo.Element}
35996      */
35997     getEl : function(){
35998         return this.el;
35999     },
36000
36001     /**
36002      * Hides this region.
36003      */
36004     hide : function(){
36005         //if(!this.collapsed){
36006             this.el.dom.style.left = "-2000px";
36007             this.el.hide();
36008         //}else{
36009          //   this.collapsedEl.dom.style.left = "-2000px";
36010          //   this.collapsedEl.hide();
36011        // }
36012         this.visible = false;
36013         this.fireEvent("visibilitychange", this, false);
36014     },
36015
36016     /**
36017      * Shows this region if it was previously hidden.
36018      */
36019     show : function(){
36020         //if(!this.collapsed){
36021             this.el.show();
36022         //}else{
36023         //    this.collapsedEl.show();
36024        // }
36025         this.visible = true;
36026         this.fireEvent("visibilitychange", this, true);
36027     },
36028 /*
36029     closeClicked : function(){
36030         if(this.activePanel){
36031             this.remove(this.activePanel);
36032         }
36033     },
36034
36035     collapseClick : function(e){
36036         if(this.isSlid){
36037            e.stopPropagation();
36038            this.slideIn();
36039         }else{
36040            e.stopPropagation();
36041            this.slideOut();
36042         }
36043     },
36044 */
36045     /**
36046      * Collapses this region.
36047      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36048      */
36049     /*
36050     collapse : function(skipAnim, skipCheck = false){
36051         if(this.collapsed) {
36052             return;
36053         }
36054         
36055         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36056             
36057             this.collapsed = true;
36058             if(this.split){
36059                 this.split.el.hide();
36060             }
36061             if(this.config.animate && skipAnim !== true){
36062                 this.fireEvent("invalidated", this);
36063                 this.animateCollapse();
36064             }else{
36065                 this.el.setLocation(-20000,-20000);
36066                 this.el.hide();
36067                 this.collapsedEl.show();
36068                 this.fireEvent("collapsed", this);
36069                 this.fireEvent("invalidated", this);
36070             }
36071         }
36072         
36073     },
36074 */
36075     animateCollapse : function(){
36076         // overridden
36077     },
36078
36079     /**
36080      * Expands this region if it was previously collapsed.
36081      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36082      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36083      */
36084     /*
36085     expand : function(e, skipAnim){
36086         if(e) {
36087             e.stopPropagation();
36088         }
36089         if(!this.collapsed || this.el.hasActiveFx()) {
36090             return;
36091         }
36092         if(this.isSlid){
36093             this.afterSlideIn();
36094             skipAnim = true;
36095         }
36096         this.collapsed = false;
36097         if(this.config.animate && skipAnim !== true){
36098             this.animateExpand();
36099         }else{
36100             this.el.show();
36101             if(this.split){
36102                 this.split.el.show();
36103             }
36104             this.collapsedEl.setLocation(-2000,-2000);
36105             this.collapsedEl.hide();
36106             this.fireEvent("invalidated", this);
36107             this.fireEvent("expanded", this);
36108         }
36109     },
36110 */
36111     animateExpand : function(){
36112         // overridden
36113     },
36114
36115     initTabs : function()
36116     {
36117         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36118         
36119         var ts = new Roo.bootstrap.panel.Tabs({
36120                 el: this.bodyEl.dom,
36121                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36122                 disableTooltips: this.config.disableTabTips,
36123                 toolbar : this.config.toolbar
36124             });
36125         
36126         if(this.config.hideTabs){
36127             ts.stripWrap.setDisplayed(false);
36128         }
36129         this.tabs = ts;
36130         ts.resizeTabs = this.config.resizeTabs === true;
36131         ts.minTabWidth = this.config.minTabWidth || 40;
36132         ts.maxTabWidth = this.config.maxTabWidth || 250;
36133         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36134         ts.monitorResize = false;
36135         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36136         ts.bodyEl.addClass('roo-layout-tabs-body');
36137         this.panels.each(this.initPanelAsTab, this);
36138     },
36139
36140     initPanelAsTab : function(panel){
36141         var ti = this.tabs.addTab(
36142             panel.getEl().id,
36143             panel.getTitle(),
36144             null,
36145             this.config.closeOnTab && panel.isClosable(),
36146             panel.tpl
36147         );
36148         if(panel.tabTip !== undefined){
36149             ti.setTooltip(panel.tabTip);
36150         }
36151         ti.on("activate", function(){
36152               this.setActivePanel(panel);
36153         }, this);
36154         
36155         if(this.config.closeOnTab){
36156             ti.on("beforeclose", function(t, e){
36157                 e.cancel = true;
36158                 this.remove(panel);
36159             }, this);
36160         }
36161         
36162         panel.tabItem = ti;
36163         
36164         return ti;
36165     },
36166
36167     updatePanelTitle : function(panel, title)
36168     {
36169         if(this.activePanel == panel){
36170             this.updateTitle(title);
36171         }
36172         if(this.tabs){
36173             var ti = this.tabs.getTab(panel.getEl().id);
36174             ti.setText(title);
36175             if(panel.tabTip !== undefined){
36176                 ti.setTooltip(panel.tabTip);
36177             }
36178         }
36179     },
36180
36181     updateTitle : function(title){
36182         if(this.titleTextEl && !this.config.title){
36183             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36184         }
36185     },
36186
36187     setActivePanel : function(panel)
36188     {
36189         panel = this.getPanel(panel);
36190         if(this.activePanel && this.activePanel != panel){
36191             if(this.activePanel.setActiveState(false) === false){
36192                 return;
36193             }
36194         }
36195         this.activePanel = panel;
36196         panel.setActiveState(true);
36197         if(this.panelSize){
36198             panel.setSize(this.panelSize.width, this.panelSize.height);
36199         }
36200         if(this.closeBtn){
36201             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36202         }
36203         this.updateTitle(panel.getTitle());
36204         if(this.tabs){
36205             this.fireEvent("invalidated", this);
36206         }
36207         this.fireEvent("panelactivated", this, panel);
36208     },
36209
36210     /**
36211      * Shows the specified panel.
36212      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36213      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36214      */
36215     showPanel : function(panel)
36216     {
36217         panel = this.getPanel(panel);
36218         if(panel){
36219             if(this.tabs){
36220                 var tab = this.tabs.getTab(panel.getEl().id);
36221                 if(tab.isHidden()){
36222                     this.tabs.unhideTab(tab.id);
36223                 }
36224                 tab.activate();
36225             }else{
36226                 this.setActivePanel(panel);
36227             }
36228         }
36229         return panel;
36230     },
36231
36232     /**
36233      * Get the active panel for this region.
36234      * @return {Roo.ContentPanel} The active panel or null
36235      */
36236     getActivePanel : function(){
36237         return this.activePanel;
36238     },
36239
36240     validateVisibility : function(){
36241         if(this.panels.getCount() < 1){
36242             this.updateTitle("&#160;");
36243             this.closeBtn.hide();
36244             this.hide();
36245         }else{
36246             if(!this.isVisible()){
36247                 this.show();
36248             }
36249         }
36250     },
36251
36252     /**
36253      * Adds the passed ContentPanel(s) to this region.
36254      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36255      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36256      */
36257     add : function(panel)
36258     {
36259         if(arguments.length > 1){
36260             for(var i = 0, len = arguments.length; i < len; i++) {
36261                 this.add(arguments[i]);
36262             }
36263             return null;
36264         }
36265         
36266         // if we have not been rendered yet, then we can not really do much of this..
36267         if (!this.bodyEl) {
36268             this.unrendered_panels.push(panel);
36269             return panel;
36270         }
36271         
36272         
36273         
36274         
36275         if(this.hasPanel(panel)){
36276             this.showPanel(panel);
36277             return panel;
36278         }
36279         panel.setRegion(this);
36280         this.panels.add(panel);
36281        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36282             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36283             // and hide them... ???
36284             this.bodyEl.dom.appendChild(panel.getEl().dom);
36285             if(panel.background !== true){
36286                 this.setActivePanel(panel);
36287             }
36288             this.fireEvent("paneladded", this, panel);
36289             return panel;
36290         }
36291         */
36292         if(!this.tabs){
36293             this.initTabs();
36294         }else{
36295             this.initPanelAsTab(panel);
36296         }
36297         
36298         
36299         if(panel.background !== true){
36300             this.tabs.activate(panel.getEl().id);
36301         }
36302         this.fireEvent("paneladded", this, panel);
36303         return panel;
36304     },
36305
36306     /**
36307      * Hides the tab for the specified panel.
36308      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36309      */
36310     hidePanel : function(panel){
36311         if(this.tabs && (panel = this.getPanel(panel))){
36312             this.tabs.hideTab(panel.getEl().id);
36313         }
36314     },
36315
36316     /**
36317      * Unhides the tab for a previously hidden panel.
36318      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36319      */
36320     unhidePanel : function(panel){
36321         if(this.tabs && (panel = this.getPanel(panel))){
36322             this.tabs.unhideTab(panel.getEl().id);
36323         }
36324     },
36325
36326     clearPanels : function(){
36327         while(this.panels.getCount() > 0){
36328              this.remove(this.panels.first());
36329         }
36330     },
36331
36332     /**
36333      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36334      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36335      * @param {Boolean} preservePanel Overrides the config preservePanel option
36336      * @return {Roo.ContentPanel} The panel that was removed
36337      */
36338     remove : function(panel, preservePanel)
36339     {
36340         panel = this.getPanel(panel);
36341         if(!panel){
36342             return null;
36343         }
36344         var e = {};
36345         this.fireEvent("beforeremove", this, panel, e);
36346         if(e.cancel === true){
36347             return null;
36348         }
36349         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36350         var panelId = panel.getId();
36351         this.panels.removeKey(panelId);
36352         if(preservePanel){
36353             document.body.appendChild(panel.getEl().dom);
36354         }
36355         if(this.tabs){
36356             this.tabs.removeTab(panel.getEl().id);
36357         }else if (!preservePanel){
36358             this.bodyEl.dom.removeChild(panel.getEl().dom);
36359         }
36360         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36361             var p = this.panels.first();
36362             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36363             tempEl.appendChild(p.getEl().dom);
36364             this.bodyEl.update("");
36365             this.bodyEl.dom.appendChild(p.getEl().dom);
36366             tempEl = null;
36367             this.updateTitle(p.getTitle());
36368             this.tabs = null;
36369             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36370             this.setActivePanel(p);
36371         }
36372         panel.setRegion(null);
36373         if(this.activePanel == panel){
36374             this.activePanel = null;
36375         }
36376         if(this.config.autoDestroy !== false && preservePanel !== true){
36377             try{panel.destroy();}catch(e){}
36378         }
36379         this.fireEvent("panelremoved", this, panel);
36380         return panel;
36381     },
36382
36383     /**
36384      * Returns the TabPanel component used by this region
36385      * @return {Roo.TabPanel}
36386      */
36387     getTabs : function(){
36388         return this.tabs;
36389     },
36390
36391     createTool : function(parentEl, className){
36392         var btn = Roo.DomHelper.append(parentEl, {
36393             tag: "div",
36394             cls: "x-layout-tools-button",
36395             children: [ {
36396                 tag: "div",
36397                 cls: "roo-layout-tools-button-inner " + className,
36398                 html: "&#160;"
36399             }]
36400         }, true);
36401         btn.addClassOnOver("roo-layout-tools-button-over");
36402         return btn;
36403     }
36404 });/*
36405  * Based on:
36406  * Ext JS Library 1.1.1
36407  * Copyright(c) 2006-2007, Ext JS, LLC.
36408  *
36409  * Originally Released Under LGPL - original licence link has changed is not relivant.
36410  *
36411  * Fork - LGPL
36412  * <script type="text/javascript">
36413  */
36414  
36415
36416
36417 /**
36418  * @class Roo.SplitLayoutRegion
36419  * @extends Roo.LayoutRegion
36420  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36421  */
36422 Roo.bootstrap.layout.Split = function(config){
36423     this.cursor = config.cursor;
36424     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36425 };
36426
36427 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36428 {
36429     splitTip : "Drag to resize.",
36430     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36431     useSplitTips : false,
36432
36433     applyConfig : function(config){
36434         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36435     },
36436     
36437     onRender : function(ctr,pos) {
36438         
36439         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36440         if(!this.config.split){
36441             return;
36442         }
36443         if(!this.split){
36444             
36445             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36446                             tag: "div",
36447                             id: this.el.id + "-split",
36448                             cls: "roo-layout-split roo-layout-split-"+this.position,
36449                             html: "&#160;"
36450             });
36451             /** The SplitBar for this region 
36452             * @type Roo.SplitBar */
36453             // does not exist yet...
36454             Roo.log([this.position, this.orientation]);
36455             
36456             this.split = new Roo.bootstrap.SplitBar({
36457                 dragElement : splitEl,
36458                 resizingElement: this.el,
36459                 orientation : this.orientation
36460             });
36461             
36462             this.split.on("moved", this.onSplitMove, this);
36463             this.split.useShim = this.config.useShim === true;
36464             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36465             if(this.useSplitTips){
36466                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36467             }
36468             //if(config.collapsible){
36469             //    this.split.el.on("dblclick", this.collapse,  this);
36470             //}
36471         }
36472         if(typeof this.config.minSize != "undefined"){
36473             this.split.minSize = this.config.minSize;
36474         }
36475         if(typeof this.config.maxSize != "undefined"){
36476             this.split.maxSize = this.config.maxSize;
36477         }
36478         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36479             this.hideSplitter();
36480         }
36481         
36482     },
36483
36484     getHMaxSize : function(){
36485          var cmax = this.config.maxSize || 10000;
36486          var center = this.mgr.getRegion("center");
36487          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36488     },
36489
36490     getVMaxSize : function(){
36491          var cmax = this.config.maxSize || 10000;
36492          var center = this.mgr.getRegion("center");
36493          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36494     },
36495
36496     onSplitMove : function(split, newSize){
36497         this.fireEvent("resized", this, newSize);
36498     },
36499     
36500     /** 
36501      * Returns the {@link Roo.SplitBar} for this region.
36502      * @return {Roo.SplitBar}
36503      */
36504     getSplitBar : function(){
36505         return this.split;
36506     },
36507     
36508     hide : function(){
36509         this.hideSplitter();
36510         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36511     },
36512
36513     hideSplitter : function(){
36514         if(this.split){
36515             this.split.el.setLocation(-2000,-2000);
36516             this.split.el.hide();
36517         }
36518     },
36519
36520     show : function(){
36521         if(this.split){
36522             this.split.el.show();
36523         }
36524         Roo.bootstrap.layout.Split.superclass.show.call(this);
36525     },
36526     
36527     beforeSlide: function(){
36528         if(Roo.isGecko){// firefox overflow auto bug workaround
36529             this.bodyEl.clip();
36530             if(this.tabs) {
36531                 this.tabs.bodyEl.clip();
36532             }
36533             if(this.activePanel){
36534                 this.activePanel.getEl().clip();
36535                 
36536                 if(this.activePanel.beforeSlide){
36537                     this.activePanel.beforeSlide();
36538                 }
36539             }
36540         }
36541     },
36542     
36543     afterSlide : function(){
36544         if(Roo.isGecko){// firefox overflow auto bug workaround
36545             this.bodyEl.unclip();
36546             if(this.tabs) {
36547                 this.tabs.bodyEl.unclip();
36548             }
36549             if(this.activePanel){
36550                 this.activePanel.getEl().unclip();
36551                 if(this.activePanel.afterSlide){
36552                     this.activePanel.afterSlide();
36553                 }
36554             }
36555         }
36556     },
36557
36558     initAutoHide : function(){
36559         if(this.autoHide !== false){
36560             if(!this.autoHideHd){
36561                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36562                 this.autoHideHd = {
36563                     "mouseout": function(e){
36564                         if(!e.within(this.el, true)){
36565                             st.delay(500);
36566                         }
36567                     },
36568                     "mouseover" : function(e){
36569                         st.cancel();
36570                     },
36571                     scope : this
36572                 };
36573             }
36574             this.el.on(this.autoHideHd);
36575         }
36576     },
36577
36578     clearAutoHide : function(){
36579         if(this.autoHide !== false){
36580             this.el.un("mouseout", this.autoHideHd.mouseout);
36581             this.el.un("mouseover", this.autoHideHd.mouseover);
36582         }
36583     },
36584
36585     clearMonitor : function(){
36586         Roo.get(document).un("click", this.slideInIf, this);
36587     },
36588
36589     // these names are backwards but not changed for compat
36590     slideOut : function(){
36591         if(this.isSlid || this.el.hasActiveFx()){
36592             return;
36593         }
36594         this.isSlid = true;
36595         if(this.collapseBtn){
36596             this.collapseBtn.hide();
36597         }
36598         this.closeBtnState = this.closeBtn.getStyle('display');
36599         this.closeBtn.hide();
36600         if(this.stickBtn){
36601             this.stickBtn.show();
36602         }
36603         this.el.show();
36604         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36605         this.beforeSlide();
36606         this.el.setStyle("z-index", 10001);
36607         this.el.slideIn(this.getSlideAnchor(), {
36608             callback: function(){
36609                 this.afterSlide();
36610                 this.initAutoHide();
36611                 Roo.get(document).on("click", this.slideInIf, this);
36612                 this.fireEvent("slideshow", this);
36613             },
36614             scope: this,
36615             block: true
36616         });
36617     },
36618
36619     afterSlideIn : function(){
36620         this.clearAutoHide();
36621         this.isSlid = false;
36622         this.clearMonitor();
36623         this.el.setStyle("z-index", "");
36624         if(this.collapseBtn){
36625             this.collapseBtn.show();
36626         }
36627         this.closeBtn.setStyle('display', this.closeBtnState);
36628         if(this.stickBtn){
36629             this.stickBtn.hide();
36630         }
36631         this.fireEvent("slidehide", this);
36632     },
36633
36634     slideIn : function(cb){
36635         if(!this.isSlid || this.el.hasActiveFx()){
36636             Roo.callback(cb);
36637             return;
36638         }
36639         this.isSlid = false;
36640         this.beforeSlide();
36641         this.el.slideOut(this.getSlideAnchor(), {
36642             callback: function(){
36643                 this.el.setLeftTop(-10000, -10000);
36644                 this.afterSlide();
36645                 this.afterSlideIn();
36646                 Roo.callback(cb);
36647             },
36648             scope: this,
36649             block: true
36650         });
36651     },
36652     
36653     slideInIf : function(e){
36654         if(!e.within(this.el)){
36655             this.slideIn();
36656         }
36657     },
36658
36659     animateCollapse : function(){
36660         this.beforeSlide();
36661         this.el.setStyle("z-index", 20000);
36662         var anchor = this.getSlideAnchor();
36663         this.el.slideOut(anchor, {
36664             callback : function(){
36665                 this.el.setStyle("z-index", "");
36666                 this.collapsedEl.slideIn(anchor, {duration:.3});
36667                 this.afterSlide();
36668                 this.el.setLocation(-10000,-10000);
36669                 this.el.hide();
36670                 this.fireEvent("collapsed", this);
36671             },
36672             scope: this,
36673             block: true
36674         });
36675     },
36676
36677     animateExpand : function(){
36678         this.beforeSlide();
36679         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36680         this.el.setStyle("z-index", 20000);
36681         this.collapsedEl.hide({
36682             duration:.1
36683         });
36684         this.el.slideIn(this.getSlideAnchor(), {
36685             callback : function(){
36686                 this.el.setStyle("z-index", "");
36687                 this.afterSlide();
36688                 if(this.split){
36689                     this.split.el.show();
36690                 }
36691                 this.fireEvent("invalidated", this);
36692                 this.fireEvent("expanded", this);
36693             },
36694             scope: this,
36695             block: true
36696         });
36697     },
36698
36699     anchors : {
36700         "west" : "left",
36701         "east" : "right",
36702         "north" : "top",
36703         "south" : "bottom"
36704     },
36705
36706     sanchors : {
36707         "west" : "l",
36708         "east" : "r",
36709         "north" : "t",
36710         "south" : "b"
36711     },
36712
36713     canchors : {
36714         "west" : "tl-tr",
36715         "east" : "tr-tl",
36716         "north" : "tl-bl",
36717         "south" : "bl-tl"
36718     },
36719
36720     getAnchor : function(){
36721         return this.anchors[this.position];
36722     },
36723
36724     getCollapseAnchor : function(){
36725         return this.canchors[this.position];
36726     },
36727
36728     getSlideAnchor : function(){
36729         return this.sanchors[this.position];
36730     },
36731
36732     getAlignAdj : function(){
36733         var cm = this.cmargins;
36734         switch(this.position){
36735             case "west":
36736                 return [0, 0];
36737             break;
36738             case "east":
36739                 return [0, 0];
36740             break;
36741             case "north":
36742                 return [0, 0];
36743             break;
36744             case "south":
36745                 return [0, 0];
36746             break;
36747         }
36748     },
36749
36750     getExpandAdj : function(){
36751         var c = this.collapsedEl, cm = this.cmargins;
36752         switch(this.position){
36753             case "west":
36754                 return [-(cm.right+c.getWidth()+cm.left), 0];
36755             break;
36756             case "east":
36757                 return [cm.right+c.getWidth()+cm.left, 0];
36758             break;
36759             case "north":
36760                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36761             break;
36762             case "south":
36763                 return [0, cm.top+cm.bottom+c.getHeight()];
36764             break;
36765         }
36766     }
36767 });/*
36768  * Based on:
36769  * Ext JS Library 1.1.1
36770  * Copyright(c) 2006-2007, Ext JS, LLC.
36771  *
36772  * Originally Released Under LGPL - original licence link has changed is not relivant.
36773  *
36774  * Fork - LGPL
36775  * <script type="text/javascript">
36776  */
36777 /*
36778  * These classes are private internal classes
36779  */
36780 Roo.bootstrap.layout.Center = function(config){
36781     config.region = "center";
36782     Roo.bootstrap.layout.Region.call(this, config);
36783     this.visible = true;
36784     this.minWidth = config.minWidth || 20;
36785     this.minHeight = config.minHeight || 20;
36786 };
36787
36788 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36789     hide : function(){
36790         // center panel can't be hidden
36791     },
36792     
36793     show : function(){
36794         // center panel can't be hidden
36795     },
36796     
36797     getMinWidth: function(){
36798         return this.minWidth;
36799     },
36800     
36801     getMinHeight: function(){
36802         return this.minHeight;
36803     }
36804 });
36805
36806
36807
36808
36809  
36810
36811
36812
36813
36814
36815 Roo.bootstrap.layout.North = function(config)
36816 {
36817     config.region = 'north';
36818     config.cursor = 'n-resize';
36819     
36820     Roo.bootstrap.layout.Split.call(this, config);
36821     
36822     
36823     if(this.split){
36824         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36825         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36826         this.split.el.addClass("roo-layout-split-v");
36827     }
36828     var size = config.initialSize || config.height;
36829     if(typeof size != "undefined"){
36830         this.el.setHeight(size);
36831     }
36832 };
36833 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36834 {
36835     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36836     
36837     
36838     
36839     getBox : function(){
36840         if(this.collapsed){
36841             return this.collapsedEl.getBox();
36842         }
36843         var box = this.el.getBox();
36844         if(this.split){
36845             box.height += this.split.el.getHeight();
36846         }
36847         return box;
36848     },
36849     
36850     updateBox : function(box){
36851         if(this.split && !this.collapsed){
36852             box.height -= this.split.el.getHeight();
36853             this.split.el.setLeft(box.x);
36854             this.split.el.setTop(box.y+box.height);
36855             this.split.el.setWidth(box.width);
36856         }
36857         if(this.collapsed){
36858             this.updateBody(box.width, null);
36859         }
36860         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36861     }
36862 });
36863
36864
36865
36866
36867
36868 Roo.bootstrap.layout.South = function(config){
36869     config.region = 'south';
36870     config.cursor = 's-resize';
36871     Roo.bootstrap.layout.Split.call(this, config);
36872     if(this.split){
36873         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36874         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36875         this.split.el.addClass("roo-layout-split-v");
36876     }
36877     var size = config.initialSize || config.height;
36878     if(typeof size != "undefined"){
36879         this.el.setHeight(size);
36880     }
36881 };
36882
36883 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36884     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36885     getBox : function(){
36886         if(this.collapsed){
36887             return this.collapsedEl.getBox();
36888         }
36889         var box = this.el.getBox();
36890         if(this.split){
36891             var sh = this.split.el.getHeight();
36892             box.height += sh;
36893             box.y -= sh;
36894         }
36895         return box;
36896     },
36897     
36898     updateBox : function(box){
36899         if(this.split && !this.collapsed){
36900             var sh = this.split.el.getHeight();
36901             box.height -= sh;
36902             box.y += sh;
36903             this.split.el.setLeft(box.x);
36904             this.split.el.setTop(box.y-sh);
36905             this.split.el.setWidth(box.width);
36906         }
36907         if(this.collapsed){
36908             this.updateBody(box.width, null);
36909         }
36910         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36911     }
36912 });
36913
36914 Roo.bootstrap.layout.East = function(config){
36915     config.region = "east";
36916     config.cursor = "e-resize";
36917     Roo.bootstrap.layout.Split.call(this, config);
36918     if(this.split){
36919         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36920         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36921         this.split.el.addClass("roo-layout-split-h");
36922     }
36923     var size = config.initialSize || config.width;
36924     if(typeof size != "undefined"){
36925         this.el.setWidth(size);
36926     }
36927 };
36928 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36929     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36930     getBox : function(){
36931         if(this.collapsed){
36932             return this.collapsedEl.getBox();
36933         }
36934         var box = this.el.getBox();
36935         if(this.split){
36936             var sw = this.split.el.getWidth();
36937             box.width += sw;
36938             box.x -= sw;
36939         }
36940         return box;
36941     },
36942
36943     updateBox : function(box){
36944         if(this.split && !this.collapsed){
36945             var sw = this.split.el.getWidth();
36946             box.width -= sw;
36947             this.split.el.setLeft(box.x);
36948             this.split.el.setTop(box.y);
36949             this.split.el.setHeight(box.height);
36950             box.x += sw;
36951         }
36952         if(this.collapsed){
36953             this.updateBody(null, box.height);
36954         }
36955         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36956     }
36957 });
36958
36959 Roo.bootstrap.layout.West = function(config){
36960     config.region = "west";
36961     config.cursor = "w-resize";
36962     
36963     Roo.bootstrap.layout.Split.call(this, config);
36964     if(this.split){
36965         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36966         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36967         this.split.el.addClass("roo-layout-split-h");
36968     }
36969     
36970 };
36971 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36972     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36973     
36974     onRender: function(ctr, pos)
36975     {
36976         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36977         var size = this.config.initialSize || this.config.width;
36978         if(typeof size != "undefined"){
36979             this.el.setWidth(size);
36980         }
36981     },
36982     
36983     getBox : function(){
36984         if(this.collapsed){
36985             return this.collapsedEl.getBox();
36986         }
36987         var box = this.el.getBox();
36988         if(this.split){
36989             box.width += this.split.el.getWidth();
36990         }
36991         return box;
36992     },
36993     
36994     updateBox : function(box){
36995         if(this.split && !this.collapsed){
36996             var sw = this.split.el.getWidth();
36997             box.width -= sw;
36998             this.split.el.setLeft(box.x+box.width);
36999             this.split.el.setTop(box.y);
37000             this.split.el.setHeight(box.height);
37001         }
37002         if(this.collapsed){
37003             this.updateBody(null, box.height);
37004         }
37005         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37006     }
37007 });
37008 Roo.namespace("Roo.bootstrap.panel");/*
37009  * Based on:
37010  * Ext JS Library 1.1.1
37011  * Copyright(c) 2006-2007, Ext JS, LLC.
37012  *
37013  * Originally Released Under LGPL - original licence link has changed is not relivant.
37014  *
37015  * Fork - LGPL
37016  * <script type="text/javascript">
37017  */
37018 /**
37019  * @class Roo.ContentPanel
37020  * @extends Roo.util.Observable
37021  * A basic ContentPanel element.
37022  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37023  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37024  * @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
37025  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37026  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37027  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37028  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37029  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37030  * @cfg {String} title          The title for this panel
37031  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37032  * @cfg {String} url            Calls {@link #setUrl} with this value
37033  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37034  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37035  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37036  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37037  * @cfg {Boolean} badges render the badges
37038
37039  * @constructor
37040  * Create a new ContentPanel.
37041  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37042  * @param {String/Object} config A string to set only the title or a config object
37043  * @param {String} content (optional) Set the HTML content for this panel
37044  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37045  */
37046 Roo.bootstrap.panel.Content = function( config){
37047     
37048     this.tpl = config.tpl || false;
37049     
37050     var el = config.el;
37051     var content = config.content;
37052
37053     if(config.autoCreate){ // xtype is available if this is called from factory
37054         el = Roo.id();
37055     }
37056     this.el = Roo.get(el);
37057     if(!this.el && config && config.autoCreate){
37058         if(typeof config.autoCreate == "object"){
37059             if(!config.autoCreate.id){
37060                 config.autoCreate.id = config.id||el;
37061             }
37062             this.el = Roo.DomHelper.append(document.body,
37063                         config.autoCreate, true);
37064         }else{
37065             var elcfg =  {   tag: "div",
37066                             cls: "roo-layout-inactive-content",
37067                             id: config.id||el
37068                             };
37069             if (config.html) {
37070                 elcfg.html = config.html;
37071                 
37072             }
37073                         
37074             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37075         }
37076     } 
37077     this.closable = false;
37078     this.loaded = false;
37079     this.active = false;
37080    
37081       
37082     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37083         
37084         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37085         
37086         this.wrapEl = this.el; //this.el.wrap();
37087         var ti = [];
37088         if (config.toolbar.items) {
37089             ti = config.toolbar.items ;
37090             delete config.toolbar.items ;
37091         }
37092         
37093         var nitems = [];
37094         this.toolbar.render(this.wrapEl, 'before');
37095         for(var i =0;i < ti.length;i++) {
37096           //  Roo.log(['add child', items[i]]);
37097             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37098         }
37099         this.toolbar.items = nitems;
37100         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37101         delete config.toolbar;
37102         
37103     }
37104     /*
37105     // xtype created footer. - not sure if will work as we normally have to render first..
37106     if (this.footer && !this.footer.el && this.footer.xtype) {
37107         if (!this.wrapEl) {
37108             this.wrapEl = this.el.wrap();
37109         }
37110     
37111         this.footer.container = this.wrapEl.createChild();
37112          
37113         this.footer = Roo.factory(this.footer, Roo);
37114         
37115     }
37116     */
37117     
37118      if(typeof config == "string"){
37119         this.title = config;
37120     }else{
37121         Roo.apply(this, config);
37122     }
37123     
37124     if(this.resizeEl){
37125         this.resizeEl = Roo.get(this.resizeEl, true);
37126     }else{
37127         this.resizeEl = this.el;
37128     }
37129     // handle view.xtype
37130     
37131  
37132     
37133     
37134     this.addEvents({
37135         /**
37136          * @event activate
37137          * Fires when this panel is activated. 
37138          * @param {Roo.ContentPanel} this
37139          */
37140         "activate" : true,
37141         /**
37142          * @event deactivate
37143          * Fires when this panel is activated. 
37144          * @param {Roo.ContentPanel} this
37145          */
37146         "deactivate" : true,
37147
37148         /**
37149          * @event resize
37150          * Fires when this panel is resized if fitToFrame is true.
37151          * @param {Roo.ContentPanel} this
37152          * @param {Number} width The width after any component adjustments
37153          * @param {Number} height The height after any component adjustments
37154          */
37155         "resize" : true,
37156         
37157          /**
37158          * @event render
37159          * Fires when this tab is created
37160          * @param {Roo.ContentPanel} this
37161          */
37162         "render" : true
37163         
37164         
37165         
37166     });
37167     
37168
37169     
37170     
37171     if(this.autoScroll){
37172         this.resizeEl.setStyle("overflow", "auto");
37173     } else {
37174         // fix randome scrolling
37175         //this.el.on('scroll', function() {
37176         //    Roo.log('fix random scolling');
37177         //    this.scrollTo('top',0); 
37178         //});
37179     }
37180     content = content || this.content;
37181     if(content){
37182         this.setContent(content);
37183     }
37184     if(config && config.url){
37185         this.setUrl(this.url, this.params, this.loadOnce);
37186     }
37187     
37188     
37189     
37190     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37191     
37192     if (this.view && typeof(this.view.xtype) != 'undefined') {
37193         this.view.el = this.el.appendChild(document.createElement("div"));
37194         this.view = Roo.factory(this.view); 
37195         this.view.render  &&  this.view.render(false, '');  
37196     }
37197     
37198     
37199     this.fireEvent('render', this);
37200 };
37201
37202 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37203     
37204     tabTip : '',
37205     
37206     setRegion : function(region){
37207         this.region = region;
37208         this.setActiveClass(region && !this.background);
37209     },
37210     
37211     
37212     setActiveClass: function(state)
37213     {
37214         if(state){
37215            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37216            this.el.setStyle('position','relative');
37217         }else{
37218            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37219            this.el.setStyle('position', 'absolute');
37220         } 
37221     },
37222     
37223     /**
37224      * Returns the toolbar for this Panel if one was configured. 
37225      * @return {Roo.Toolbar} 
37226      */
37227     getToolbar : function(){
37228         return this.toolbar;
37229     },
37230     
37231     setActiveState : function(active)
37232     {
37233         this.active = active;
37234         this.setActiveClass(active);
37235         if(!active){
37236             if(this.fireEvent("deactivate", this) === false){
37237                 return false;
37238             }
37239             return true;
37240         }
37241         this.fireEvent("activate", this);
37242         return true;
37243     },
37244     /**
37245      * Updates this panel's element
37246      * @param {String} content The new content
37247      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37248     */
37249     setContent : function(content, loadScripts){
37250         this.el.update(content, loadScripts);
37251     },
37252
37253     ignoreResize : function(w, h){
37254         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37255             return true;
37256         }else{
37257             this.lastSize = {width: w, height: h};
37258             return false;
37259         }
37260     },
37261     /**
37262      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37263      * @return {Roo.UpdateManager} The UpdateManager
37264      */
37265     getUpdateManager : function(){
37266         return this.el.getUpdateManager();
37267     },
37268      /**
37269      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37270      * @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:
37271 <pre><code>
37272 panel.load({
37273     url: "your-url.php",
37274     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37275     callback: yourFunction,
37276     scope: yourObject, //(optional scope)
37277     discardUrl: false,
37278     nocache: false,
37279     text: "Loading...",
37280     timeout: 30,
37281     scripts: false
37282 });
37283 </code></pre>
37284      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37285      * 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.
37286      * @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}
37287      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37288      * @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.
37289      * @return {Roo.ContentPanel} this
37290      */
37291     load : function(){
37292         var um = this.el.getUpdateManager();
37293         um.update.apply(um, arguments);
37294         return this;
37295     },
37296
37297
37298     /**
37299      * 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.
37300      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37301      * @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)
37302      * @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)
37303      * @return {Roo.UpdateManager} The UpdateManager
37304      */
37305     setUrl : function(url, params, loadOnce){
37306         if(this.refreshDelegate){
37307             this.removeListener("activate", this.refreshDelegate);
37308         }
37309         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37310         this.on("activate", this.refreshDelegate);
37311         return this.el.getUpdateManager();
37312     },
37313     
37314     _handleRefresh : function(url, params, loadOnce){
37315         if(!loadOnce || !this.loaded){
37316             var updater = this.el.getUpdateManager();
37317             updater.update(url, params, this._setLoaded.createDelegate(this));
37318         }
37319     },
37320     
37321     _setLoaded : function(){
37322         this.loaded = true;
37323     }, 
37324     
37325     /**
37326      * Returns this panel's id
37327      * @return {String} 
37328      */
37329     getId : function(){
37330         return this.el.id;
37331     },
37332     
37333     /** 
37334      * Returns this panel's element - used by regiosn to add.
37335      * @return {Roo.Element} 
37336      */
37337     getEl : function(){
37338         return this.wrapEl || this.el;
37339     },
37340     
37341    
37342     
37343     adjustForComponents : function(width, height)
37344     {
37345         //Roo.log('adjustForComponents ');
37346         if(this.resizeEl != this.el){
37347             width -= this.el.getFrameWidth('lr');
37348             height -= this.el.getFrameWidth('tb');
37349         }
37350         if(this.toolbar){
37351             var te = this.toolbar.getEl();
37352             te.setWidth(width);
37353             height -= te.getHeight();
37354         }
37355         if(this.footer){
37356             var te = this.footer.getEl();
37357             te.setWidth(width);
37358             height -= te.getHeight();
37359         }
37360         
37361         
37362         if(this.adjustments){
37363             width += this.adjustments[0];
37364             height += this.adjustments[1];
37365         }
37366         return {"width": width, "height": height};
37367     },
37368     
37369     setSize : function(width, height){
37370         if(this.fitToFrame && !this.ignoreResize(width, height)){
37371             if(this.fitContainer && this.resizeEl != this.el){
37372                 this.el.setSize(width, height);
37373             }
37374             var size = this.adjustForComponents(width, height);
37375             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37376             this.fireEvent('resize', this, size.width, size.height);
37377         }
37378     },
37379     
37380     /**
37381      * Returns this panel's title
37382      * @return {String} 
37383      */
37384     getTitle : function(){
37385         
37386         if (typeof(this.title) != 'object') {
37387             return this.title;
37388         }
37389         
37390         var t = '';
37391         for (var k in this.title) {
37392             if (!this.title.hasOwnProperty(k)) {
37393                 continue;
37394             }
37395             
37396             if (k.indexOf('-') >= 0) {
37397                 var s = k.split('-');
37398                 for (var i = 0; i<s.length; i++) {
37399                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37400                 }
37401             } else {
37402                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37403             }
37404         }
37405         return t;
37406     },
37407     
37408     /**
37409      * Set this panel's title
37410      * @param {String} title
37411      */
37412     setTitle : function(title){
37413         this.title = title;
37414         if(this.region){
37415             this.region.updatePanelTitle(this, title);
37416         }
37417     },
37418     
37419     /**
37420      * Returns true is this panel was configured to be closable
37421      * @return {Boolean} 
37422      */
37423     isClosable : function(){
37424         return this.closable;
37425     },
37426     
37427     beforeSlide : function(){
37428         this.el.clip();
37429         this.resizeEl.clip();
37430     },
37431     
37432     afterSlide : function(){
37433         this.el.unclip();
37434         this.resizeEl.unclip();
37435     },
37436     
37437     /**
37438      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37439      *   Will fail silently if the {@link #setUrl} method has not been called.
37440      *   This does not activate the panel, just updates its content.
37441      */
37442     refresh : function(){
37443         if(this.refreshDelegate){
37444            this.loaded = false;
37445            this.refreshDelegate();
37446         }
37447     },
37448     
37449     /**
37450      * Destroys this panel
37451      */
37452     destroy : function(){
37453         this.el.removeAllListeners();
37454         var tempEl = document.createElement("span");
37455         tempEl.appendChild(this.el.dom);
37456         tempEl.innerHTML = "";
37457         this.el.remove();
37458         this.el = null;
37459     },
37460     
37461     /**
37462      * form - if the content panel contains a form - this is a reference to it.
37463      * @type {Roo.form.Form}
37464      */
37465     form : false,
37466     /**
37467      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37468      *    This contains a reference to it.
37469      * @type {Roo.View}
37470      */
37471     view : false,
37472     
37473       /**
37474      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37475      * <pre><code>
37476
37477 layout.addxtype({
37478        xtype : 'Form',
37479        items: [ .... ]
37480    }
37481 );
37482
37483 </code></pre>
37484      * @param {Object} cfg Xtype definition of item to add.
37485      */
37486     
37487     
37488     getChildContainer: function () {
37489         return this.getEl();
37490     }
37491     
37492     
37493     /*
37494         var  ret = new Roo.factory(cfg);
37495         return ret;
37496         
37497         
37498         // add form..
37499         if (cfg.xtype.match(/^Form$/)) {
37500             
37501             var el;
37502             //if (this.footer) {
37503             //    el = this.footer.container.insertSibling(false, 'before');
37504             //} else {
37505                 el = this.el.createChild();
37506             //}
37507
37508             this.form = new  Roo.form.Form(cfg);
37509             
37510             
37511             if ( this.form.allItems.length) {
37512                 this.form.render(el.dom);
37513             }
37514             return this.form;
37515         }
37516         // should only have one of theses..
37517         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37518             // views.. should not be just added - used named prop 'view''
37519             
37520             cfg.el = this.el.appendChild(document.createElement("div"));
37521             // factory?
37522             
37523             var ret = new Roo.factory(cfg);
37524              
37525              ret.render && ret.render(false, ''); // render blank..
37526             this.view = ret;
37527             return ret;
37528         }
37529         return false;
37530     }
37531     \*/
37532 });
37533  
37534 /**
37535  * @class Roo.bootstrap.panel.Grid
37536  * @extends Roo.bootstrap.panel.Content
37537  * @constructor
37538  * Create a new GridPanel.
37539  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37540  * @param {Object} config A the config object
37541   
37542  */
37543
37544
37545
37546 Roo.bootstrap.panel.Grid = function(config)
37547 {
37548     
37549       
37550     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37551         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37552
37553     config.el = this.wrapper;
37554     //this.el = this.wrapper;
37555     
37556       if (config.container) {
37557         // ctor'ed from a Border/panel.grid
37558         
37559         
37560         this.wrapper.setStyle("overflow", "hidden");
37561         this.wrapper.addClass('roo-grid-container');
37562
37563     }
37564     
37565     
37566     if(config.toolbar){
37567         var tool_el = this.wrapper.createChild();    
37568         this.toolbar = Roo.factory(config.toolbar);
37569         var ti = [];
37570         if (config.toolbar.items) {
37571             ti = config.toolbar.items ;
37572             delete config.toolbar.items ;
37573         }
37574         
37575         var nitems = [];
37576         this.toolbar.render(tool_el);
37577         for(var i =0;i < ti.length;i++) {
37578           //  Roo.log(['add child', items[i]]);
37579             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37580         }
37581         this.toolbar.items = nitems;
37582         
37583         delete config.toolbar;
37584     }
37585     
37586     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37587     config.grid.scrollBody = true;;
37588     config.grid.monitorWindowResize = false; // turn off autosizing
37589     config.grid.autoHeight = false;
37590     config.grid.autoWidth = false;
37591     
37592     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37593     
37594     if (config.background) {
37595         // render grid on panel activation (if panel background)
37596         this.on('activate', function(gp) {
37597             if (!gp.grid.rendered) {
37598                 gp.grid.render(this.wrapper);
37599                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37600             }
37601         });
37602             
37603     } else {
37604         this.grid.render(this.wrapper);
37605         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37606
37607     }
37608     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37609     // ??? needed ??? config.el = this.wrapper;
37610     
37611     
37612     
37613   
37614     // xtype created footer. - not sure if will work as we normally have to render first..
37615     if (this.footer && !this.footer.el && this.footer.xtype) {
37616         
37617         var ctr = this.grid.getView().getFooterPanel(true);
37618         this.footer.dataSource = this.grid.dataSource;
37619         this.footer = Roo.factory(this.footer, Roo);
37620         this.footer.render(ctr);
37621         
37622     }
37623     
37624     
37625     
37626     
37627      
37628 };
37629
37630 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37631     getId : function(){
37632         return this.grid.id;
37633     },
37634     
37635     /**
37636      * Returns the grid for this panel
37637      * @return {Roo.bootstrap.Table} 
37638      */
37639     getGrid : function(){
37640         return this.grid;    
37641     },
37642     
37643     setSize : function(width, height){
37644         if(!this.ignoreResize(width, height)){
37645             var grid = this.grid;
37646             var size = this.adjustForComponents(width, height);
37647             var gridel = grid.getGridEl();
37648             gridel.setSize(size.width, size.height);
37649             /*
37650             var thd = grid.getGridEl().select('thead',true).first();
37651             var tbd = grid.getGridEl().select('tbody', true).first();
37652             if (tbd) {
37653                 tbd.setSize(width, height - thd.getHeight());
37654             }
37655             */
37656             grid.autoSize();
37657         }
37658     },
37659      
37660     
37661     
37662     beforeSlide : function(){
37663         this.grid.getView().scroller.clip();
37664     },
37665     
37666     afterSlide : function(){
37667         this.grid.getView().scroller.unclip();
37668     },
37669     
37670     destroy : function(){
37671         this.grid.destroy();
37672         delete this.grid;
37673         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37674     }
37675 });
37676
37677 /**
37678  * @class Roo.bootstrap.panel.Nest
37679  * @extends Roo.bootstrap.panel.Content
37680  * @constructor
37681  * Create a new Panel, that can contain a layout.Border.
37682  * 
37683  * 
37684  * @param {Roo.BorderLayout} layout The layout for this panel
37685  * @param {String/Object} config A string to set only the title or a config object
37686  */
37687 Roo.bootstrap.panel.Nest = function(config)
37688 {
37689     // construct with only one argument..
37690     /* FIXME - implement nicer consturctors
37691     if (layout.layout) {
37692         config = layout;
37693         layout = config.layout;
37694         delete config.layout;
37695     }
37696     if (layout.xtype && !layout.getEl) {
37697         // then layout needs constructing..
37698         layout = Roo.factory(layout, Roo);
37699     }
37700     */
37701     
37702     config.el =  config.layout.getEl();
37703     
37704     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37705     
37706     config.layout.monitorWindowResize = false; // turn off autosizing
37707     this.layout = config.layout;
37708     this.layout.getEl().addClass("roo-layout-nested-layout");
37709     
37710     
37711     
37712     
37713 };
37714
37715 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37716
37717     setSize : function(width, height){
37718         if(!this.ignoreResize(width, height)){
37719             var size = this.adjustForComponents(width, height);
37720             var el = this.layout.getEl();
37721             if (size.height < 1) {
37722                 el.setWidth(size.width);   
37723             } else {
37724                 el.setSize(size.width, size.height);
37725             }
37726             var touch = el.dom.offsetWidth;
37727             this.layout.layout();
37728             // ie requires a double layout on the first pass
37729             if(Roo.isIE && !this.initialized){
37730                 this.initialized = true;
37731                 this.layout.layout();
37732             }
37733         }
37734     },
37735     
37736     // activate all subpanels if not currently active..
37737     
37738     setActiveState : function(active){
37739         this.active = active;
37740         this.setActiveClass(active);
37741         
37742         if(!active){
37743             this.fireEvent("deactivate", this);
37744             return;
37745         }
37746         
37747         this.fireEvent("activate", this);
37748         // not sure if this should happen before or after..
37749         if (!this.layout) {
37750             return; // should not happen..
37751         }
37752         var reg = false;
37753         for (var r in this.layout.regions) {
37754             reg = this.layout.getRegion(r);
37755             if (reg.getActivePanel()) {
37756                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37757                 reg.setActivePanel(reg.getActivePanel());
37758                 continue;
37759             }
37760             if (!reg.panels.length) {
37761                 continue;
37762             }
37763             reg.showPanel(reg.getPanel(0));
37764         }
37765         
37766         
37767         
37768         
37769     },
37770     
37771     /**
37772      * Returns the nested BorderLayout for this panel
37773      * @return {Roo.BorderLayout} 
37774      */
37775     getLayout : function(){
37776         return this.layout;
37777     },
37778     
37779      /**
37780      * Adds a xtype elements to the layout of the nested panel
37781      * <pre><code>
37782
37783 panel.addxtype({
37784        xtype : 'ContentPanel',
37785        region: 'west',
37786        items: [ .... ]
37787    }
37788 );
37789
37790 panel.addxtype({
37791         xtype : 'NestedLayoutPanel',
37792         region: 'west',
37793         layout: {
37794            center: { },
37795            west: { }   
37796         },
37797         items : [ ... list of content panels or nested layout panels.. ]
37798    }
37799 );
37800 </code></pre>
37801      * @param {Object} cfg Xtype definition of item to add.
37802      */
37803     addxtype : function(cfg) {
37804         return this.layout.addxtype(cfg);
37805     
37806     }
37807 });        /*
37808  * Based on:
37809  * Ext JS Library 1.1.1
37810  * Copyright(c) 2006-2007, Ext JS, LLC.
37811  *
37812  * Originally Released Under LGPL - original licence link has changed is not relivant.
37813  *
37814  * Fork - LGPL
37815  * <script type="text/javascript">
37816  */
37817 /**
37818  * @class Roo.TabPanel
37819  * @extends Roo.util.Observable
37820  * A lightweight tab container.
37821  * <br><br>
37822  * Usage:
37823  * <pre><code>
37824 // basic tabs 1, built from existing content
37825 var tabs = new Roo.TabPanel("tabs1");
37826 tabs.addTab("script", "View Script");
37827 tabs.addTab("markup", "View Markup");
37828 tabs.activate("script");
37829
37830 // more advanced tabs, built from javascript
37831 var jtabs = new Roo.TabPanel("jtabs");
37832 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37833
37834 // set up the UpdateManager
37835 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37836 var updater = tab2.getUpdateManager();
37837 updater.setDefaultUrl("ajax1.htm");
37838 tab2.on('activate', updater.refresh, updater, true);
37839
37840 // Use setUrl for Ajax loading
37841 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37842 tab3.setUrl("ajax2.htm", null, true);
37843
37844 // Disabled tab
37845 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37846 tab4.disable();
37847
37848 jtabs.activate("jtabs-1");
37849  * </code></pre>
37850  * @constructor
37851  * Create a new TabPanel.
37852  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37853  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37854  */
37855 Roo.bootstrap.panel.Tabs = function(config){
37856     /**
37857     * The container element for this TabPanel.
37858     * @type Roo.Element
37859     */
37860     this.el = Roo.get(config.el);
37861     delete config.el;
37862     if(config){
37863         if(typeof config == "boolean"){
37864             this.tabPosition = config ? "bottom" : "top";
37865         }else{
37866             Roo.apply(this, config);
37867         }
37868     }
37869     
37870     if(this.tabPosition == "bottom"){
37871         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37872         this.el.addClass("roo-tabs-bottom");
37873     }
37874     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37875     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37876     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37877     if(Roo.isIE){
37878         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37879     }
37880     if(this.tabPosition != "bottom"){
37881         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37882          * @type Roo.Element
37883          */
37884         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37885         this.el.addClass("roo-tabs-top");
37886     }
37887     this.items = [];
37888
37889     this.bodyEl.setStyle("position", "relative");
37890
37891     this.active = null;
37892     this.activateDelegate = this.activate.createDelegate(this);
37893
37894     this.addEvents({
37895         /**
37896          * @event tabchange
37897          * Fires when the active tab changes
37898          * @param {Roo.TabPanel} this
37899          * @param {Roo.TabPanelItem} activePanel The new active tab
37900          */
37901         "tabchange": true,
37902         /**
37903          * @event beforetabchange
37904          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37905          * @param {Roo.TabPanel} this
37906          * @param {Object} e Set cancel to true on this object to cancel the tab change
37907          * @param {Roo.TabPanelItem} tab The tab being changed to
37908          */
37909         "beforetabchange" : true
37910     });
37911
37912     Roo.EventManager.onWindowResize(this.onResize, this);
37913     this.cpad = this.el.getPadding("lr");
37914     this.hiddenCount = 0;
37915
37916
37917     // toolbar on the tabbar support...
37918     if (this.toolbar) {
37919         alert("no toolbar support yet");
37920         this.toolbar  = false;
37921         /*
37922         var tcfg = this.toolbar;
37923         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37924         this.toolbar = new Roo.Toolbar(tcfg);
37925         if (Roo.isSafari) {
37926             var tbl = tcfg.container.child('table', true);
37927             tbl.setAttribute('width', '100%');
37928         }
37929         */
37930         
37931     }
37932    
37933
37934
37935     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37936 };
37937
37938 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37939     /*
37940      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37941      */
37942     tabPosition : "top",
37943     /*
37944      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37945      */
37946     currentTabWidth : 0,
37947     /*
37948      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37949      */
37950     minTabWidth : 40,
37951     /*
37952      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37953      */
37954     maxTabWidth : 250,
37955     /*
37956      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37957      */
37958     preferredTabWidth : 175,
37959     /*
37960      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37961      */
37962     resizeTabs : false,
37963     /*
37964      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37965      */
37966     monitorResize : true,
37967     /*
37968      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37969      */
37970     toolbar : false,
37971
37972     /**
37973      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37974      * @param {String} id The id of the div to use <b>or create</b>
37975      * @param {String} text The text for the tab
37976      * @param {String} content (optional) Content to put in the TabPanelItem body
37977      * @param {Boolean} closable (optional) True to create a close icon on the tab
37978      * @return {Roo.TabPanelItem} The created TabPanelItem
37979      */
37980     addTab : function(id, text, content, closable, tpl)
37981     {
37982         var item = new Roo.bootstrap.panel.TabItem({
37983             panel: this,
37984             id : id,
37985             text : text,
37986             closable : closable,
37987             tpl : tpl
37988         });
37989         this.addTabItem(item);
37990         if(content){
37991             item.setContent(content);
37992         }
37993         return item;
37994     },
37995
37996     /**
37997      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37998      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37999      * @return {Roo.TabPanelItem}
38000      */
38001     getTab : function(id){
38002         return this.items[id];
38003     },
38004
38005     /**
38006      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38007      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38008      */
38009     hideTab : function(id){
38010         var t = this.items[id];
38011         if(!t.isHidden()){
38012            t.setHidden(true);
38013            this.hiddenCount++;
38014            this.autoSizeTabs();
38015         }
38016     },
38017
38018     /**
38019      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38020      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38021      */
38022     unhideTab : function(id){
38023         var t = this.items[id];
38024         if(t.isHidden()){
38025            t.setHidden(false);
38026            this.hiddenCount--;
38027            this.autoSizeTabs();
38028         }
38029     },
38030
38031     /**
38032      * Adds an existing {@link Roo.TabPanelItem}.
38033      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38034      */
38035     addTabItem : function(item){
38036         this.items[item.id] = item;
38037         this.items.push(item);
38038       //  if(this.resizeTabs){
38039     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38040   //         this.autoSizeTabs();
38041 //        }else{
38042 //            item.autoSize();
38043        // }
38044     },
38045
38046     /**
38047      * Removes a {@link Roo.TabPanelItem}.
38048      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38049      */
38050     removeTab : function(id){
38051         var items = this.items;
38052         var tab = items[id];
38053         if(!tab) { return; }
38054         var index = items.indexOf(tab);
38055         if(this.active == tab && items.length > 1){
38056             var newTab = this.getNextAvailable(index);
38057             if(newTab) {
38058                 newTab.activate();
38059             }
38060         }
38061         this.stripEl.dom.removeChild(tab.pnode.dom);
38062         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38063             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38064         }
38065         items.splice(index, 1);
38066         delete this.items[tab.id];
38067         tab.fireEvent("close", tab);
38068         tab.purgeListeners();
38069         this.autoSizeTabs();
38070     },
38071
38072     getNextAvailable : function(start){
38073         var items = this.items;
38074         var index = start;
38075         // look for a next tab that will slide over to
38076         // replace the one being removed
38077         while(index < items.length){
38078             var item = items[++index];
38079             if(item && !item.isHidden()){
38080                 return item;
38081             }
38082         }
38083         // if one isn't found select the previous tab (on the left)
38084         index = start;
38085         while(index >= 0){
38086             var item = items[--index];
38087             if(item && !item.isHidden()){
38088                 return item;
38089             }
38090         }
38091         return null;
38092     },
38093
38094     /**
38095      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38096      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38097      */
38098     disableTab : function(id){
38099         var tab = this.items[id];
38100         if(tab && this.active != tab){
38101             tab.disable();
38102         }
38103     },
38104
38105     /**
38106      * Enables a {@link Roo.TabPanelItem} that is disabled.
38107      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38108      */
38109     enableTab : function(id){
38110         var tab = this.items[id];
38111         tab.enable();
38112     },
38113
38114     /**
38115      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38116      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38117      * @return {Roo.TabPanelItem} The TabPanelItem.
38118      */
38119     activate : function(id){
38120         var tab = this.items[id];
38121         if(!tab){
38122             return null;
38123         }
38124         if(tab == this.active || tab.disabled){
38125             return tab;
38126         }
38127         var e = {};
38128         this.fireEvent("beforetabchange", this, e, tab);
38129         if(e.cancel !== true && !tab.disabled){
38130             if(this.active){
38131                 this.active.hide();
38132             }
38133             this.active = this.items[id];
38134             this.active.show();
38135             this.fireEvent("tabchange", this, this.active);
38136         }
38137         return tab;
38138     },
38139
38140     /**
38141      * Gets the active {@link Roo.TabPanelItem}.
38142      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38143      */
38144     getActiveTab : function(){
38145         return this.active;
38146     },
38147
38148     /**
38149      * Updates the tab body element to fit the height of the container element
38150      * for overflow scrolling
38151      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38152      */
38153     syncHeight : function(targetHeight){
38154         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38155         var bm = this.bodyEl.getMargins();
38156         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38157         this.bodyEl.setHeight(newHeight);
38158         return newHeight;
38159     },
38160
38161     onResize : function(){
38162         if(this.monitorResize){
38163             this.autoSizeTabs();
38164         }
38165     },
38166
38167     /**
38168      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38169      */
38170     beginUpdate : function(){
38171         this.updating = true;
38172     },
38173
38174     /**
38175      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38176      */
38177     endUpdate : function(){
38178         this.updating = false;
38179         this.autoSizeTabs();
38180     },
38181
38182     /**
38183      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38184      */
38185     autoSizeTabs : function(){
38186         var count = this.items.length;
38187         var vcount = count - this.hiddenCount;
38188         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38189             return;
38190         }
38191         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38192         var availWidth = Math.floor(w / vcount);
38193         var b = this.stripBody;
38194         if(b.getWidth() > w){
38195             var tabs = this.items;
38196             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38197             if(availWidth < this.minTabWidth){
38198                 /*if(!this.sleft){    // incomplete scrolling code
38199                     this.createScrollButtons();
38200                 }
38201                 this.showScroll();
38202                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38203             }
38204         }else{
38205             if(this.currentTabWidth < this.preferredTabWidth){
38206                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38207             }
38208         }
38209     },
38210
38211     /**
38212      * Returns the number of tabs in this TabPanel.
38213      * @return {Number}
38214      */
38215      getCount : function(){
38216          return this.items.length;
38217      },
38218
38219     /**
38220      * Resizes all the tabs to the passed width
38221      * @param {Number} The new width
38222      */
38223     setTabWidth : function(width){
38224         this.currentTabWidth = width;
38225         for(var i = 0, len = this.items.length; i < len; i++) {
38226                 if(!this.items[i].isHidden()) {
38227                 this.items[i].setWidth(width);
38228             }
38229         }
38230     },
38231
38232     /**
38233      * Destroys this TabPanel
38234      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38235      */
38236     destroy : function(removeEl){
38237         Roo.EventManager.removeResizeListener(this.onResize, this);
38238         for(var i = 0, len = this.items.length; i < len; i++){
38239             this.items[i].purgeListeners();
38240         }
38241         if(removeEl === true){
38242             this.el.update("");
38243             this.el.remove();
38244         }
38245     },
38246     
38247     createStrip : function(container)
38248     {
38249         var strip = document.createElement("nav");
38250         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38251         container.appendChild(strip);
38252         return strip;
38253     },
38254     
38255     createStripList : function(strip)
38256     {
38257         // div wrapper for retard IE
38258         // returns the "tr" element.
38259         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38260         //'<div class="x-tabs-strip-wrap">'+
38261           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38262           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38263         return strip.firstChild; //.firstChild.firstChild.firstChild;
38264     },
38265     createBody : function(container)
38266     {
38267         var body = document.createElement("div");
38268         Roo.id(body, "tab-body");
38269         //Roo.fly(body).addClass("x-tabs-body");
38270         Roo.fly(body).addClass("tab-content");
38271         container.appendChild(body);
38272         return body;
38273     },
38274     createItemBody :function(bodyEl, id){
38275         var body = Roo.getDom(id);
38276         if(!body){
38277             body = document.createElement("div");
38278             body.id = id;
38279         }
38280         //Roo.fly(body).addClass("x-tabs-item-body");
38281         Roo.fly(body).addClass("tab-pane");
38282          bodyEl.insertBefore(body, bodyEl.firstChild);
38283         return body;
38284     },
38285     /** @private */
38286     createStripElements :  function(stripEl, text, closable, tpl)
38287     {
38288         var td = document.createElement("li"); // was td..
38289         
38290         
38291         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38292         
38293         
38294         stripEl.appendChild(td);
38295         /*if(closable){
38296             td.className = "x-tabs-closable";
38297             if(!this.closeTpl){
38298                 this.closeTpl = new Roo.Template(
38299                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38300                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38301                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38302                 );
38303             }
38304             var el = this.closeTpl.overwrite(td, {"text": text});
38305             var close = el.getElementsByTagName("div")[0];
38306             var inner = el.getElementsByTagName("em")[0];
38307             return {"el": el, "close": close, "inner": inner};
38308         } else {
38309         */
38310         // not sure what this is..
38311 //            if(!this.tabTpl){
38312                 //this.tabTpl = new Roo.Template(
38313                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38314                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38315                 //);
38316 //                this.tabTpl = new Roo.Template(
38317 //                   '<a href="#">' +
38318 //                   '<span unselectable="on"' +
38319 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38320 //                            ' >{text}</span></a>'
38321 //                );
38322 //                
38323 //            }
38324
38325
38326             var template = tpl || this.tabTpl || false;
38327             
38328             if(!template){
38329                 
38330                 template = new Roo.Template(
38331                    '<a href="#">' +
38332                    '<span unselectable="on"' +
38333                             (this.disableTooltips ? '' : ' title="{text}"') +
38334                             ' >{text}</span></a>'
38335                 );
38336             }
38337             
38338             switch (typeof(template)) {
38339                 case 'object' :
38340                     break;
38341                 case 'string' :
38342                     template = new Roo.Template(template);
38343                     break;
38344                 default :
38345                     break;
38346             }
38347             
38348             var el = template.overwrite(td, {"text": text});
38349             
38350             var inner = el.getElementsByTagName("span")[0];
38351             
38352             return {"el": el, "inner": inner};
38353             
38354     }
38355         
38356     
38357 });
38358
38359 /**
38360  * @class Roo.TabPanelItem
38361  * @extends Roo.util.Observable
38362  * Represents an individual item (tab plus body) in a TabPanel.
38363  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38364  * @param {String} id The id of this TabPanelItem
38365  * @param {String} text The text for the tab of this TabPanelItem
38366  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38367  */
38368 Roo.bootstrap.panel.TabItem = function(config){
38369     /**
38370      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38371      * @type Roo.TabPanel
38372      */
38373     this.tabPanel = config.panel;
38374     /**
38375      * The id for this TabPanelItem
38376      * @type String
38377      */
38378     this.id = config.id;
38379     /** @private */
38380     this.disabled = false;
38381     /** @private */
38382     this.text = config.text;
38383     /** @private */
38384     this.loaded = false;
38385     this.closable = config.closable;
38386
38387     /**
38388      * The body element for this TabPanelItem.
38389      * @type Roo.Element
38390      */
38391     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38392     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38393     this.bodyEl.setStyle("display", "block");
38394     this.bodyEl.setStyle("zoom", "1");
38395     //this.hideAction();
38396
38397     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38398     /** @private */
38399     this.el = Roo.get(els.el);
38400     this.inner = Roo.get(els.inner, true);
38401     this.textEl = Roo.get(this.el.dom.firstChild, true);
38402     this.pnode = Roo.get(els.el.parentNode, true);
38403 //    this.el.on("mousedown", this.onTabMouseDown, this);
38404     this.el.on("click", this.onTabClick, this);
38405     /** @private */
38406     if(config.closable){
38407         var c = Roo.get(els.close, true);
38408         c.dom.title = this.closeText;
38409         c.addClassOnOver("close-over");
38410         c.on("click", this.closeClick, this);
38411      }
38412
38413     this.addEvents({
38414          /**
38415          * @event activate
38416          * Fires when this tab becomes the active tab.
38417          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38418          * @param {Roo.TabPanelItem} this
38419          */
38420         "activate": true,
38421         /**
38422          * @event beforeclose
38423          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38424          * @param {Roo.TabPanelItem} this
38425          * @param {Object} e Set cancel to true on this object to cancel the close.
38426          */
38427         "beforeclose": true,
38428         /**
38429          * @event close
38430          * Fires when this tab is closed.
38431          * @param {Roo.TabPanelItem} this
38432          */
38433          "close": true,
38434         /**
38435          * @event deactivate
38436          * Fires when this tab is no longer the active tab.
38437          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38438          * @param {Roo.TabPanelItem} this
38439          */
38440          "deactivate" : true
38441     });
38442     this.hidden = false;
38443
38444     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38445 };
38446
38447 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38448            {
38449     purgeListeners : function(){
38450        Roo.util.Observable.prototype.purgeListeners.call(this);
38451        this.el.removeAllListeners();
38452     },
38453     /**
38454      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38455      */
38456     show : function(){
38457         this.pnode.addClass("active");
38458         this.showAction();
38459         if(Roo.isOpera){
38460             this.tabPanel.stripWrap.repaint();
38461         }
38462         this.fireEvent("activate", this.tabPanel, this);
38463     },
38464
38465     /**
38466      * Returns true if this tab is the active tab.
38467      * @return {Boolean}
38468      */
38469     isActive : function(){
38470         return this.tabPanel.getActiveTab() == this;
38471     },
38472
38473     /**
38474      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38475      */
38476     hide : function(){
38477         this.pnode.removeClass("active");
38478         this.hideAction();
38479         this.fireEvent("deactivate", this.tabPanel, this);
38480     },
38481
38482     hideAction : function(){
38483         this.bodyEl.hide();
38484         this.bodyEl.setStyle("position", "absolute");
38485         this.bodyEl.setLeft("-20000px");
38486         this.bodyEl.setTop("-20000px");
38487     },
38488
38489     showAction : function(){
38490         this.bodyEl.setStyle("position", "relative");
38491         this.bodyEl.setTop("");
38492         this.bodyEl.setLeft("");
38493         this.bodyEl.show();
38494     },
38495
38496     /**
38497      * Set the tooltip for the tab.
38498      * @param {String} tooltip The tab's tooltip
38499      */
38500     setTooltip : function(text){
38501         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38502             this.textEl.dom.qtip = text;
38503             this.textEl.dom.removeAttribute('title');
38504         }else{
38505             this.textEl.dom.title = text;
38506         }
38507     },
38508
38509     onTabClick : function(e){
38510         e.preventDefault();
38511         this.tabPanel.activate(this.id);
38512     },
38513
38514     onTabMouseDown : function(e){
38515         e.preventDefault();
38516         this.tabPanel.activate(this.id);
38517     },
38518 /*
38519     getWidth : function(){
38520         return this.inner.getWidth();
38521     },
38522
38523     setWidth : function(width){
38524         var iwidth = width - this.pnode.getPadding("lr");
38525         this.inner.setWidth(iwidth);
38526         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38527         this.pnode.setWidth(width);
38528     },
38529 */
38530     /**
38531      * Show or hide the tab
38532      * @param {Boolean} hidden True to hide or false to show.
38533      */
38534     setHidden : function(hidden){
38535         this.hidden = hidden;
38536         this.pnode.setStyle("display", hidden ? "none" : "");
38537     },
38538
38539     /**
38540      * Returns true if this tab is "hidden"
38541      * @return {Boolean}
38542      */
38543     isHidden : function(){
38544         return this.hidden;
38545     },
38546
38547     /**
38548      * Returns the text for this tab
38549      * @return {String}
38550      */
38551     getText : function(){
38552         return this.text;
38553     },
38554     /*
38555     autoSize : function(){
38556         //this.el.beginMeasure();
38557         this.textEl.setWidth(1);
38558         /*
38559          *  #2804 [new] Tabs in Roojs
38560          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38561          */
38562         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38563         //this.el.endMeasure();
38564     //},
38565
38566     /**
38567      * Sets the text for the tab (Note: this also sets the tooltip text)
38568      * @param {String} text The tab's text and tooltip
38569      */
38570     setText : function(text){
38571         this.text = text;
38572         this.textEl.update(text);
38573         this.setTooltip(text);
38574         //if(!this.tabPanel.resizeTabs){
38575         //    this.autoSize();
38576         //}
38577     },
38578     /**
38579      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38580      */
38581     activate : function(){
38582         this.tabPanel.activate(this.id);
38583     },
38584
38585     /**
38586      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38587      */
38588     disable : function(){
38589         if(this.tabPanel.active != this){
38590             this.disabled = true;
38591             this.pnode.addClass("disabled");
38592         }
38593     },
38594
38595     /**
38596      * Enables this TabPanelItem if it was previously disabled.
38597      */
38598     enable : function(){
38599         this.disabled = false;
38600         this.pnode.removeClass("disabled");
38601     },
38602
38603     /**
38604      * Sets the content for this TabPanelItem.
38605      * @param {String} content The content
38606      * @param {Boolean} loadScripts true to look for and load scripts
38607      */
38608     setContent : function(content, loadScripts){
38609         this.bodyEl.update(content, loadScripts);
38610     },
38611
38612     /**
38613      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38614      * @return {Roo.UpdateManager} The UpdateManager
38615      */
38616     getUpdateManager : function(){
38617         return this.bodyEl.getUpdateManager();
38618     },
38619
38620     /**
38621      * Set a URL to be used to load the content for this TabPanelItem.
38622      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38623      * @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)
38624      * @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)
38625      * @return {Roo.UpdateManager} The UpdateManager
38626      */
38627     setUrl : function(url, params, loadOnce){
38628         if(this.refreshDelegate){
38629             this.un('activate', this.refreshDelegate);
38630         }
38631         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38632         this.on("activate", this.refreshDelegate);
38633         return this.bodyEl.getUpdateManager();
38634     },
38635
38636     /** @private */
38637     _handleRefresh : function(url, params, loadOnce){
38638         if(!loadOnce || !this.loaded){
38639             var updater = this.bodyEl.getUpdateManager();
38640             updater.update(url, params, this._setLoaded.createDelegate(this));
38641         }
38642     },
38643
38644     /**
38645      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38646      *   Will fail silently if the setUrl method has not been called.
38647      *   This does not activate the panel, just updates its content.
38648      */
38649     refresh : function(){
38650         if(this.refreshDelegate){
38651            this.loaded = false;
38652            this.refreshDelegate();
38653         }
38654     },
38655
38656     /** @private */
38657     _setLoaded : function(){
38658         this.loaded = true;
38659     },
38660
38661     /** @private */
38662     closeClick : function(e){
38663         var o = {};
38664         e.stopEvent();
38665         this.fireEvent("beforeclose", this, o);
38666         if(o.cancel !== true){
38667             this.tabPanel.removeTab(this.id);
38668         }
38669     },
38670     /**
38671      * The text displayed in the tooltip for the close icon.
38672      * @type String
38673      */
38674     closeText : "Close this tab"
38675 });
38676 /**
38677 *    This script refer to:
38678 *    Title: International Telephone Input
38679 *    Author: Jack O'Connor
38680 *    Code version:  v12.1.12
38681 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38682 **/
38683
38684 Roo.bootstrap.PhoneInputData = function() {
38685     var d = [
38686       [
38687         "Afghanistan (‫افغانستان‬‎)",
38688         "af",
38689         "93"
38690       ],
38691       [
38692         "Albania (Shqipëri)",
38693         "al",
38694         "355"
38695       ],
38696       [
38697         "Algeria (‫الجزائر‬‎)",
38698         "dz",
38699         "213"
38700       ],
38701       [
38702         "American Samoa",
38703         "as",
38704         "1684"
38705       ],
38706       [
38707         "Andorra",
38708         "ad",
38709         "376"
38710       ],
38711       [
38712         "Angola",
38713         "ao",
38714         "244"
38715       ],
38716       [
38717         "Anguilla",
38718         "ai",
38719         "1264"
38720       ],
38721       [
38722         "Antigua and Barbuda",
38723         "ag",
38724         "1268"
38725       ],
38726       [
38727         "Argentina",
38728         "ar",
38729         "54"
38730       ],
38731       [
38732         "Armenia (Հայաստան)",
38733         "am",
38734         "374"
38735       ],
38736       [
38737         "Aruba",
38738         "aw",
38739         "297"
38740       ],
38741       [
38742         "Australia",
38743         "au",
38744         "61",
38745         0
38746       ],
38747       [
38748         "Austria (Österreich)",
38749         "at",
38750         "43"
38751       ],
38752       [
38753         "Azerbaijan (Azərbaycan)",
38754         "az",
38755         "994"
38756       ],
38757       [
38758         "Bahamas",
38759         "bs",
38760         "1242"
38761       ],
38762       [
38763         "Bahrain (‫البحرين‬‎)",
38764         "bh",
38765         "973"
38766       ],
38767       [
38768         "Bangladesh (বাংলাদেশ)",
38769         "bd",
38770         "880"
38771       ],
38772       [
38773         "Barbados",
38774         "bb",
38775         "1246"
38776       ],
38777       [
38778         "Belarus (Беларусь)",
38779         "by",
38780         "375"
38781       ],
38782       [
38783         "Belgium (België)",
38784         "be",
38785         "32"
38786       ],
38787       [
38788         "Belize",
38789         "bz",
38790         "501"
38791       ],
38792       [
38793         "Benin (Bénin)",
38794         "bj",
38795         "229"
38796       ],
38797       [
38798         "Bermuda",
38799         "bm",
38800         "1441"
38801       ],
38802       [
38803         "Bhutan (འབྲུག)",
38804         "bt",
38805         "975"
38806       ],
38807       [
38808         "Bolivia",
38809         "bo",
38810         "591"
38811       ],
38812       [
38813         "Bosnia and Herzegovina (Босна и Херцеговина)",
38814         "ba",
38815         "387"
38816       ],
38817       [
38818         "Botswana",
38819         "bw",
38820         "267"
38821       ],
38822       [
38823         "Brazil (Brasil)",
38824         "br",
38825         "55"
38826       ],
38827       [
38828         "British Indian Ocean Territory",
38829         "io",
38830         "246"
38831       ],
38832       [
38833         "British Virgin Islands",
38834         "vg",
38835         "1284"
38836       ],
38837       [
38838         "Brunei",
38839         "bn",
38840         "673"
38841       ],
38842       [
38843         "Bulgaria (България)",
38844         "bg",
38845         "359"
38846       ],
38847       [
38848         "Burkina Faso",
38849         "bf",
38850         "226"
38851       ],
38852       [
38853         "Burundi (Uburundi)",
38854         "bi",
38855         "257"
38856       ],
38857       [
38858         "Cambodia (កម្ពុជា)",
38859         "kh",
38860         "855"
38861       ],
38862       [
38863         "Cameroon (Cameroun)",
38864         "cm",
38865         "237"
38866       ],
38867       [
38868         "Canada",
38869         "ca",
38870         "1",
38871         1,
38872         ["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"]
38873       ],
38874       [
38875         "Cape Verde (Kabu Verdi)",
38876         "cv",
38877         "238"
38878       ],
38879       [
38880         "Caribbean Netherlands",
38881         "bq",
38882         "599",
38883         1
38884       ],
38885       [
38886         "Cayman Islands",
38887         "ky",
38888         "1345"
38889       ],
38890       [
38891         "Central African Republic (République centrafricaine)",
38892         "cf",
38893         "236"
38894       ],
38895       [
38896         "Chad (Tchad)",
38897         "td",
38898         "235"
38899       ],
38900       [
38901         "Chile",
38902         "cl",
38903         "56"
38904       ],
38905       [
38906         "China (中国)",
38907         "cn",
38908         "86"
38909       ],
38910       [
38911         "Christmas Island",
38912         "cx",
38913         "61",
38914         2
38915       ],
38916       [
38917         "Cocos (Keeling) Islands",
38918         "cc",
38919         "61",
38920         1
38921       ],
38922       [
38923         "Colombia",
38924         "co",
38925         "57"
38926       ],
38927       [
38928         "Comoros (‫جزر القمر‬‎)",
38929         "km",
38930         "269"
38931       ],
38932       [
38933         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38934         "cd",
38935         "243"
38936       ],
38937       [
38938         "Congo (Republic) (Congo-Brazzaville)",
38939         "cg",
38940         "242"
38941       ],
38942       [
38943         "Cook Islands",
38944         "ck",
38945         "682"
38946       ],
38947       [
38948         "Costa Rica",
38949         "cr",
38950         "506"
38951       ],
38952       [
38953         "Côte d’Ivoire",
38954         "ci",
38955         "225"
38956       ],
38957       [
38958         "Croatia (Hrvatska)",
38959         "hr",
38960         "385"
38961       ],
38962       [
38963         "Cuba",
38964         "cu",
38965         "53"
38966       ],
38967       [
38968         "Curaçao",
38969         "cw",
38970         "599",
38971         0
38972       ],
38973       [
38974         "Cyprus (Κύπρος)",
38975         "cy",
38976         "357"
38977       ],
38978       [
38979         "Czech Republic (Česká republika)",
38980         "cz",
38981         "420"
38982       ],
38983       [
38984         "Denmark (Danmark)",
38985         "dk",
38986         "45"
38987       ],
38988       [
38989         "Djibouti",
38990         "dj",
38991         "253"
38992       ],
38993       [
38994         "Dominica",
38995         "dm",
38996         "1767"
38997       ],
38998       [
38999         "Dominican Republic (República Dominicana)",
39000         "do",
39001         "1",
39002         2,
39003         ["809", "829", "849"]
39004       ],
39005       [
39006         "Ecuador",
39007         "ec",
39008         "593"
39009       ],
39010       [
39011         "Egypt (‫مصر‬‎)",
39012         "eg",
39013         "20"
39014       ],
39015       [
39016         "El Salvador",
39017         "sv",
39018         "503"
39019       ],
39020       [
39021         "Equatorial Guinea (Guinea Ecuatorial)",
39022         "gq",
39023         "240"
39024       ],
39025       [
39026         "Eritrea",
39027         "er",
39028         "291"
39029       ],
39030       [
39031         "Estonia (Eesti)",
39032         "ee",
39033         "372"
39034       ],
39035       [
39036         "Ethiopia",
39037         "et",
39038         "251"
39039       ],
39040       [
39041         "Falkland Islands (Islas Malvinas)",
39042         "fk",
39043         "500"
39044       ],
39045       [
39046         "Faroe Islands (Føroyar)",
39047         "fo",
39048         "298"
39049       ],
39050       [
39051         "Fiji",
39052         "fj",
39053         "679"
39054       ],
39055       [
39056         "Finland (Suomi)",
39057         "fi",
39058         "358",
39059         0
39060       ],
39061       [
39062         "France",
39063         "fr",
39064         "33"
39065       ],
39066       [
39067         "French Guiana (Guyane française)",
39068         "gf",
39069         "594"
39070       ],
39071       [
39072         "French Polynesia (Polynésie française)",
39073         "pf",
39074         "689"
39075       ],
39076       [
39077         "Gabon",
39078         "ga",
39079         "241"
39080       ],
39081       [
39082         "Gambia",
39083         "gm",
39084         "220"
39085       ],
39086       [
39087         "Georgia (საქართველო)",
39088         "ge",
39089         "995"
39090       ],
39091       [
39092         "Germany (Deutschland)",
39093         "de",
39094         "49"
39095       ],
39096       [
39097         "Ghana (Gaana)",
39098         "gh",
39099         "233"
39100       ],
39101       [
39102         "Gibraltar",
39103         "gi",
39104         "350"
39105       ],
39106       [
39107         "Greece (Ελλάδα)",
39108         "gr",
39109         "30"
39110       ],
39111       [
39112         "Greenland (Kalaallit Nunaat)",
39113         "gl",
39114         "299"
39115       ],
39116       [
39117         "Grenada",
39118         "gd",
39119         "1473"
39120       ],
39121       [
39122         "Guadeloupe",
39123         "gp",
39124         "590",
39125         0
39126       ],
39127       [
39128         "Guam",
39129         "gu",
39130         "1671"
39131       ],
39132       [
39133         "Guatemala",
39134         "gt",
39135         "502"
39136       ],
39137       [
39138         "Guernsey",
39139         "gg",
39140         "44",
39141         1
39142       ],
39143       [
39144         "Guinea (Guinée)",
39145         "gn",
39146         "224"
39147       ],
39148       [
39149         "Guinea-Bissau (Guiné Bissau)",
39150         "gw",
39151         "245"
39152       ],
39153       [
39154         "Guyana",
39155         "gy",
39156         "592"
39157       ],
39158       [
39159         "Haiti",
39160         "ht",
39161         "509"
39162       ],
39163       [
39164         "Honduras",
39165         "hn",
39166         "504"
39167       ],
39168       [
39169         "Hong Kong (香港)",
39170         "hk",
39171         "852"
39172       ],
39173       [
39174         "Hungary (Magyarország)",
39175         "hu",
39176         "36"
39177       ],
39178       [
39179         "Iceland (Ísland)",
39180         "is",
39181         "354"
39182       ],
39183       [
39184         "India (भारत)",
39185         "in",
39186         "91"
39187       ],
39188       [
39189         "Indonesia",
39190         "id",
39191         "62"
39192       ],
39193       [
39194         "Iran (‫ایران‬‎)",
39195         "ir",
39196         "98"
39197       ],
39198       [
39199         "Iraq (‫العراق‬‎)",
39200         "iq",
39201         "964"
39202       ],
39203       [
39204         "Ireland",
39205         "ie",
39206         "353"
39207       ],
39208       [
39209         "Isle of Man",
39210         "im",
39211         "44",
39212         2
39213       ],
39214       [
39215         "Israel (‫ישראל‬‎)",
39216         "il",
39217         "972"
39218       ],
39219       [
39220         "Italy (Italia)",
39221         "it",
39222         "39",
39223         0
39224       ],
39225       [
39226         "Jamaica",
39227         "jm",
39228         "1876"
39229       ],
39230       [
39231         "Japan (日本)",
39232         "jp",
39233         "81"
39234       ],
39235       [
39236         "Jersey",
39237         "je",
39238         "44",
39239         3
39240       ],
39241       [
39242         "Jordan (‫الأردن‬‎)",
39243         "jo",
39244         "962"
39245       ],
39246       [
39247         "Kazakhstan (Казахстан)",
39248         "kz",
39249         "7",
39250         1
39251       ],
39252       [
39253         "Kenya",
39254         "ke",
39255         "254"
39256       ],
39257       [
39258         "Kiribati",
39259         "ki",
39260         "686"
39261       ],
39262       [
39263         "Kosovo",
39264         "xk",
39265         "383"
39266       ],
39267       [
39268         "Kuwait (‫الكويت‬‎)",
39269         "kw",
39270         "965"
39271       ],
39272       [
39273         "Kyrgyzstan (Кыргызстан)",
39274         "kg",
39275         "996"
39276       ],
39277       [
39278         "Laos (ລາວ)",
39279         "la",
39280         "856"
39281       ],
39282       [
39283         "Latvia (Latvija)",
39284         "lv",
39285         "371"
39286       ],
39287       [
39288         "Lebanon (‫لبنان‬‎)",
39289         "lb",
39290         "961"
39291       ],
39292       [
39293         "Lesotho",
39294         "ls",
39295         "266"
39296       ],
39297       [
39298         "Liberia",
39299         "lr",
39300         "231"
39301       ],
39302       [
39303         "Libya (‫ليبيا‬‎)",
39304         "ly",
39305         "218"
39306       ],
39307       [
39308         "Liechtenstein",
39309         "li",
39310         "423"
39311       ],
39312       [
39313         "Lithuania (Lietuva)",
39314         "lt",
39315         "370"
39316       ],
39317       [
39318         "Luxembourg",
39319         "lu",
39320         "352"
39321       ],
39322       [
39323         "Macau (澳門)",
39324         "mo",
39325         "853"
39326       ],
39327       [
39328         "Macedonia (FYROM) (Македонија)",
39329         "mk",
39330         "389"
39331       ],
39332       [
39333         "Madagascar (Madagasikara)",
39334         "mg",
39335         "261"
39336       ],
39337       [
39338         "Malawi",
39339         "mw",
39340         "265"
39341       ],
39342       [
39343         "Malaysia",
39344         "my",
39345         "60"
39346       ],
39347       [
39348         "Maldives",
39349         "mv",
39350         "960"
39351       ],
39352       [
39353         "Mali",
39354         "ml",
39355         "223"
39356       ],
39357       [
39358         "Malta",
39359         "mt",
39360         "356"
39361       ],
39362       [
39363         "Marshall Islands",
39364         "mh",
39365         "692"
39366       ],
39367       [
39368         "Martinique",
39369         "mq",
39370         "596"
39371       ],
39372       [
39373         "Mauritania (‫موريتانيا‬‎)",
39374         "mr",
39375         "222"
39376       ],
39377       [
39378         "Mauritius (Moris)",
39379         "mu",
39380         "230"
39381       ],
39382       [
39383         "Mayotte",
39384         "yt",
39385         "262",
39386         1
39387       ],
39388       [
39389         "Mexico (México)",
39390         "mx",
39391         "52"
39392       ],
39393       [
39394         "Micronesia",
39395         "fm",
39396         "691"
39397       ],
39398       [
39399         "Moldova (Republica Moldova)",
39400         "md",
39401         "373"
39402       ],
39403       [
39404         "Monaco",
39405         "mc",
39406         "377"
39407       ],
39408       [
39409         "Mongolia (Монгол)",
39410         "mn",
39411         "976"
39412       ],
39413       [
39414         "Montenegro (Crna Gora)",
39415         "me",
39416         "382"
39417       ],
39418       [
39419         "Montserrat",
39420         "ms",
39421         "1664"
39422       ],
39423       [
39424         "Morocco (‫المغرب‬‎)",
39425         "ma",
39426         "212",
39427         0
39428       ],
39429       [
39430         "Mozambique (Moçambique)",
39431         "mz",
39432         "258"
39433       ],
39434       [
39435         "Myanmar (Burma) (မြန်မာ)",
39436         "mm",
39437         "95"
39438       ],
39439       [
39440         "Namibia (Namibië)",
39441         "na",
39442         "264"
39443       ],
39444       [
39445         "Nauru",
39446         "nr",
39447         "674"
39448       ],
39449       [
39450         "Nepal (नेपाल)",
39451         "np",
39452         "977"
39453       ],
39454       [
39455         "Netherlands (Nederland)",
39456         "nl",
39457         "31"
39458       ],
39459       [
39460         "New Caledonia (Nouvelle-Calédonie)",
39461         "nc",
39462         "687"
39463       ],
39464       [
39465         "New Zealand",
39466         "nz",
39467         "64"
39468       ],
39469       [
39470         "Nicaragua",
39471         "ni",
39472         "505"
39473       ],
39474       [
39475         "Niger (Nijar)",
39476         "ne",
39477         "227"
39478       ],
39479       [
39480         "Nigeria",
39481         "ng",
39482         "234"
39483       ],
39484       [
39485         "Niue",
39486         "nu",
39487         "683"
39488       ],
39489       [
39490         "Norfolk Island",
39491         "nf",
39492         "672"
39493       ],
39494       [
39495         "North Korea (조선 민주주의 인민 공화국)",
39496         "kp",
39497         "850"
39498       ],
39499       [
39500         "Northern Mariana Islands",
39501         "mp",
39502         "1670"
39503       ],
39504       [
39505         "Norway (Norge)",
39506         "no",
39507         "47",
39508         0
39509       ],
39510       [
39511         "Oman (‫عُمان‬‎)",
39512         "om",
39513         "968"
39514       ],
39515       [
39516         "Pakistan (‫پاکستان‬‎)",
39517         "pk",
39518         "92"
39519       ],
39520       [
39521         "Palau",
39522         "pw",
39523         "680"
39524       ],
39525       [
39526         "Palestine (‫فلسطين‬‎)",
39527         "ps",
39528         "970"
39529       ],
39530       [
39531         "Panama (Panamá)",
39532         "pa",
39533         "507"
39534       ],
39535       [
39536         "Papua New Guinea",
39537         "pg",
39538         "675"
39539       ],
39540       [
39541         "Paraguay",
39542         "py",
39543         "595"
39544       ],
39545       [
39546         "Peru (Perú)",
39547         "pe",
39548         "51"
39549       ],
39550       [
39551         "Philippines",
39552         "ph",
39553         "63"
39554       ],
39555       [
39556         "Poland (Polska)",
39557         "pl",
39558         "48"
39559       ],
39560       [
39561         "Portugal",
39562         "pt",
39563         "351"
39564       ],
39565       [
39566         "Puerto Rico",
39567         "pr",
39568         "1",
39569         3,
39570         ["787", "939"]
39571       ],
39572       [
39573         "Qatar (‫قطر‬‎)",
39574         "qa",
39575         "974"
39576       ],
39577       [
39578         "Réunion (La Réunion)",
39579         "re",
39580         "262",
39581         0
39582       ],
39583       [
39584         "Romania (România)",
39585         "ro",
39586         "40"
39587       ],
39588       [
39589         "Russia (Россия)",
39590         "ru",
39591         "7",
39592         0
39593       ],
39594       [
39595         "Rwanda",
39596         "rw",
39597         "250"
39598       ],
39599       [
39600         "Saint Barthélemy",
39601         "bl",
39602         "590",
39603         1
39604       ],
39605       [
39606         "Saint Helena",
39607         "sh",
39608         "290"
39609       ],
39610       [
39611         "Saint Kitts and Nevis",
39612         "kn",
39613         "1869"
39614       ],
39615       [
39616         "Saint Lucia",
39617         "lc",
39618         "1758"
39619       ],
39620       [
39621         "Saint Martin (Saint-Martin (partie française))",
39622         "mf",
39623         "590",
39624         2
39625       ],
39626       [
39627         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39628         "pm",
39629         "508"
39630       ],
39631       [
39632         "Saint Vincent and the Grenadines",
39633         "vc",
39634         "1784"
39635       ],
39636       [
39637         "Samoa",
39638         "ws",
39639         "685"
39640       ],
39641       [
39642         "San Marino",
39643         "sm",
39644         "378"
39645       ],
39646       [
39647         "São Tomé and Príncipe (São Tomé e Príncipe)",
39648         "st",
39649         "239"
39650       ],
39651       [
39652         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39653         "sa",
39654         "966"
39655       ],
39656       [
39657         "Senegal (Sénégal)",
39658         "sn",
39659         "221"
39660       ],
39661       [
39662         "Serbia (Србија)",
39663         "rs",
39664         "381"
39665       ],
39666       [
39667         "Seychelles",
39668         "sc",
39669         "248"
39670       ],
39671       [
39672         "Sierra Leone",
39673         "sl",
39674         "232"
39675       ],
39676       [
39677         "Singapore",
39678         "sg",
39679         "65"
39680       ],
39681       [
39682         "Sint Maarten",
39683         "sx",
39684         "1721"
39685       ],
39686       [
39687         "Slovakia (Slovensko)",
39688         "sk",
39689         "421"
39690       ],
39691       [
39692         "Slovenia (Slovenija)",
39693         "si",
39694         "386"
39695       ],
39696       [
39697         "Solomon Islands",
39698         "sb",
39699         "677"
39700       ],
39701       [
39702         "Somalia (Soomaaliya)",
39703         "so",
39704         "252"
39705       ],
39706       [
39707         "South Africa",
39708         "za",
39709         "27"
39710       ],
39711       [
39712         "South Korea (대한민국)",
39713         "kr",
39714         "82"
39715       ],
39716       [
39717         "South Sudan (‫جنوب السودان‬‎)",
39718         "ss",
39719         "211"
39720       ],
39721       [
39722         "Spain (España)",
39723         "es",
39724         "34"
39725       ],
39726       [
39727         "Sri Lanka (ශ්‍රී ලංකාව)",
39728         "lk",
39729         "94"
39730       ],
39731       [
39732         "Sudan (‫السودان‬‎)",
39733         "sd",
39734         "249"
39735       ],
39736       [
39737         "Suriname",
39738         "sr",
39739         "597"
39740       ],
39741       [
39742         "Svalbard and Jan Mayen",
39743         "sj",
39744         "47",
39745         1
39746       ],
39747       [
39748         "Swaziland",
39749         "sz",
39750         "268"
39751       ],
39752       [
39753         "Sweden (Sverige)",
39754         "se",
39755         "46"
39756       ],
39757       [
39758         "Switzerland (Schweiz)",
39759         "ch",
39760         "41"
39761       ],
39762       [
39763         "Syria (‫سوريا‬‎)",
39764         "sy",
39765         "963"
39766       ],
39767       [
39768         "Taiwan (台灣)",
39769         "tw",
39770         "886"
39771       ],
39772       [
39773         "Tajikistan",
39774         "tj",
39775         "992"
39776       ],
39777       [
39778         "Tanzania",
39779         "tz",
39780         "255"
39781       ],
39782       [
39783         "Thailand (ไทย)",
39784         "th",
39785         "66"
39786       ],
39787       [
39788         "Timor-Leste",
39789         "tl",
39790         "670"
39791       ],
39792       [
39793         "Togo",
39794         "tg",
39795         "228"
39796       ],
39797       [
39798         "Tokelau",
39799         "tk",
39800         "690"
39801       ],
39802       [
39803         "Tonga",
39804         "to",
39805         "676"
39806       ],
39807       [
39808         "Trinidad and Tobago",
39809         "tt",
39810         "1868"
39811       ],
39812       [
39813         "Tunisia (‫تونس‬‎)",
39814         "tn",
39815         "216"
39816       ],
39817       [
39818         "Turkey (Türkiye)",
39819         "tr",
39820         "90"
39821       ],
39822       [
39823         "Turkmenistan",
39824         "tm",
39825         "993"
39826       ],
39827       [
39828         "Turks and Caicos Islands",
39829         "tc",
39830         "1649"
39831       ],
39832       [
39833         "Tuvalu",
39834         "tv",
39835         "688"
39836       ],
39837       [
39838         "U.S. Virgin Islands",
39839         "vi",
39840         "1340"
39841       ],
39842       [
39843         "Uganda",
39844         "ug",
39845         "256"
39846       ],
39847       [
39848         "Ukraine (Україна)",
39849         "ua",
39850         "380"
39851       ],
39852       [
39853         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39854         "ae",
39855         "971"
39856       ],
39857       [
39858         "United Kingdom",
39859         "gb",
39860         "44",
39861         0
39862       ],
39863       [
39864         "United States",
39865         "us",
39866         "1",
39867         0
39868       ],
39869       [
39870         "Uruguay",
39871         "uy",
39872         "598"
39873       ],
39874       [
39875         "Uzbekistan (Oʻzbekiston)",
39876         "uz",
39877         "998"
39878       ],
39879       [
39880         "Vanuatu",
39881         "vu",
39882         "678"
39883       ],
39884       [
39885         "Vatican City (Città del Vaticano)",
39886         "va",
39887         "39",
39888         1
39889       ],
39890       [
39891         "Venezuela",
39892         "ve",
39893         "58"
39894       ],
39895       [
39896         "Vietnam (Việt Nam)",
39897         "vn",
39898         "84"
39899       ],
39900       [
39901         "Wallis and Futuna (Wallis-et-Futuna)",
39902         "wf",
39903         "681"
39904       ],
39905       [
39906         "Western Sahara (‫الصحراء الغربية‬‎)",
39907         "eh",
39908         "212",
39909         1
39910       ],
39911       [
39912         "Yemen (‫اليمن‬‎)",
39913         "ye",
39914         "967"
39915       ],
39916       [
39917         "Zambia",
39918         "zm",
39919         "260"
39920       ],
39921       [
39922         "Zimbabwe",
39923         "zw",
39924         "263"
39925       ],
39926       [
39927         "Åland Islands",
39928         "ax",
39929         "358",
39930         1
39931       ]
39932   ];
39933   
39934   return d;
39935 }/**
39936 *    This script refer to:
39937 *    Title: International Telephone Input
39938 *    Author: Jack O'Connor
39939 *    Code version:  v12.1.12
39940 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39941 **/
39942
39943 /**
39944  * @class Roo.bootstrap.PhoneInput
39945  * @extends Roo.bootstrap.TriggerField
39946  * An input with International dial-code selection
39947  
39948  * @cfg {String} defaultDialCode default '+852'
39949  * @cfg {Array} preferedCountries default []
39950   
39951  * @constructor
39952  * Create a new PhoneInput.
39953  * @param {Object} config Configuration options
39954  */
39955
39956 Roo.bootstrap.PhoneInput = function(config) {
39957     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39958 };
39959
39960 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39961         
39962         listWidth: undefined,
39963         
39964         selectedClass: 'active',
39965         
39966         invalidClass : "has-warning",
39967         
39968         validClass: 'has-success',
39969         
39970         allowed: '0123456789',
39971         
39972         max_length: 15,
39973         
39974         /**
39975          * @cfg {String} defaultDialCode The default dial code when initializing the input
39976          */
39977         defaultDialCode: '+852',
39978         
39979         /**
39980          * @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
39981          */
39982         preferedCountries: false,
39983         
39984         getAutoCreate : function()
39985         {
39986             var data = Roo.bootstrap.PhoneInputData();
39987             var align = this.labelAlign || this.parentLabelAlign();
39988             var id = Roo.id();
39989             
39990             this.allCountries = [];
39991             this.dialCodeMapping = [];
39992             
39993             for (var i = 0; i < data.length; i++) {
39994               var c = data[i];
39995               this.allCountries[i] = {
39996                 name: c[0],
39997                 iso2: c[1],
39998                 dialCode: c[2],
39999                 priority: c[3] || 0,
40000                 areaCodes: c[4] || null
40001               };
40002               this.dialCodeMapping[c[2]] = {
40003                   name: c[0],
40004                   iso2: c[1],
40005                   priority: c[3] || 0,
40006                   areaCodes: c[4] || null
40007               };
40008             }
40009             
40010             var cfg = {
40011                 cls: 'form-group',
40012                 cn: []
40013             };
40014             
40015             var input =  {
40016                 tag: 'input',
40017                 id : id,
40018                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40019                 maxlength: this.max_length,
40020                 cls : 'form-control tel-input',
40021                 autocomplete: 'new-password'
40022             };
40023             
40024             var hiddenInput = {
40025                 tag: 'input',
40026                 type: 'hidden',
40027                 cls: 'hidden-tel-input'
40028             };
40029             
40030             if (this.name) {
40031                 hiddenInput.name = this.name;
40032             }
40033             
40034             if (this.disabled) {
40035                 input.disabled = true;
40036             }
40037             
40038             var flag_container = {
40039                 tag: 'div',
40040                 cls: 'flag-box',
40041                 cn: [
40042                     {
40043                         tag: 'div',
40044                         cls: 'flag'
40045                     },
40046                     {
40047                         tag: 'div',
40048                         cls: 'caret'
40049                     }
40050                 ]
40051             };
40052             
40053             var box = {
40054                 tag: 'div',
40055                 cls: this.hasFeedback ? 'has-feedback' : '',
40056                 cn: [
40057                     hiddenInput,
40058                     input,
40059                     {
40060                         tag: 'input',
40061                         cls: 'dial-code-holder',
40062                         disabled: true
40063                     }
40064                 ]
40065             };
40066             
40067             var container = {
40068                 cls: 'roo-select2-container input-group',
40069                 cn: [
40070                     flag_container,
40071                     box
40072                 ]
40073             };
40074             
40075             if (this.fieldLabel.length) {
40076                 var indicator = {
40077                     tag: 'i',
40078                     tooltip: 'This field is required'
40079                 };
40080                 
40081                 var label = {
40082                     tag: 'label',
40083                     'for':  id,
40084                     cls: 'control-label',
40085                     cn: []
40086                 };
40087                 
40088                 var label_text = {
40089                     tag: 'span',
40090                     html: this.fieldLabel
40091                 };
40092                 
40093                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40094                 label.cn = [
40095                     indicator,
40096                     label_text
40097                 ];
40098                 
40099                 if(this.indicatorpos == 'right') {
40100                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40101                     label.cn = [
40102                         label_text,
40103                         indicator
40104                     ];
40105                 }
40106                 
40107                 if(align == 'left') {
40108                     container = {
40109                         tag: 'div',
40110                         cn: [
40111                             container
40112                         ]
40113                     };
40114                     
40115                     if(this.labelWidth > 12){
40116                         label.style = "width: " + this.labelWidth + 'px';
40117                     }
40118                     if(this.labelWidth < 13 && this.labelmd == 0){
40119                         this.labelmd = this.labelWidth;
40120                     }
40121                     if(this.labellg > 0){
40122                         label.cls += ' col-lg-' + this.labellg;
40123                         input.cls += ' col-lg-' + (12 - this.labellg);
40124                     }
40125                     if(this.labelmd > 0){
40126                         label.cls += ' col-md-' + this.labelmd;
40127                         container.cls += ' col-md-' + (12 - this.labelmd);
40128                     }
40129                     if(this.labelsm > 0){
40130                         label.cls += ' col-sm-' + this.labelsm;
40131                         container.cls += ' col-sm-' + (12 - this.labelsm);
40132                     }
40133                     if(this.labelxs > 0){
40134                         label.cls += ' col-xs-' + this.labelxs;
40135                         container.cls += ' col-xs-' + (12 - this.labelxs);
40136                     }
40137                 }
40138             }
40139             
40140             cfg.cn = [
40141                 label,
40142                 container
40143             ];
40144             
40145             var settings = this;
40146             
40147             ['xs','sm','md','lg'].map(function(size){
40148                 if (settings[size]) {
40149                     cfg.cls += ' col-' + size + '-' + settings[size];
40150                 }
40151             });
40152             
40153             this.store = new Roo.data.Store({
40154                 proxy : new Roo.data.MemoryProxy({}),
40155                 reader : new Roo.data.JsonReader({
40156                     fields : [
40157                         {
40158                             'name' : 'name',
40159                             'type' : 'string'
40160                         },
40161                         {
40162                             'name' : 'iso2',
40163                             'type' : 'string'
40164                         },
40165                         {
40166                             'name' : 'dialCode',
40167                             'type' : 'string'
40168                         },
40169                         {
40170                             'name' : 'priority',
40171                             'type' : 'string'
40172                         },
40173                         {
40174                             'name' : 'areaCodes',
40175                             'type' : 'string'
40176                         }
40177                     ]
40178                 })
40179             });
40180             
40181             if(!this.preferedCountries) {
40182                 this.preferedCountries = [
40183                     'hk',
40184                     'gb',
40185                     'us'
40186                 ];
40187             }
40188             
40189             var p = this.preferedCountries.reverse();
40190             
40191             if(p) {
40192                 for (var i = 0; i < p.length; i++) {
40193                     for (var j = 0; j < this.allCountries.length; j++) {
40194                         if(this.allCountries[j].iso2 == p[i]) {
40195                             var t = this.allCountries[j];
40196                             this.allCountries.splice(j,1);
40197                             this.allCountries.unshift(t);
40198                         }
40199                     } 
40200                 }
40201             }
40202             
40203             this.store.proxy.data = {
40204                 success: true,
40205                 data: this.allCountries
40206             };
40207             
40208             return cfg;
40209         },
40210         
40211         initEvents : function()
40212         {
40213             this.createList();
40214             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40215             
40216             this.indicator = this.indicatorEl();
40217             this.flag = this.flagEl();
40218             this.dialCodeHolder = this.dialCodeHolderEl();
40219             
40220             this.trigger = this.el.select('div.flag-box',true).first();
40221             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40222             
40223             var _this = this;
40224             
40225             (function(){
40226                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40227                 _this.list.setWidth(lw);
40228             }).defer(100);
40229             
40230             this.list.on('mouseover', this.onViewOver, this);
40231             this.list.on('mousemove', this.onViewMove, this);
40232             this.inputEl().on("keyup", this.onKeyUp, this);
40233             this.inputEl().on("keypress", this.onKeyPress, this);
40234             
40235             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40236
40237             this.view = new Roo.View(this.list, this.tpl, {
40238                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40239             });
40240             
40241             this.view.on('click', this.onViewClick, this);
40242             this.setValue(this.defaultDialCode);
40243         },
40244         
40245         onTriggerClick : function(e)
40246         {
40247             Roo.log('trigger click');
40248             if(this.disabled){
40249                 return;
40250             }
40251             
40252             if(this.isExpanded()){
40253                 this.collapse();
40254                 this.hasFocus = false;
40255             }else {
40256                 this.store.load({});
40257                 this.hasFocus = true;
40258                 this.expand();
40259             }
40260         },
40261         
40262         isExpanded : function()
40263         {
40264             return this.list.isVisible();
40265         },
40266         
40267         collapse : function()
40268         {
40269             if(!this.isExpanded()){
40270                 return;
40271             }
40272             this.list.hide();
40273             Roo.get(document).un('mousedown', this.collapseIf, this);
40274             Roo.get(document).un('mousewheel', this.collapseIf, this);
40275             this.fireEvent('collapse', this);
40276             this.validate();
40277         },
40278         
40279         expand : function()
40280         {
40281             Roo.log('expand');
40282
40283             if(this.isExpanded() || !this.hasFocus){
40284                 return;
40285             }
40286             
40287             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40288             this.list.setWidth(lw);
40289             
40290             this.list.show();
40291             this.restrictHeight();
40292             
40293             Roo.get(document).on('mousedown', this.collapseIf, this);
40294             Roo.get(document).on('mousewheel', this.collapseIf, this);
40295             
40296             this.fireEvent('expand', this);
40297         },
40298         
40299         restrictHeight : function()
40300         {
40301             this.list.alignTo(this.inputEl(), this.listAlign);
40302             this.list.alignTo(this.inputEl(), this.listAlign);
40303         },
40304         
40305         onViewOver : function(e, t)
40306         {
40307             if(this.inKeyMode){
40308                 return;
40309             }
40310             var item = this.view.findItemFromChild(t);
40311             
40312             if(item){
40313                 var index = this.view.indexOf(item);
40314                 this.select(index, false);
40315             }
40316         },
40317
40318         // private
40319         onViewClick : function(view, doFocus, el, e)
40320         {
40321             var index = this.view.getSelectedIndexes()[0];
40322             
40323             var r = this.store.getAt(index);
40324             
40325             if(r){
40326                 this.onSelect(r, index);
40327             }
40328             if(doFocus !== false && !this.blockFocus){
40329                 this.inputEl().focus();
40330             }
40331         },
40332         
40333         onViewMove : function(e, t)
40334         {
40335             this.inKeyMode = false;
40336         },
40337         
40338         select : function(index, scrollIntoView)
40339         {
40340             this.selectedIndex = index;
40341             this.view.select(index);
40342             if(scrollIntoView !== false){
40343                 var el = this.view.getNode(index);
40344                 if(el){
40345                     this.list.scrollChildIntoView(el, false);
40346                 }
40347             }
40348         },
40349         
40350         createList : function()
40351         {
40352             this.list = Roo.get(document.body).createChild({
40353                 tag: 'ul',
40354                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40355                 style: 'display:none'
40356             });
40357             
40358             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40359         },
40360         
40361         collapseIf : function(e)
40362         {
40363             var in_combo  = e.within(this.el);
40364             var in_list =  e.within(this.list);
40365             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40366             
40367             if (in_combo || in_list || is_list) {
40368                 return;
40369             }
40370             this.collapse();
40371         },
40372         
40373         onSelect : function(record, index)
40374         {
40375             if(this.fireEvent('beforeselect', this, record, index) !== false){
40376                 
40377                 this.setFlagClass(record.data.iso2);
40378                 this.setDialCode(record.data.dialCode);
40379                 this.hasFocus = false;
40380                 this.collapse();
40381                 this.fireEvent('select', this, record, index);
40382             }
40383         },
40384         
40385         flagEl : function()
40386         {
40387             var flag = this.el.select('div.flag',true).first();
40388             if(!flag){
40389                 return false;
40390             }
40391             return flag;
40392         },
40393         
40394         dialCodeHolderEl : function()
40395         {
40396             var d = this.el.select('input.dial-code-holder',true).first();
40397             if(!d){
40398                 return false;
40399             }
40400             return d;
40401         },
40402         
40403         setDialCode : function(v)
40404         {
40405             this.dialCodeHolder.dom.value = '+'+v;
40406         },
40407         
40408         setFlagClass : function(n)
40409         {
40410             this.flag.dom.className = 'flag '+n;
40411         },
40412         
40413         getValue : function()
40414         {
40415             var v = this.inputEl().getValue();
40416             if(this.dialCodeHolder) {
40417                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40418             }
40419             return v;
40420         },
40421         
40422         setValue : function(v)
40423         {
40424             var d = this.getDialCode(v);
40425             
40426             //invalid dial code
40427             if(v.length == 0 || !d || d.length == 0) {
40428                 if(this.rendered){
40429                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40430                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40431                 }
40432                 return;
40433             }
40434             
40435             //valid dial code
40436             this.setFlagClass(this.dialCodeMapping[d].iso2);
40437             this.setDialCode(d);
40438             this.inputEl().dom.value = v.replace('+'+d,'');
40439             this.hiddenEl().dom.value = this.getValue();
40440             
40441             this.validate();
40442         },
40443         
40444         getDialCode : function(v)
40445         {
40446             v = v ||  '';
40447             
40448             if (v.length == 0) {
40449                 return this.dialCodeHolder.dom.value;
40450             }
40451             
40452             var dialCode = "";
40453             if (v.charAt(0) != "+") {
40454                 return false;
40455             }
40456             var numericChars = "";
40457             for (var i = 1; i < v.length; i++) {
40458               var c = v.charAt(i);
40459               if (!isNaN(c)) {
40460                 numericChars += c;
40461                 if (this.dialCodeMapping[numericChars]) {
40462                   dialCode = v.substr(1, i);
40463                 }
40464                 if (numericChars.length == 4) {
40465                   break;
40466                 }
40467               }
40468             }
40469             return dialCode;
40470         },
40471         
40472         reset : function()
40473         {
40474             this.setValue(this.defaultDialCode);
40475             this.validate();
40476         },
40477         
40478         hiddenEl : function()
40479         {
40480             return this.el.select('input.hidden-tel-input',true).first();
40481         },
40482         
40483         // after setting val
40484         onKeyUp : function(e){
40485             this.setValue(this.getValue());
40486         },
40487         
40488         onKeyPress : function(e){
40489             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40490                 e.stopEvent();
40491             }
40492         }
40493         
40494 });
40495 /**
40496  * @class Roo.bootstrap.MoneyField
40497  * @extends Roo.bootstrap.ComboBox
40498  * Bootstrap MoneyField class
40499  * 
40500  * @constructor
40501  * Create a new MoneyField.
40502  * @param {Object} config Configuration options
40503  */
40504
40505 Roo.bootstrap.MoneyField = function(config) {
40506     
40507     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40508     
40509 };
40510
40511 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40512     
40513     /**
40514      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40515      */
40516     allowDecimals : true,
40517     /**
40518      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40519      */
40520     decimalSeparator : ".",
40521     /**
40522      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40523      */
40524     decimalPrecision : 0,
40525     /**
40526      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40527      */
40528     allowNegative : true,
40529     /**
40530      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40531      */
40532     allowZero: true,
40533     /**
40534      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40535      */
40536     minValue : Number.NEGATIVE_INFINITY,
40537     /**
40538      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40539      */
40540     maxValue : Number.MAX_VALUE,
40541     /**
40542      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40543      */
40544     minText : "The minimum value for this field is {0}",
40545     /**
40546      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40547      */
40548     maxText : "The maximum value for this field is {0}",
40549     /**
40550      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40551      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40552      */
40553     nanText : "{0} is not a valid number",
40554     /**
40555      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40556      */
40557     castInt : true,
40558     /**
40559      * @cfg {String} defaults currency of the MoneyField
40560      * value should be in lkey
40561      */
40562     defaultCurrency : false,
40563     /**
40564      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40565      */
40566     thousandsDelimiter : false,
40567     /**
40568      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40569      */
40570     max_length: false,
40571     
40572     inputlg : 9,
40573     inputmd : 9,
40574     inputsm : 9,
40575     inputxs : 6,
40576     
40577     store : false,
40578     
40579     getAutoCreate : function()
40580     {
40581         var align = this.labelAlign || this.parentLabelAlign();
40582         
40583         var id = Roo.id();
40584
40585         var cfg = {
40586             cls: 'form-group',
40587             cn: []
40588         };
40589
40590         var input =  {
40591             tag: 'input',
40592             id : id,
40593             cls : 'form-control roo-money-amount-input',
40594             autocomplete: 'new-password'
40595         };
40596         
40597         var hiddenInput = {
40598             tag: 'input',
40599             type: 'hidden',
40600             id: Roo.id(),
40601             cls: 'hidden-number-input'
40602         };
40603         
40604         if(this.max_length) {
40605             input.maxlength = this.max_length; 
40606         }
40607         
40608         if (this.name) {
40609             hiddenInput.name = this.name;
40610         }
40611
40612         if (this.disabled) {
40613             input.disabled = true;
40614         }
40615
40616         var clg = 12 - this.inputlg;
40617         var cmd = 12 - this.inputmd;
40618         var csm = 12 - this.inputsm;
40619         var cxs = 12 - this.inputxs;
40620         
40621         var container = {
40622             tag : 'div',
40623             cls : 'row roo-money-field',
40624             cn : [
40625                 {
40626                     tag : 'div',
40627                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40628                     cn : [
40629                         {
40630                             tag : 'div',
40631                             cls: 'roo-select2-container input-group',
40632                             cn: [
40633                                 {
40634                                     tag : 'input',
40635                                     cls : 'form-control roo-money-currency-input',
40636                                     autocomplete: 'new-password',
40637                                     readOnly : 1,
40638                                     name : this.currencyName
40639                                 },
40640                                 {
40641                                     tag :'span',
40642                                     cls : 'input-group-addon',
40643                                     cn : [
40644                                         {
40645                                             tag: 'span',
40646                                             cls: 'caret'
40647                                         }
40648                                     ]
40649                                 }
40650                             ]
40651                         }
40652                     ]
40653                 },
40654                 {
40655                     tag : 'div',
40656                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40657                     cn : [
40658                         {
40659                             tag: 'div',
40660                             cls: this.hasFeedback ? 'has-feedback' : '',
40661                             cn: [
40662                                 input
40663                             ]
40664                         }
40665                     ]
40666                 }
40667             ]
40668             
40669         };
40670         
40671         if (this.fieldLabel.length) {
40672             var indicator = {
40673                 tag: 'i',
40674                 tooltip: 'This field is required'
40675             };
40676
40677             var label = {
40678                 tag: 'label',
40679                 'for':  id,
40680                 cls: 'control-label',
40681                 cn: []
40682             };
40683
40684             var label_text = {
40685                 tag: 'span',
40686                 html: this.fieldLabel
40687             };
40688
40689             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40690             label.cn = [
40691                 indicator,
40692                 label_text
40693             ];
40694
40695             if(this.indicatorpos == 'right') {
40696                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40697                 label.cn = [
40698                     label_text,
40699                     indicator
40700                 ];
40701             }
40702
40703             if(align == 'left') {
40704                 container = {
40705                     tag: 'div',
40706                     cn: [
40707                         container
40708                     ]
40709                 };
40710
40711                 if(this.labelWidth > 12){
40712                     label.style = "width: " + this.labelWidth + 'px';
40713                 }
40714                 if(this.labelWidth < 13 && this.labelmd == 0){
40715                     this.labelmd = this.labelWidth;
40716                 }
40717                 if(this.labellg > 0){
40718                     label.cls += ' col-lg-' + this.labellg;
40719                     input.cls += ' col-lg-' + (12 - this.labellg);
40720                 }
40721                 if(this.labelmd > 0){
40722                     label.cls += ' col-md-' + this.labelmd;
40723                     container.cls += ' col-md-' + (12 - this.labelmd);
40724                 }
40725                 if(this.labelsm > 0){
40726                     label.cls += ' col-sm-' + this.labelsm;
40727                     container.cls += ' col-sm-' + (12 - this.labelsm);
40728                 }
40729                 if(this.labelxs > 0){
40730                     label.cls += ' col-xs-' + this.labelxs;
40731                     container.cls += ' col-xs-' + (12 - this.labelxs);
40732                 }
40733             }
40734         }
40735
40736         cfg.cn = [
40737             label,
40738             container,
40739             hiddenInput
40740         ];
40741         
40742         var settings = this;
40743
40744         ['xs','sm','md','lg'].map(function(size){
40745             if (settings[size]) {
40746                 cfg.cls += ' col-' + size + '-' + settings[size];
40747             }
40748         });
40749         
40750         return cfg;
40751     },
40752     
40753     initEvents : function()
40754     {
40755         this.indicator = this.indicatorEl();
40756         
40757         this.initCurrencyEvent();
40758         
40759         this.initNumberEvent();
40760     },
40761     
40762     initCurrencyEvent : function()
40763     {
40764         if (!this.store) {
40765             throw "can not find store for combo";
40766         }
40767         
40768         this.store = Roo.factory(this.store, Roo.data);
40769         this.store.parent = this;
40770         
40771         this.createList();
40772         
40773         this.triggerEl = this.el.select('.input-group-addon', true).first();
40774         
40775         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40776         
40777         var _this = this;
40778         
40779         (function(){
40780             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40781             _this.list.setWidth(lw);
40782         }).defer(100);
40783         
40784         this.list.on('mouseover', this.onViewOver, this);
40785         this.list.on('mousemove', this.onViewMove, this);
40786         this.list.on('scroll', this.onViewScroll, this);
40787         
40788         if(!this.tpl){
40789             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40790         }
40791         
40792         this.view = new Roo.View(this.list, this.tpl, {
40793             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40794         });
40795         
40796         this.view.on('click', this.onViewClick, this);
40797         
40798         this.store.on('beforeload', this.onBeforeLoad, this);
40799         this.store.on('load', this.onLoad, this);
40800         this.store.on('loadexception', this.onLoadException, this);
40801         
40802         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40803             "up" : function(e){
40804                 this.inKeyMode = true;
40805                 this.selectPrev();
40806             },
40807
40808             "down" : function(e){
40809                 if(!this.isExpanded()){
40810                     this.onTriggerClick();
40811                 }else{
40812                     this.inKeyMode = true;
40813                     this.selectNext();
40814                 }
40815             },
40816
40817             "enter" : function(e){
40818                 this.collapse();
40819                 
40820                 if(this.fireEvent("specialkey", this, e)){
40821                     this.onViewClick(false);
40822                 }
40823                 
40824                 return true;
40825             },
40826
40827             "esc" : function(e){
40828                 this.collapse();
40829             },
40830
40831             "tab" : function(e){
40832                 this.collapse();
40833                 
40834                 if(this.fireEvent("specialkey", this, e)){
40835                     this.onViewClick(false);
40836                 }
40837                 
40838                 return true;
40839             },
40840
40841             scope : this,
40842
40843             doRelay : function(foo, bar, hname){
40844                 if(hname == 'down' || this.scope.isExpanded()){
40845                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40846                 }
40847                 return true;
40848             },
40849
40850             forceKeyDown: true
40851         });
40852         
40853         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40854         
40855     },
40856     
40857     initNumberEvent : function(e)
40858     {
40859         this.inputEl().on("keydown" , this.fireKey,  this);
40860         this.inputEl().on("focus", this.onFocus,  this);
40861         this.inputEl().on("blur", this.onBlur,  this);
40862         
40863         this.inputEl().relayEvent('keyup', this);
40864         
40865         if(this.indicator){
40866             this.indicator.addClass('invisible');
40867         }
40868  
40869         this.originalValue = this.getValue();
40870         
40871         if(this.validationEvent == 'keyup'){
40872             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40873             this.inputEl().on('keyup', this.filterValidation, this);
40874         }
40875         else if(this.validationEvent !== false){
40876             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40877         }
40878         
40879         if(this.selectOnFocus){
40880             this.on("focus", this.preFocus, this);
40881             
40882         }
40883         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40884             this.inputEl().on("keypress", this.filterKeys, this);
40885         } else {
40886             this.inputEl().relayEvent('keypress', this);
40887         }
40888         
40889         var allowed = "0123456789";
40890         
40891         if(this.allowDecimals){
40892             allowed += this.decimalSeparator;
40893         }
40894         
40895         if(this.allowNegative){
40896             allowed += "-";
40897         }
40898         
40899         if(this.thousandsDelimiter) {
40900             allowed += ",";
40901         }
40902         
40903         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40904         
40905         var keyPress = function(e){
40906             
40907             var k = e.getKey();
40908             
40909             var c = e.getCharCode();
40910             
40911             if(
40912                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40913                     allowed.indexOf(String.fromCharCode(c)) === -1
40914             ){
40915                 e.stopEvent();
40916                 return;
40917             }
40918             
40919             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40920                 return;
40921             }
40922             
40923             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40924                 e.stopEvent();
40925             }
40926         };
40927         
40928         this.inputEl().on("keypress", keyPress, this);
40929         
40930     },
40931     
40932     onTriggerClick : function(e)
40933     {   
40934         if(this.disabled){
40935             return;
40936         }
40937         
40938         this.page = 0;
40939         this.loadNext = false;
40940         
40941         if(this.isExpanded()){
40942             this.collapse();
40943             return;
40944         }
40945         
40946         this.hasFocus = true;
40947         
40948         if(this.triggerAction == 'all') {
40949             this.doQuery(this.allQuery, true);
40950             return;
40951         }
40952         
40953         this.doQuery(this.getRawValue());
40954     },
40955     
40956     getCurrency : function()
40957     {   
40958         var v = this.currencyEl().getValue();
40959         
40960         return v;
40961     },
40962     
40963     restrictHeight : function()
40964     {
40965         this.list.alignTo(this.currencyEl(), this.listAlign);
40966         this.list.alignTo(this.currencyEl(), this.listAlign);
40967     },
40968     
40969     onViewClick : function(view, doFocus, el, e)
40970     {
40971         var index = this.view.getSelectedIndexes()[0];
40972         
40973         var r = this.store.getAt(index);
40974         
40975         if(r){
40976             this.onSelect(r, index);
40977         }
40978     },
40979     
40980     onSelect : function(record, index){
40981         
40982         if(this.fireEvent('beforeselect', this, record, index) !== false){
40983         
40984             this.setFromCurrencyData(index > -1 ? record.data : false);
40985             
40986             this.collapse();
40987             
40988             this.fireEvent('select', this, record, index);
40989         }
40990     },
40991     
40992     setFromCurrencyData : function(o)
40993     {
40994         var currency = '';
40995         
40996         this.lastCurrency = o;
40997         
40998         if (this.currencyField) {
40999             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41000         } else {
41001             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41002         }
41003         
41004         this.lastSelectionText = currency;
41005         
41006         //setting default currency
41007         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41008             this.setCurrency(this.defaultCurrency);
41009             return;
41010         }
41011         
41012         this.setCurrency(currency);
41013     },
41014     
41015     setFromData : function(o)
41016     {
41017         var c = {};
41018         
41019         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41020         
41021         this.setFromCurrencyData(c);
41022         
41023         var value = '';
41024         
41025         if (this.name) {
41026             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41027         } else {
41028             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41029         }
41030         
41031         this.setValue(value);
41032         
41033     },
41034     
41035     setCurrency : function(v)
41036     {   
41037         this.currencyValue = v;
41038         
41039         if(this.rendered){
41040             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41041             this.validate();
41042         }
41043     },
41044     
41045     setValue : function(v)
41046     {
41047         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41048         
41049         this.value = v;
41050         
41051         if(this.rendered){
41052             
41053             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41054             
41055             this.inputEl().dom.value = (v == '') ? '' :
41056                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41057             
41058             if(!this.allowZero && v === '0') {
41059                 this.hiddenEl().dom.value = '';
41060                 this.inputEl().dom.value = '';
41061             }
41062             
41063             this.validate();
41064         }
41065     },
41066     
41067     getRawValue : function()
41068     {
41069         var v = this.inputEl().getValue();
41070         
41071         return v;
41072     },
41073     
41074     getValue : function()
41075     {
41076         return this.fixPrecision(this.parseValue(this.getRawValue()));
41077     },
41078     
41079     parseValue : function(value)
41080     {
41081         if(this.thousandsDelimiter) {
41082             value += "";
41083             r = new RegExp(",", "g");
41084             value = value.replace(r, "");
41085         }
41086         
41087         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41088         return isNaN(value) ? '' : value;
41089         
41090     },
41091     
41092     fixPrecision : function(value)
41093     {
41094         if(this.thousandsDelimiter) {
41095             value += "";
41096             r = new RegExp(",", "g");
41097             value = value.replace(r, "");
41098         }
41099         
41100         var nan = isNaN(value);
41101         
41102         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41103             return nan ? '' : value;
41104         }
41105         return parseFloat(value).toFixed(this.decimalPrecision);
41106     },
41107     
41108     decimalPrecisionFcn : function(v)
41109     {
41110         return Math.floor(v);
41111     },
41112     
41113     validateValue : function(value)
41114     {
41115         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41116             return false;
41117         }
41118         
41119         var num = this.parseValue(value);
41120         
41121         if(isNaN(num)){
41122             this.markInvalid(String.format(this.nanText, value));
41123             return false;
41124         }
41125         
41126         if(num < this.minValue){
41127             this.markInvalid(String.format(this.minText, this.minValue));
41128             return false;
41129         }
41130         
41131         if(num > this.maxValue){
41132             this.markInvalid(String.format(this.maxText, this.maxValue));
41133             return false;
41134         }
41135         
41136         return true;
41137     },
41138     
41139     validate : function()
41140     {
41141         if(this.disabled || this.allowBlank){
41142             this.markValid();
41143             return true;
41144         }
41145         
41146         var currency = this.getCurrency();
41147         
41148         if(this.validateValue(this.getRawValue()) && currency.length){
41149             this.markValid();
41150             return true;
41151         }
41152         
41153         this.markInvalid();
41154         return false;
41155     },
41156     
41157     getName: function()
41158     {
41159         return this.name;
41160     },
41161     
41162     beforeBlur : function()
41163     {
41164         if(!this.castInt){
41165             return;
41166         }
41167         
41168         var v = this.parseValue(this.getRawValue());
41169         
41170         if(v || v == 0){
41171             this.setValue(v);
41172         }
41173     },
41174     
41175     onBlur : function()
41176     {
41177         this.beforeBlur();
41178         
41179         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41180             //this.el.removeClass(this.focusClass);
41181         }
41182         
41183         this.hasFocus = false;
41184         
41185         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41186             this.validate();
41187         }
41188         
41189         var v = this.getValue();
41190         
41191         if(String(v) !== String(this.startValue)){
41192             this.fireEvent('change', this, v, this.startValue);
41193         }
41194         
41195         this.fireEvent("blur", this);
41196     },
41197     
41198     inputEl : function()
41199     {
41200         return this.el.select('.roo-money-amount-input', true).first();
41201     },
41202     
41203     currencyEl : function()
41204     {
41205         return this.el.select('.roo-money-currency-input', true).first();
41206     },
41207     
41208     hiddenEl : function()
41209     {
41210         return this.el.select('input.hidden-number-input',true).first();
41211     }
41212     
41213 });