roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     badge: '',
653     theme: 'default',
654     inverse: false,
655     
656     toggle: false,
657     ontext: 'ON',
658     offtext: 'OFF',
659     defaulton: true,
660     preventDefault: true,
661     removeClass: false,
662     name: false,
663     target: false,
664      
665     pressed : null,
666      
667     
668     getAutoCreate : function(){
669         
670         var cfg = {
671             tag : 'button',
672             cls : 'roo-button',
673             html: ''
674         };
675         
676         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
677             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
678             this.tag = 'button';
679         } else {
680             cfg.tag = this.tag;
681         }
682         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
683         
684         if (this.toggle == true) {
685             cfg={
686                 tag: 'div',
687                 cls: 'slider-frame roo-button',
688                 cn: [
689                     {
690                         tag: 'span',
691                         'data-on-text':'ON',
692                         'data-off-text':'OFF',
693                         cls: 'slider-button',
694                         html: this.offtext
695                     }
696                 ]
697             };
698             
699             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
700                 cfg.cls += ' '+this.weight;
701             }
702             
703             return cfg;
704         }
705         
706         if (this.isClose) {
707             cfg.cls += ' close';
708             
709             cfg["aria-hidden"] = true;
710             
711             cfg.html = "&times;";
712             
713             return cfg;
714         }
715         
716          
717         if (this.theme==='default') {
718             cfg.cls = 'btn roo-button';
719             
720             //if (this.parentType != 'Navbar') {
721             this.weight = this.weight.length ?  this.weight : 'default';
722             //}
723             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
724                 
725                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
726                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
727                 cfg.cls += ' btn-' + outline + weight;
728                 if (this.weight == 'default') {
729                     // BC
730                     cfg.cls += ' btn-' + this.weight;
731                 }
732             }
733         } else if (this.theme==='glow') {
734             
735             cfg.tag = 'a';
736             cfg.cls = 'btn-glow roo-button';
737             
738             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
739                 
740                 cfg.cls += ' ' + this.weight;
741             }
742         }
743    
744         
745         if (this.inverse) {
746             this.cls += ' inverse';
747         }
748         
749         
750         if (this.active || this.pressed === true) {
751             cfg.cls += ' active';
752         }
753         
754         if (this.disabled) {
755             cfg.disabled = 'disabled';
756         }
757         
758         if (this.items) {
759             Roo.log('changing to ul' );
760             cfg.tag = 'ul';
761             this.glyphicon = 'caret';
762         }
763         
764         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
765          
766         //gsRoo.log(this.parentType);
767         if (this.parentType === 'Navbar' && !this.parent().bar) {
768             Roo.log('changing to li?');
769             
770             cfg.tag = 'li';
771             
772             cfg.cls = '';
773             cfg.cn =  [{
774                 tag : 'a',
775                 cls : 'roo-button',
776                 html : this.html,
777                 href : this.href || '#'
778             }];
779             if (this.menu) {
780                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
781                 cfg.cls += ' dropdown';
782             }   
783             
784             delete cfg.html;
785             
786         }
787         
788        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
789         
790         if (this.glyphicon) {
791             cfg.html = ' ' + cfg.html;
792             
793             cfg.cn = [
794                 {
795                     tag: 'span',
796                     cls: 'glyphicon glyphicon-' + this.glyphicon
797                 }
798             ];
799         }
800         
801         if (this.badge) {
802             cfg.html += ' ';
803             
804             cfg.tag = 'a';
805             
806 //            cfg.cls='btn roo-button';
807             
808             cfg.href=this.href;
809             
810             var value = cfg.html;
811             
812             if(this.glyphicon){
813                 value = {
814                             tag: 'span',
815                             cls: 'glyphicon glyphicon-' + this.glyphicon,
816                             html: this.html
817                         };
818                 
819             }
820             var bw = this.badge_weight.length ? this.badge_weight :
821                 (this.weight.length ? this.weight : 'secondary');
822             bw = bw == 'default' ? 'secondary' : bw;
823             
824             cfg.cn = [
825                 value,
826                 {
827                     tag: 'span',
828                     cls: 'badge badge-' + bw,
829                     html: this.badge
830                 }
831             ];
832             
833             cfg.html='';
834         }
835         
836         if (this.menu) {
837             cfg.cls += ' dropdown';
838             cfg.html = typeof(cfg.html) != 'undefined' ?
839                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
840         }
841         
842         if (cfg.tag !== 'a' && this.href !== '') {
843             throw "Tag must be a to set href.";
844         } else if (this.href.length > 0) {
845             cfg.href = this.href;
846         }
847         
848         if(this.removeClass){
849             cfg.cls = '';
850         }
851         
852         if(this.target){
853             cfg.target = this.target;
854         }
855         
856         return cfg;
857     },
858     initEvents: function() {
859        // Roo.log('init events?');
860 //        Roo.log(this.el.dom);
861         // add the menu...
862         
863         if (typeof (this.menu) != 'undefined') {
864             this.menu.parentType = this.xtype;
865             this.menu.triggerEl = this.el;
866             this.addxtype(Roo.apply({}, this.menu));
867         }
868
869
870        if (this.el.hasClass('roo-button')) {
871             this.el.on('click', this.onClick, this);
872        } else {
873             this.el.select('.roo-button').on('click', this.onClick, this);
874        }
875        
876        if(this.removeClass){
877            this.el.on('click', this.onClick, this);
878        }
879        
880        this.el.enableDisplayMode();
881         
882     },
883     onClick : function(e)
884     {
885         if (this.disabled) {
886             return;
887         }
888         
889         Roo.log('button on click ');
890         if(this.preventDefault){
891             e.preventDefault();
892         }
893         
894         if (this.pressed === true || this.pressed === false) {
895             this.toggleActive(e);
896         }
897         
898         
899         this.fireEvent('click', this, e);
900     },
901     
902     /**
903      * Enables this button
904      */
905     enable : function()
906     {
907         this.disabled = false;
908         this.el.removeClass('disabled');
909     },
910     
911     /**
912      * Disable this button
913      */
914     disable : function()
915     {
916         this.disabled = true;
917         this.el.addClass('disabled');
918     },
919      /**
920      * sets the active state on/off, 
921      * @param {Boolean} state (optional) Force a particular state
922      */
923     setActive : function(v) {
924         
925         this.el[v ? 'addClass' : 'removeClass']('active');
926         this.pressed = v;
927     },
928      /**
929      * toggles the current active state 
930      */
931     toggleActive : function(e)
932     {
933         this.setActive(!this.pressed);
934         this.fireEvent('toggle', this, e, !this.pressed);
935     },
936      /**
937      * get the current active state
938      * @return {boolean} true if it's active
939      */
940     isActive : function()
941     {
942         return this.el.hasClass('active');
943     },
944     /**
945      * set the text of the first selected button
946      */
947     setText : function(str)
948     {
949         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
950     },
951     /**
952      * get the text of the first selected button
953      */
954     getText : function()
955     {
956         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
957     },
958     
959     setWeight : function(str)
960     {
961         this.el.removeClass(this.weightClass);
962         this.weight = str;
963         var outline = this.outline ? 'outline-' : '';
964         if (str == 'default') {
965             this.el.addClass('btn-default btn-outline-secondary');        
966             return;
967         }
968         this.el.addClass('btn-' + outline + str);        
969     }
970     
971     
972 });
973
974  /*
975  * - LGPL
976  *
977  * column
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Column
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Column class
985  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
986  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
987  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
988  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
989  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
990  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
991  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
992  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
993  *
994  * 
995  * @cfg {Boolean} hidden (true|false) hide the element
996  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
997  * @cfg {String} fa (ban|check|...) font awesome icon
998  * @cfg {Number} fasize (1|2|....) font awsome size
999
1000  * @cfg {String} icon (info-sign|check|...) glyphicon name
1001
1002  * @cfg {String} html content of column.
1003  * 
1004  * @constructor
1005  * Create a new Column
1006  * @param {Object} config The config object
1007  */
1008
1009 Roo.bootstrap.Column = function(config){
1010     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1011 };
1012
1013 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1014     
1015     xs: false,
1016     sm: false,
1017     md: false,
1018     lg: false,
1019     xsoff: false,
1020     smoff: false,
1021     mdoff: false,
1022     lgoff: false,
1023     html: '',
1024     offset: 0,
1025     alert: false,
1026     fa: false,
1027     icon : false,
1028     hidden : false,
1029     fasize : 1,
1030     
1031     getAutoCreate : function(){
1032         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1033         
1034         cfg = {
1035             tag: 'div',
1036             cls: 'column'
1037         };
1038         
1039         var settings=this;
1040         ['xs','sm','md','lg'].map(function(size){
1041             //Roo.log( size + ':' + settings[size]);
1042             
1043             if (settings[size+'off'] !== false) {
1044                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1045             }
1046             
1047             if (settings[size] === false) {
1048                 return;
1049             }
1050             
1051             if (!settings[size]) { // 0 = hidden
1052                 cfg.cls += ' hidden-' + size;
1053                 return;
1054             }
1055             cfg.cls += ' col-' + size + '-' + settings[size];
1056             
1057         });
1058         
1059         if (this.hidden) {
1060             cfg.cls += ' hidden';
1061         }
1062         
1063         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1064             cfg.cls +=' alert alert-' + this.alert;
1065         }
1066         
1067         
1068         if (this.html.length) {
1069             cfg.html = this.html;
1070         }
1071         if (this.fa) {
1072             var fasize = '';
1073             if (this.fasize > 1) {
1074                 fasize = ' fa-' + this.fasize + 'x';
1075             }
1076             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1077             
1078             
1079         }
1080         if (this.icon) {
1081             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1082         }
1083         
1084         return cfg;
1085     }
1086    
1087 });
1088
1089  
1090
1091  /*
1092  * - LGPL
1093  *
1094  * page container.
1095  * 
1096  */
1097
1098
1099 /**
1100  * @class Roo.bootstrap.Container
1101  * @extends Roo.bootstrap.Component
1102  * Bootstrap Container class
1103  * @cfg {Boolean} jumbotron is it a jumbotron element
1104  * @cfg {String} html content of element
1105  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1106  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1107  * @cfg {String} header content of header (for panel)
1108  * @cfg {String} footer content of footer (for panel)
1109  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1110  * @cfg {String} tag (header|aside|section) type of HTML tag.
1111  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1112  * @cfg {String} fa font awesome icon
1113  * @cfg {String} icon (info-sign|check|...) glyphicon name
1114  * @cfg {Boolean} hidden (true|false) hide the element
1115  * @cfg {Boolean} expandable (true|false) default false
1116  * @cfg {Boolean} expanded (true|false) default true
1117  * @cfg {String} rheader contet on the right of header
1118  * @cfg {Boolean} clickable (true|false) default false
1119
1120  *     
1121  * @constructor
1122  * Create a new Container
1123  * @param {Object} config The config object
1124  */
1125
1126 Roo.bootstrap.Container = function(config){
1127     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1128     
1129     this.addEvents({
1130         // raw events
1131          /**
1132          * @event expand
1133          * After the panel has been expand
1134          * 
1135          * @param {Roo.bootstrap.Container} this
1136          */
1137         "expand" : true,
1138         /**
1139          * @event collapse
1140          * After the panel has been collapsed
1141          * 
1142          * @param {Roo.bootstrap.Container} this
1143          */
1144         "collapse" : true,
1145         /**
1146          * @event click
1147          * When a element is chick
1148          * @param {Roo.bootstrap.Container} this
1149          * @param {Roo.EventObject} e
1150          */
1151         "click" : true
1152     });
1153 };
1154
1155 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1156     
1157     jumbotron : false,
1158     well: '',
1159     panel : '',
1160     header: '',
1161     footer : '',
1162     sticky: '',
1163     tag : false,
1164     alert : false,
1165     fa: false,
1166     icon : false,
1167     expandable : false,
1168     rheader : '',
1169     expanded : true,
1170     clickable: false,
1171   
1172      
1173     getChildContainer : function() {
1174         
1175         if(!this.el){
1176             return false;
1177         }
1178         
1179         if (this.panel.length) {
1180             return this.el.select('.panel-body',true).first();
1181         }
1182         
1183         return this.el;
1184     },
1185     
1186     
1187     getAutoCreate : function(){
1188         
1189         var cfg = {
1190             tag : this.tag || 'div',
1191             html : '',
1192             cls : ''
1193         };
1194         if (this.jumbotron) {
1195             cfg.cls = 'jumbotron';
1196         }
1197         
1198         
1199         
1200         // - this is applied by the parent..
1201         //if (this.cls) {
1202         //    cfg.cls = this.cls + '';
1203         //}
1204         
1205         if (this.sticky.length) {
1206             
1207             var bd = Roo.get(document.body);
1208             if (!bd.hasClass('bootstrap-sticky')) {
1209                 bd.addClass('bootstrap-sticky');
1210                 Roo.select('html',true).setStyle('height', '100%');
1211             }
1212              
1213             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1214         }
1215         
1216         
1217         if (this.well.length) {
1218             switch (this.well) {
1219                 case 'lg':
1220                 case 'sm':
1221                     cfg.cls +=' well well-' +this.well;
1222                     break;
1223                 default:
1224                     cfg.cls +=' well';
1225                     break;
1226             }
1227         }
1228         
1229         if (this.hidden) {
1230             cfg.cls += ' hidden';
1231         }
1232         
1233         
1234         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1235             cfg.cls +=' alert alert-' + this.alert;
1236         }
1237         
1238         var body = cfg;
1239         
1240         if (this.panel.length) {
1241             cfg.cls += ' panel panel-' + this.panel;
1242             cfg.cn = [];
1243             if (this.header.length) {
1244                 
1245                 var h = [];
1246                 
1247                 if(this.expandable){
1248                     
1249                     cfg.cls = cfg.cls + ' expandable';
1250                     
1251                     h.push({
1252                         tag: 'i',
1253                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1254                     });
1255                     
1256                 }
1257                 
1258                 h.push(
1259                     {
1260                         tag: 'span',
1261                         cls : 'panel-title',
1262                         html : (this.expandable ? '&nbsp;' : '') + this.header
1263                     },
1264                     {
1265                         tag: 'span',
1266                         cls: 'panel-header-right',
1267                         html: this.rheader
1268                     }
1269                 );
1270                 
1271                 cfg.cn.push({
1272                     cls : 'panel-heading',
1273                     style : this.expandable ? 'cursor: pointer' : '',
1274                     cn : h
1275                 });
1276                 
1277             }
1278             
1279             body = false;
1280             cfg.cn.push({
1281                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1282                 html : this.html
1283             });
1284             
1285             
1286             if (this.footer.length) {
1287                 cfg.cn.push({
1288                     cls : 'panel-footer',
1289                     html : this.footer
1290                     
1291                 });
1292             }
1293             
1294         }
1295         
1296         if (body) {
1297             body.html = this.html || cfg.html;
1298             // prefix with the icons..
1299             if (this.fa) {
1300                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1301             }
1302             if (this.icon) {
1303                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1304             }
1305             
1306             
1307         }
1308         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1309             cfg.cls =  'container';
1310         }
1311         
1312         return cfg;
1313     },
1314     
1315     initEvents: function() 
1316     {
1317         if(this.expandable){
1318             var headerEl = this.headerEl();
1319         
1320             if(headerEl){
1321                 headerEl.on('click', this.onToggleClick, this);
1322             }
1323         }
1324         
1325         if(this.clickable){
1326             this.el.on('click', this.onClick, this);
1327         }
1328         
1329     },
1330     
1331     onToggleClick : function()
1332     {
1333         var headerEl = this.headerEl();
1334         
1335         if(!headerEl){
1336             return;
1337         }
1338         
1339         if(this.expanded){
1340             this.collapse();
1341             return;
1342         }
1343         
1344         this.expand();
1345     },
1346     
1347     expand : function()
1348     {
1349         if(this.fireEvent('expand', this)) {
1350             
1351             this.expanded = true;
1352             
1353             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1354             
1355             this.el.select('.panel-body',true).first().removeClass('hide');
1356             
1357             var toggleEl = this.toggleEl();
1358
1359             if(!toggleEl){
1360                 return;
1361             }
1362
1363             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1364         }
1365         
1366     },
1367     
1368     collapse : function()
1369     {
1370         if(this.fireEvent('collapse', this)) {
1371             
1372             this.expanded = false;
1373             
1374             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1375             this.el.select('.panel-body',true).first().addClass('hide');
1376         
1377             var toggleEl = this.toggleEl();
1378
1379             if(!toggleEl){
1380                 return;
1381             }
1382
1383             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1384         }
1385     },
1386     
1387     toggleEl : function()
1388     {
1389         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1390             return;
1391         }
1392         
1393         return this.el.select('.panel-heading .fa',true).first();
1394     },
1395     
1396     headerEl : function()
1397     {
1398         if(!this.el || !this.panel.length || !this.header.length){
1399             return;
1400         }
1401         
1402         return this.el.select('.panel-heading',true).first()
1403     },
1404     
1405     bodyEl : function()
1406     {
1407         if(!this.el || !this.panel.length){
1408             return;
1409         }
1410         
1411         return this.el.select('.panel-body',true).first()
1412     },
1413     
1414     titleEl : function()
1415     {
1416         if(!this.el || !this.panel.length || !this.header.length){
1417             return;
1418         }
1419         
1420         return this.el.select('.panel-title',true).first();
1421     },
1422     
1423     setTitle : function(v)
1424     {
1425         var titleEl = this.titleEl();
1426         
1427         if(!titleEl){
1428             return;
1429         }
1430         
1431         titleEl.dom.innerHTML = v;
1432     },
1433     
1434     getTitle : function()
1435     {
1436         
1437         var titleEl = this.titleEl();
1438         
1439         if(!titleEl){
1440             return '';
1441         }
1442         
1443         return titleEl.dom.innerHTML;
1444     },
1445     
1446     setRightTitle : function(v)
1447     {
1448         var t = this.el.select('.panel-header-right',true).first();
1449         
1450         if(!t){
1451             return;
1452         }
1453         
1454         t.dom.innerHTML = v;
1455     },
1456     
1457     onClick : function(e)
1458     {
1459         e.preventDefault();
1460         
1461         this.fireEvent('click', this, e);
1462     }
1463 });
1464
1465  /*
1466  * - LGPL
1467  *
1468  * image
1469  * 
1470  */
1471
1472
1473 /**
1474  * @class Roo.bootstrap.Img
1475  * @extends Roo.bootstrap.Component
1476  * Bootstrap Img class
1477  * @cfg {Boolean} imgResponsive false | true
1478  * @cfg {String} border rounded | circle | thumbnail
1479  * @cfg {String} src image source
1480  * @cfg {String} alt image alternative text
1481  * @cfg {String} href a tag href
1482  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1483  * @cfg {String} xsUrl xs image source
1484  * @cfg {String} smUrl sm image source
1485  * @cfg {String} mdUrl md image source
1486  * @cfg {String} lgUrl lg image source
1487  * 
1488  * @constructor
1489  * Create a new Input
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Img = function(config){
1494     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1495     
1496     this.addEvents({
1497         // img events
1498         /**
1499          * @event click
1500          * The img click event for the img.
1501          * @param {Roo.EventObject} e
1502          */
1503         "click" : true
1504     });
1505 };
1506
1507 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1508     
1509     imgResponsive: true,
1510     border: '',
1511     src: 'about:blank',
1512     href: false,
1513     target: false,
1514     xsUrl: '',
1515     smUrl: '',
1516     mdUrl: '',
1517     lgUrl: '',
1518
1519     getAutoCreate : function()
1520     {   
1521         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1522             return this.createSingleImg();
1523         }
1524         
1525         var cfg = {
1526             tag: 'div',
1527             cls: 'roo-image-responsive-group',
1528             cn: []
1529         };
1530         var _this = this;
1531         
1532         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1533             
1534             if(!_this[size + 'Url']){
1535                 return;
1536             }
1537             
1538             var img = {
1539                 tag: 'img',
1540                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1541                 html: _this.html || cfg.html,
1542                 src: _this[size + 'Url']
1543             };
1544             
1545             img.cls += ' roo-image-responsive-' + size;
1546             
1547             var s = ['xs', 'sm', 'md', 'lg'];
1548             
1549             s.splice(s.indexOf(size), 1);
1550             
1551             Roo.each(s, function(ss){
1552                 img.cls += ' hidden-' + ss;
1553             });
1554             
1555             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1556                 cfg.cls += ' img-' + _this.border;
1557             }
1558             
1559             if(_this.alt){
1560                 cfg.alt = _this.alt;
1561             }
1562             
1563             if(_this.href){
1564                 var a = {
1565                     tag: 'a',
1566                     href: _this.href,
1567                     cn: [
1568                         img
1569                     ]
1570                 };
1571
1572                 if(this.target){
1573                     a.target = _this.target;
1574                 }
1575             }
1576             
1577             cfg.cn.push((_this.href) ? a : img);
1578             
1579         });
1580         
1581         return cfg;
1582     },
1583     
1584     createSingleImg : function()
1585     {
1586         var cfg = {
1587             tag: 'img',
1588             cls: (this.imgResponsive) ? 'img-responsive' : '',
1589             html : null,
1590             src : 'about:blank'  // just incase src get's set to undefined?!?
1591         };
1592         
1593         cfg.html = this.html || cfg.html;
1594         
1595         cfg.src = this.src || cfg.src;
1596         
1597         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1598             cfg.cls += ' img-' + this.border;
1599         }
1600         
1601         if(this.alt){
1602             cfg.alt = this.alt;
1603         }
1604         
1605         if(this.href){
1606             var a = {
1607                 tag: 'a',
1608                 href: this.href,
1609                 cn: [
1610                     cfg
1611                 ]
1612             };
1613             
1614             if(this.target){
1615                 a.target = this.target;
1616             }
1617             
1618         }
1619         
1620         return (this.href) ? a : cfg;
1621     },
1622     
1623     initEvents: function() 
1624     {
1625         if(!this.href){
1626             this.el.on('click', this.onClick, this);
1627         }
1628         
1629     },
1630     
1631     onClick : function(e)
1632     {
1633         Roo.log('img onclick');
1634         this.fireEvent('click', this, e);
1635     },
1636     /**
1637      * Sets the url of the image - used to update it
1638      * @param {String} url the url of the image
1639      */
1640     
1641     setSrc : function(url)
1642     {
1643         this.src =  url;
1644         
1645         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1646             this.el.dom.src =  url;
1647             return;
1648         }
1649         
1650         this.el.select('img', true).first().dom.src =  url;
1651     }
1652     
1653     
1654    
1655 });
1656
1657  /*
1658  * - LGPL
1659  *
1660  * image
1661  * 
1662  */
1663
1664
1665 /**
1666  * @class Roo.bootstrap.Link
1667  * @extends Roo.bootstrap.Component
1668  * Bootstrap Link Class
1669  * @cfg {String} alt image alternative text
1670  * @cfg {String} href a tag href
1671  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1672  * @cfg {String} html the content of the link.
1673  * @cfg {String} anchor name for the anchor link
1674  * @cfg {String} fa - favicon
1675
1676  * @cfg {Boolean} preventDefault (true | false) default false
1677
1678  * 
1679  * @constructor
1680  * Create a new Input
1681  * @param {Object} config The config object
1682  */
1683
1684 Roo.bootstrap.Link = function(config){
1685     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1686     
1687     this.addEvents({
1688         // img events
1689         /**
1690          * @event click
1691          * The img click event for the img.
1692          * @param {Roo.EventObject} e
1693          */
1694         "click" : true
1695     });
1696 };
1697
1698 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1699     
1700     href: false,
1701     target: false,
1702     preventDefault: false,
1703     anchor : false,
1704     alt : false,
1705     fa: false,
1706
1707
1708     getAutoCreate : function()
1709     {
1710         var html = this.html || '';
1711         
1712         if (this.fa !== false) {
1713             html = '<i class="fa fa-' + this.fa + '"></i>';
1714         }
1715         var cfg = {
1716             tag: 'a'
1717         };
1718         // anchor's do not require html/href...
1719         if (this.anchor === false) {
1720             cfg.html = html;
1721             cfg.href = this.href || '#';
1722         } else {
1723             cfg.name = this.anchor;
1724             if (this.html !== false || this.fa !== false) {
1725                 cfg.html = html;
1726             }
1727             if (this.href !== false) {
1728                 cfg.href = this.href;
1729             }
1730         }
1731         
1732         if(this.alt !== false){
1733             cfg.alt = this.alt;
1734         }
1735         
1736         
1737         if(this.target !== false) {
1738             cfg.target = this.target;
1739         }
1740         
1741         return cfg;
1742     },
1743     
1744     initEvents: function() {
1745         
1746         if(!this.href || this.preventDefault){
1747             this.el.on('click', this.onClick, this);
1748         }
1749     },
1750     
1751     onClick : function(e)
1752     {
1753         if(this.preventDefault){
1754             e.preventDefault();
1755         }
1756         //Roo.log('img onclick');
1757         this.fireEvent('click', this, e);
1758     }
1759    
1760 });
1761
1762  /*
1763  * - LGPL
1764  *
1765  * header
1766  * 
1767  */
1768
1769 /**
1770  * @class Roo.bootstrap.Header
1771  * @extends Roo.bootstrap.Component
1772  * Bootstrap Header class
1773  * @cfg {String} html content of header
1774  * @cfg {Number} level (1|2|3|4|5|6) default 1
1775  * 
1776  * @constructor
1777  * Create a new Header
1778  * @param {Object} config The config object
1779  */
1780
1781
1782 Roo.bootstrap.Header  = function(config){
1783     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1784 };
1785
1786 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1787     
1788     //href : false,
1789     html : false,
1790     level : 1,
1791     
1792     
1793     
1794     getAutoCreate : function(){
1795         
1796         
1797         
1798         var cfg = {
1799             tag: 'h' + (1 *this.level),
1800             html: this.html || ''
1801         } ;
1802         
1803         return cfg;
1804     }
1805    
1806 });
1807
1808  
1809
1810  /*
1811  * Based on:
1812  * Ext JS Library 1.1.1
1813  * Copyright(c) 2006-2007, Ext JS, LLC.
1814  *
1815  * Originally Released Under LGPL - original licence link has changed is not relivant.
1816  *
1817  * Fork - LGPL
1818  * <script type="text/javascript">
1819  */
1820  
1821 /**
1822  * @class Roo.bootstrap.MenuMgr
1823  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1824  * @singleton
1825  */
1826 Roo.bootstrap.MenuMgr = function(){
1827    var menus, active, groups = {}, attached = false, lastShow = new Date();
1828
1829    // private - called when first menu is created
1830    function init(){
1831        menus = {};
1832        active = new Roo.util.MixedCollection();
1833        Roo.get(document).addKeyListener(27, function(){
1834            if(active.length > 0){
1835                hideAll();
1836            }
1837        });
1838    }
1839
1840    // private
1841    function hideAll(){
1842        if(active && active.length > 0){
1843            var c = active.clone();
1844            c.each(function(m){
1845                m.hide();
1846            });
1847        }
1848    }
1849
1850    // private
1851    function onHide(m){
1852        active.remove(m);
1853        if(active.length < 1){
1854            Roo.get(document).un("mouseup", onMouseDown);
1855             
1856            attached = false;
1857        }
1858    }
1859
1860    // private
1861    function onShow(m){
1862        var last = active.last();
1863        lastShow = new Date();
1864        active.add(m);
1865        if(!attached){
1866           Roo.get(document).on("mouseup", onMouseDown);
1867            
1868            attached = true;
1869        }
1870        if(m.parentMenu){
1871           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1872           m.parentMenu.activeChild = m;
1873        }else if(last && last.isVisible()){
1874           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1875        }
1876    }
1877
1878    // private
1879    function onBeforeHide(m){
1880        if(m.activeChild){
1881            m.activeChild.hide();
1882        }
1883        if(m.autoHideTimer){
1884            clearTimeout(m.autoHideTimer);
1885            delete m.autoHideTimer;
1886        }
1887    }
1888
1889    // private
1890    function onBeforeShow(m){
1891        var pm = m.parentMenu;
1892        if(!pm && !m.allowOtherMenus){
1893            hideAll();
1894        }else if(pm && pm.activeChild && active != m){
1895            pm.activeChild.hide();
1896        }
1897    }
1898
1899    // private this should really trigger on mouseup..
1900    function onMouseDown(e){
1901         Roo.log("on Mouse Up");
1902         
1903         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1904             Roo.log("MenuManager hideAll");
1905             hideAll();
1906             e.stopEvent();
1907         }
1908         
1909         
1910    }
1911
1912    // private
1913    function onBeforeCheck(mi, state){
1914        if(state){
1915            var g = groups[mi.group];
1916            for(var i = 0, l = g.length; i < l; i++){
1917                if(g[i] != mi){
1918                    g[i].setChecked(false);
1919                }
1920            }
1921        }
1922    }
1923
1924    return {
1925
1926        /**
1927         * Hides all menus that are currently visible
1928         */
1929        hideAll : function(){
1930             hideAll();  
1931        },
1932
1933        // private
1934        register : function(menu){
1935            if(!menus){
1936                init();
1937            }
1938            menus[menu.id] = menu;
1939            menu.on("beforehide", onBeforeHide);
1940            menu.on("hide", onHide);
1941            menu.on("beforeshow", onBeforeShow);
1942            menu.on("show", onShow);
1943            var g = menu.group;
1944            if(g && menu.events["checkchange"]){
1945                if(!groups[g]){
1946                    groups[g] = [];
1947                }
1948                groups[g].push(menu);
1949                menu.on("checkchange", onCheck);
1950            }
1951        },
1952
1953         /**
1954          * Returns a {@link Roo.menu.Menu} object
1955          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1956          * be used to generate and return a new Menu instance.
1957          */
1958        get : function(menu){
1959            if(typeof menu == "string"){ // menu id
1960                return menus[menu];
1961            }else if(menu.events){  // menu instance
1962                return menu;
1963            }
1964            /*else if(typeof menu.length == 'number'){ // array of menu items?
1965                return new Roo.bootstrap.Menu({items:menu});
1966            }else{ // otherwise, must be a config
1967                return new Roo.bootstrap.Menu(menu);
1968            }
1969            */
1970            return false;
1971        },
1972
1973        // private
1974        unregister : function(menu){
1975            delete menus[menu.id];
1976            menu.un("beforehide", onBeforeHide);
1977            menu.un("hide", onHide);
1978            menu.un("beforeshow", onBeforeShow);
1979            menu.un("show", onShow);
1980            var g = menu.group;
1981            if(g && menu.events["checkchange"]){
1982                groups[g].remove(menu);
1983                menu.un("checkchange", onCheck);
1984            }
1985        },
1986
1987        // private
1988        registerCheckable : function(menuItem){
1989            var g = menuItem.group;
1990            if(g){
1991                if(!groups[g]){
1992                    groups[g] = [];
1993                }
1994                groups[g].push(menuItem);
1995                menuItem.on("beforecheckchange", onBeforeCheck);
1996            }
1997        },
1998
1999        // private
2000        unregisterCheckable : function(menuItem){
2001            var g = menuItem.group;
2002            if(g){
2003                groups[g].remove(menuItem);
2004                menuItem.un("beforecheckchange", onBeforeCheck);
2005            }
2006        }
2007    };
2008 }();/*
2009  * - LGPL
2010  *
2011  * menu
2012  * 
2013  */
2014
2015 /**
2016  * @class Roo.bootstrap.Menu
2017  * @extends Roo.bootstrap.Component
2018  * Bootstrap Menu class - container for MenuItems
2019  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2020  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2021  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2022  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2023  * 
2024  * @constructor
2025  * Create a new Menu
2026  * @param {Object} config The config object
2027  */
2028
2029
2030 Roo.bootstrap.Menu = function(config){
2031     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2032     if (this.registerMenu && this.type != 'treeview')  {
2033         Roo.bootstrap.MenuMgr.register(this);
2034     }
2035     
2036     
2037     this.addEvents({
2038         /**
2039          * @event beforeshow
2040          * Fires before this menu is displayed
2041          * @param {Roo.menu.Menu} this
2042          */
2043         beforeshow : true,
2044         /**
2045          * @event beforehide
2046          * Fires before this menu is hidden
2047          * @param {Roo.menu.Menu} this
2048          */
2049         beforehide : true,
2050         /**
2051          * @event show
2052          * Fires after this menu is displayed
2053          * @param {Roo.menu.Menu} this
2054          */
2055         show : true,
2056         /**
2057          * @event hide
2058          * Fires after this menu is hidden
2059          * @param {Roo.menu.Menu} this
2060          */
2061         hide : true,
2062         /**
2063          * @event click
2064          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2065          * @param {Roo.menu.Menu} this
2066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2067          * @param {Roo.EventObject} e
2068          */
2069         click : true,
2070         /**
2071          * @event mouseover
2072          * Fires when the mouse is hovering over this menu
2073          * @param {Roo.menu.Menu} this
2074          * @param {Roo.EventObject} e
2075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2076          */
2077         mouseover : true,
2078         /**
2079          * @event mouseout
2080          * Fires when the mouse exits this menu
2081          * @param {Roo.menu.Menu} this
2082          * @param {Roo.EventObject} e
2083          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2084          */
2085         mouseout : true,
2086         /**
2087          * @event itemclick
2088          * Fires when a menu item contained in this menu is clicked
2089          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         itemclick: true
2093     });
2094     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2095 };
2096
2097 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2098     
2099    /// html : false,
2100     //align : '',
2101     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2102     type: false,
2103     /**
2104      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2105      */
2106     registerMenu : true,
2107     
2108     menuItems :false, // stores the menu items..
2109     
2110     hidden:true,
2111         
2112     parentMenu : false,
2113     
2114     stopEvent : true,
2115     
2116     isLink : false,
2117     
2118     getChildContainer : function() {
2119         return this.el;  
2120     },
2121     
2122     getAutoCreate : function(){
2123          
2124         //if (['right'].indexOf(this.align)!==-1) {
2125         //    cfg.cn[1].cls += ' pull-right'
2126         //}
2127         
2128         
2129         var cfg = {
2130             tag : 'ul',
2131             cls : 'dropdown-menu' ,
2132             style : 'z-index:1000'
2133             
2134         };
2135         
2136         if (this.type === 'submenu') {
2137             cfg.cls = 'submenu active';
2138         }
2139         if (this.type === 'treeview') {
2140             cfg.cls = 'treeview-menu';
2141         }
2142         
2143         return cfg;
2144     },
2145     initEvents : function() {
2146         
2147        // Roo.log("ADD event");
2148        // Roo.log(this.triggerEl.dom);
2149         
2150         this.triggerEl.on('click', this.onTriggerClick, this);
2151         
2152         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2153         
2154         
2155         if (this.triggerEl.hasClass('nav-item')) {
2156             // dropdown toggle on the 'a' in BS4?
2157             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2158         } else {
2159             this.triggerEl.addClass('dropdown-toggle');
2160         }
2161         if (Roo.isTouch) {
2162             this.el.on('touchstart'  , this.onTouch, this);
2163         }
2164         this.el.on('click' , this.onClick, this);
2165
2166         this.el.on("mouseover", this.onMouseOver, this);
2167         this.el.on("mouseout", this.onMouseOut, this);
2168         
2169     },
2170     
2171     findTargetItem : function(e)
2172     {
2173         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2174         if(!t){
2175             return false;
2176         }
2177         //Roo.log(t);         Roo.log(t.id);
2178         if(t && t.id){
2179             //Roo.log(this.menuitems);
2180             return this.menuitems.get(t.id);
2181             
2182             //return this.items.get(t.menuItemId);
2183         }
2184         
2185         return false;
2186     },
2187     
2188     onTouch : function(e) 
2189     {
2190         Roo.log("menu.onTouch");
2191         //e.stopEvent(); this make the user popdown broken
2192         this.onClick(e);
2193     },
2194     
2195     onClick : function(e)
2196     {
2197         Roo.log("menu.onClick");
2198         
2199         var t = this.findTargetItem(e);
2200         if(!t || t.isContainer){
2201             return;
2202         }
2203         Roo.log(e);
2204         /*
2205         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2206             if(t == this.activeItem && t.shouldDeactivate(e)){
2207                 this.activeItem.deactivate();
2208                 delete this.activeItem;
2209                 return;
2210             }
2211             if(t.canActivate){
2212                 this.setActiveItem(t, true);
2213             }
2214             return;
2215             
2216             
2217         }
2218         */
2219        
2220         Roo.log('pass click event');
2221         
2222         t.onClick(e);
2223         
2224         this.fireEvent("click", this, t, e);
2225         
2226         var _this = this;
2227         
2228         if(!t.href.length || t.href == '#'){
2229             (function() { _this.hide(); }).defer(100);
2230         }
2231         
2232     },
2233     
2234     onMouseOver : function(e){
2235         var t  = this.findTargetItem(e);
2236         //Roo.log(t);
2237         //if(t){
2238         //    if(t.canActivate && !t.disabled){
2239         //        this.setActiveItem(t, true);
2240         //    }
2241         //}
2242         
2243         this.fireEvent("mouseover", this, e, t);
2244     },
2245     isVisible : function(){
2246         return !this.hidden;
2247     },
2248      onMouseOut : function(e){
2249         var t  = this.findTargetItem(e);
2250         
2251         //if(t ){
2252         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2253         //        this.activeItem.deactivate();
2254         //        delete this.activeItem;
2255         //    }
2256         //}
2257         this.fireEvent("mouseout", this, e, t);
2258     },
2259     
2260     
2261     /**
2262      * Displays this menu relative to another element
2263      * @param {String/HTMLElement/Roo.Element} element The element to align to
2264      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2265      * the element (defaults to this.defaultAlign)
2266      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2267      */
2268     show : function(el, pos, parentMenu){
2269         this.parentMenu = parentMenu;
2270         if(!this.el){
2271             this.render();
2272         }
2273         this.fireEvent("beforeshow", this);
2274         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2275     },
2276      /**
2277      * Displays this menu at a specific xy position
2278      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2279      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2280      */
2281     showAt : function(xy, parentMenu, /* private: */_e){
2282         this.parentMenu = parentMenu;
2283         if(!this.el){
2284             this.render();
2285         }
2286         if(_e !== false){
2287             this.fireEvent("beforeshow", this);
2288             //xy = this.el.adjustForConstraints(xy);
2289         }
2290         
2291         //this.el.show();
2292         this.hideMenuItems();
2293         this.hidden = false;
2294         this.triggerEl.addClass('open');
2295         this.el.addClass('show');
2296         
2297         // reassign x when hitting right
2298         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2299             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2300         }
2301         
2302         // reassign y when hitting bottom
2303         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2304             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2305         }
2306         
2307         // but the list may align on trigger left or trigger top... should it be a properity?
2308         
2309         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2310             this.el.setXY(xy);
2311         }
2312         
2313         this.focus();
2314         this.fireEvent("show", this);
2315     },
2316     
2317     focus : function(){
2318         return;
2319         if(!this.hidden){
2320             this.doFocus.defer(50, this);
2321         }
2322     },
2323
2324     doFocus : function(){
2325         if(!this.hidden){
2326             this.focusEl.focus();
2327         }
2328     },
2329
2330     /**
2331      * Hides this menu and optionally all parent menus
2332      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2333      */
2334     hide : function(deep)
2335     {
2336         
2337         this.hideMenuItems();
2338         if(this.el && this.isVisible()){
2339             this.fireEvent("beforehide", this);
2340             if(this.activeItem){
2341                 this.activeItem.deactivate();
2342                 this.activeItem = null;
2343             }
2344             this.triggerEl.removeClass('open');;
2345             this.el.removeClass('show');
2346             this.hidden = true;
2347             this.fireEvent("hide", this);
2348         }
2349         if(deep === true && this.parentMenu){
2350             this.parentMenu.hide(true);
2351         }
2352     },
2353     
2354     onTriggerClick : function(e)
2355     {
2356         Roo.log('trigger click');
2357         
2358         var target = e.getTarget();
2359         
2360         Roo.log(target.nodeName.toLowerCase());
2361         
2362         if(target.nodeName.toLowerCase() === 'i'){
2363             e.preventDefault();
2364         }
2365         
2366     },
2367     
2368     onTriggerPress  : function(e)
2369     {
2370         Roo.log('trigger press');
2371         //Roo.log(e.getTarget());
2372        // Roo.log(this.triggerEl.dom);
2373        
2374         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2375         var pel = Roo.get(e.getTarget());
2376         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2377             Roo.log('is treeview or dropdown?');
2378             return;
2379         }
2380         
2381         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2382             return;
2383         }
2384         
2385         if (this.isVisible()) {
2386             Roo.log('hide');
2387             this.hide();
2388         } else {
2389             Roo.log('show');
2390             this.show(this.triggerEl, false, false);
2391         }
2392         
2393         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2394             e.stopEvent();
2395         }
2396         
2397     },
2398        
2399     
2400     hideMenuItems : function()
2401     {
2402         Roo.log("hide Menu Items");
2403         if (!this.el) { 
2404             return;
2405         }
2406         //$(backdrop).remove()
2407         this.el.select('.open',true).each(function(aa) {
2408             
2409             aa.removeClass('open');
2410           //var parent = getParent($(this))
2411           //var relatedTarget = { relatedTarget: this }
2412           
2413            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2414           //if (e.isDefaultPrevented()) return
2415            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2416         });
2417     },
2418     addxtypeChild : function (tree, cntr) {
2419         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2420           
2421         this.menuitems.add(comp);
2422         return comp;
2423
2424     },
2425     getEl : function()
2426     {
2427         Roo.log(this.el);
2428         return this.el;
2429     },
2430     
2431     clear : function()
2432     {
2433         this.getEl().dom.innerHTML = '';
2434         this.menuitems.clear();
2435     }
2436 });
2437
2438  
2439  /*
2440  * - LGPL
2441  *
2442  * menu item
2443  * 
2444  */
2445
2446
2447 /**
2448  * @class Roo.bootstrap.MenuItem
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap MenuItem class
2451  * @cfg {String} html the menu label
2452  * @cfg {String} href the link
2453  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2454  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2455  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2456  * @cfg {String} fa favicon to show on left of menu item.
2457  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2458  * 
2459  * 
2460  * @constructor
2461  * Create a new MenuItem
2462  * @param {Object} config The config object
2463  */
2464
2465
2466 Roo.bootstrap.MenuItem = function(config){
2467     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2468     this.addEvents({
2469         // raw events
2470         /**
2471          * @event click
2472          * The raw click event for the entire grid.
2473          * @param {Roo.bootstrap.MenuItem} this
2474          * @param {Roo.EventObject} e
2475          */
2476         "click" : true
2477     });
2478 };
2479
2480 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2481     
2482     href : false,
2483     html : false,
2484     preventDefault: false,
2485     isContainer : false,
2486     active : false,
2487     fa: false,
2488     
2489     getAutoCreate : function(){
2490         
2491         if(this.isContainer){
2492             return {
2493                 tag: 'li',
2494                 cls: 'dropdown-menu-item dropdown-item'
2495             };
2496         }
2497         var ctag = {
2498             tag: 'span',
2499             html: 'Link'
2500         };
2501         
2502         var anc = {
2503             tag : 'a',
2504             href : '#',
2505             cn : [  ]
2506         };
2507         
2508         if (this.fa !== false) {
2509             anc.cn.push({
2510                 tag : 'i',
2511                 cls : 'fa fa-' + this.fa
2512             });
2513         }
2514         
2515         anc.cn.push(ctag);
2516         
2517         
2518         var cfg= {
2519             tag: 'li',
2520             cls: 'dropdown-menu-item dropdown-item',
2521             cn: [ anc ]
2522         };
2523         if (this.parent().type == 'treeview') {
2524             cfg.cls = 'treeview-menu';
2525         }
2526         if (this.active) {
2527             cfg.cls += ' active';
2528         }
2529         
2530         
2531         
2532         anc.href = this.href || cfg.cn[0].href ;
2533         ctag.html = this.html || cfg.cn[0].html ;
2534         return cfg;
2535     },
2536     
2537     initEvents: function()
2538     {
2539         if (this.parent().type == 'treeview') {
2540             this.el.select('a').on('click', this.onClick, this);
2541         }
2542         
2543         if (this.menu) {
2544             this.menu.parentType = this.xtype;
2545             this.menu.triggerEl = this.el;
2546             this.menu = this.addxtype(Roo.apply({}, this.menu));
2547         }
2548         
2549     },
2550     onClick : function(e)
2551     {
2552         Roo.log('item on click ');
2553         
2554         if(this.preventDefault){
2555             e.preventDefault();
2556         }
2557         //this.parent().hideMenuItems();
2558         
2559         this.fireEvent('click', this, e);
2560     },
2561     getEl : function()
2562     {
2563         return this.el;
2564     } 
2565 });
2566
2567  
2568
2569  /*
2570  * - LGPL
2571  *
2572  * menu separator
2573  * 
2574  */
2575
2576
2577 /**
2578  * @class Roo.bootstrap.MenuSeparator
2579  * @extends Roo.bootstrap.Component
2580  * Bootstrap MenuSeparator class
2581  * 
2582  * @constructor
2583  * Create a new MenuItem
2584  * @param {Object} config The config object
2585  */
2586
2587
2588 Roo.bootstrap.MenuSeparator = function(config){
2589     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2590 };
2591
2592 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2593     
2594     getAutoCreate : function(){
2595         var cfg = {
2596             cls: 'divider',
2597             tag : 'li'
2598         };
2599         
2600         return cfg;
2601     }
2602    
2603 });
2604
2605  
2606
2607  
2608 /*
2609 * Licence: LGPL
2610 */
2611
2612 /**
2613  * @class Roo.bootstrap.Modal
2614  * @extends Roo.bootstrap.Component
2615  * Bootstrap Modal class
2616  * @cfg {String} title Title of dialog
2617  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2618  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2619  * @cfg {Boolean} specificTitle default false
2620  * @cfg {Array} buttons Array of buttons or standard button set..
2621  * @cfg {String} buttonPosition (left|right|center) default right
2622  * @cfg {Boolean} animate default true
2623  * @cfg {Boolean} allow_close default true
2624  * @cfg {Boolean} fitwindow default false
2625  * @cfg {String} size (sm|lg) default empty
2626  * @cfg {Number} max_width set the max width of modal
2627  *
2628  *
2629  * @constructor
2630  * Create a new Modal Dialog
2631  * @param {Object} config The config object
2632  */
2633
2634 Roo.bootstrap.Modal = function(config){
2635     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2636     this.addEvents({
2637         // raw events
2638         /**
2639          * @event btnclick
2640          * The raw btnclick event for the button
2641          * @param {Roo.EventObject} e
2642          */
2643         "btnclick" : true,
2644         /**
2645          * @event resize
2646          * Fire when dialog resize
2647          * @param {Roo.bootstrap.Modal} this
2648          * @param {Roo.EventObject} e
2649          */
2650         "resize" : true
2651     });
2652     this.buttons = this.buttons || [];
2653
2654     if (this.tmpl) {
2655         this.tmpl = Roo.factory(this.tmpl);
2656     }
2657
2658 };
2659
2660 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2661
2662     title : 'test dialog',
2663
2664     buttons : false,
2665
2666     // set on load...
2667
2668     html: false,
2669
2670     tmp: false,
2671
2672     specificTitle: false,
2673
2674     buttonPosition: 'right',
2675
2676     allow_close : true,
2677
2678     animate : true,
2679
2680     fitwindow: false,
2681     
2682      // private
2683     dialogEl: false,
2684     bodyEl:  false,
2685     footerEl:  false,
2686     titleEl:  false,
2687     closeEl:  false,
2688
2689     size: '',
2690     
2691     max_width: 0,
2692     
2693     max_height: 0,
2694     
2695     fit_content: false,
2696
2697     onRender : function(ct, position)
2698     {
2699         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2700
2701         if(!this.el){
2702             var cfg = Roo.apply({},  this.getAutoCreate());
2703             cfg.id = Roo.id();
2704             //if(!cfg.name){
2705             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2706             //}
2707             //if (!cfg.name.length) {
2708             //    delete cfg.name;
2709            // }
2710             if (this.cls) {
2711                 cfg.cls += ' ' + this.cls;
2712             }
2713             if (this.style) {
2714                 cfg.style = this.style;
2715             }
2716             this.el = Roo.get(document.body).createChild(cfg, position);
2717         }
2718         //var type = this.el.dom.type;
2719
2720
2721         if(this.tabIndex !== undefined){
2722             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2723         }
2724
2725         this.dialogEl = this.el.select('.modal-dialog',true).first();
2726         this.bodyEl = this.el.select('.modal-body',true).first();
2727         this.closeEl = this.el.select('.modal-header .close', true).first();
2728         this.headerEl = this.el.select('.modal-header',true).first();
2729         this.titleEl = this.el.select('.modal-title',true).first();
2730         this.footerEl = this.el.select('.modal-footer',true).first();
2731
2732         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2733         
2734         //this.el.addClass("x-dlg-modal");
2735
2736         if (this.buttons.length) {
2737             Roo.each(this.buttons, function(bb) {
2738                 var b = Roo.apply({}, bb);
2739                 b.xns = b.xns || Roo.bootstrap;
2740                 b.xtype = b.xtype || 'Button';
2741                 if (typeof(b.listeners) == 'undefined') {
2742                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2743                 }
2744
2745                 var btn = Roo.factory(b);
2746
2747                 btn.render(this.el.select('.modal-footer div').first());
2748
2749             },this);
2750         }
2751         // render the children.
2752         var nitems = [];
2753
2754         if(typeof(this.items) != 'undefined'){
2755             var items = this.items;
2756             delete this.items;
2757
2758             for(var i =0;i < items.length;i++) {
2759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2760             }
2761         }
2762
2763         this.items = nitems;
2764
2765         // where are these used - they used to be body/close/footer
2766
2767
2768         this.initEvents();
2769         //this.el.addClass([this.fieldClass, this.cls]);
2770
2771     },
2772
2773     getAutoCreate : function()
2774     {
2775         var bdy = {
2776                 cls : 'modal-body',
2777                 html : this.html || ''
2778         };
2779
2780         var title = {
2781             tag: 'h4',
2782             cls : 'modal-title',
2783             html : this.title
2784         };
2785
2786         if(this.specificTitle){
2787             title = this.title;
2788
2789         };
2790
2791         var header = [];
2792         if (this.allow_close && Roo.bootstrap.version == 3) {
2793             header.push({
2794                 tag: 'button',
2795                 cls : 'close',
2796                 html : '&times'
2797             });
2798         }
2799
2800         header.push(title);
2801
2802         if (this.allow_close && Roo.bootstrap.version == 4) {
2803             header.push({
2804                 tag: 'button',
2805                 cls : 'close',
2806                 html : '&times'
2807             });
2808         }
2809         
2810         var size = '';
2811
2812         if(this.size.length){
2813             size = 'modal-' + this.size;
2814         }
2815
2816         var modal = {
2817             cls: "modal",
2818              cn : [
2819                 {
2820                     cls: "modal-dialog " + size,
2821                     cn : [
2822                         {
2823                             cls : "modal-content",
2824                             cn : [
2825                                 {
2826                                     cls : 'modal-header',
2827                                     cn : header
2828                                 },
2829                                 bdy,
2830                                 {
2831                                     cls : 'modal-footer',
2832                                     cn : [
2833                                         {
2834                                             tag: 'div',
2835                                             cls: 'btn-' + this.buttonPosition
2836                                         }
2837                                     ]
2838
2839                                 }
2840
2841
2842                             ]
2843
2844                         }
2845                     ]
2846
2847                 }
2848             ]
2849         };
2850
2851         if(this.animate){
2852             modal.cls += ' fade';
2853         }
2854
2855         return modal;
2856
2857     },
2858     getChildContainer : function() {
2859
2860          return this.bodyEl;
2861
2862     },
2863     getButtonContainer : function() {
2864          return this.el.select('.modal-footer div',true).first();
2865
2866     },
2867     initEvents : function()
2868     {
2869         if (this.allow_close) {
2870             this.closeEl.on('click', this.hide, this);
2871         }
2872         Roo.EventManager.onWindowResize(this.resize, this, true);
2873
2874
2875     },
2876
2877     resize : function()
2878     {
2879         this.maskEl.setSize(
2880             Roo.lib.Dom.getViewWidth(true),
2881             Roo.lib.Dom.getViewHeight(true)
2882         );
2883         
2884         if (this.fitwindow) {
2885             this.setSize(
2886                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2887                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2888             );
2889             return;
2890         }
2891         
2892         if(this.max_width !== 0) {
2893             
2894             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2895             
2896             if(this.height) {
2897                 this.setSize(w, this.height);
2898                 return;
2899             }
2900             
2901             if(this.max_height) {
2902                 this.setSize(w,Math.min(
2903                     this.max_height,
2904                     Roo.lib.Dom.getViewportHeight(true) - 60
2905                 ));
2906                 
2907                 return;
2908             }
2909             
2910             if(!this.fit_content) {
2911                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2912                 return;
2913             }
2914             
2915             this.setSize(w, Math.min(
2916                 60 +
2917                 this.headerEl.getHeight() + 
2918                 this.footerEl.getHeight() + 
2919                 this.getChildHeight(this.bodyEl.dom.childNodes),
2920                 Roo.lib.Dom.getViewportHeight(true) - 60)
2921             );
2922         }
2923         
2924     },
2925
2926     setSize : function(w,h)
2927     {
2928         if (!w && !h) {
2929             return;
2930         }
2931         
2932         this.resizeTo(w,h);
2933     },
2934
2935     show : function() {
2936
2937         if (!this.rendered) {
2938             this.render();
2939         }
2940
2941         //this.el.setStyle('display', 'block');
2942         this.el.removeClass('hideing');
2943         this.el.dom.style.display='block';
2944         
2945         Roo.get(document.body).addClass('modal-open');
2946  
2947         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2948             var _this = this;
2949             (function(){
2950                 this.el.addClass('show');
2951                 this.el.addClass('in');
2952             }).defer(50, this);
2953         }else{
2954             this.el.addClass('show');
2955             this.el.addClass('in');
2956         }
2957
2958         // not sure how we can show data in here..
2959         //if (this.tmpl) {
2960         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2961         //}
2962
2963         Roo.get(document.body).addClass("x-body-masked");
2964         
2965         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2966         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2967         this.maskEl.dom.style.display = 'block';
2968         this.maskEl.addClass('show');
2969         
2970         
2971         this.resize();
2972         
2973         this.fireEvent('show', this);
2974
2975         // set zindex here - otherwise it appears to be ignored...
2976         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2977
2978         (function () {
2979             this.items.forEach( function(e) {
2980                 e.layout ? e.layout() : false;
2981
2982             });
2983         }).defer(100,this);
2984
2985     },
2986     hide : function()
2987     {
2988         if(this.fireEvent("beforehide", this) !== false){
2989             
2990             this.maskEl.removeClass('show');
2991             
2992             this.maskEl.dom.style.display = '';
2993             Roo.get(document.body).removeClass("x-body-masked");
2994             this.el.removeClass('in');
2995             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2996
2997             if(this.animate){ // why
2998                 this.el.addClass('hideing');
2999                 this.el.removeClass('show');
3000                 (function(){
3001                     if (!this.el.hasClass('hideing')) {
3002                         return; // it's been shown again...
3003                     }
3004                     
3005                     this.el.dom.style.display='';
3006
3007                     Roo.get(document.body).removeClass('modal-open');
3008                     this.el.removeClass('hideing');
3009                 }).defer(150,this);
3010                 
3011             }else{
3012                 this.el.removeClass('show');
3013                 this.el.dom.style.display='';
3014                 Roo.get(document.body).removeClass('modal-open');
3015
3016             }
3017             this.fireEvent('hide', this);
3018         }
3019     },
3020     isVisible : function()
3021     {
3022         
3023         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3024         
3025     },
3026
3027     addButton : function(str, cb)
3028     {
3029
3030
3031         var b = Roo.apply({}, { html : str } );
3032         b.xns = b.xns || Roo.bootstrap;
3033         b.xtype = b.xtype || 'Button';
3034         if (typeof(b.listeners) == 'undefined') {
3035             b.listeners = { click : cb.createDelegate(this)  };
3036         }
3037
3038         var btn = Roo.factory(b);
3039
3040         btn.render(this.el.select('.modal-footer div').first());
3041
3042         return btn;
3043
3044     },
3045
3046     setDefaultButton : function(btn)
3047     {
3048         //this.el.select('.modal-footer').()
3049     },
3050     diff : false,
3051
3052     resizeTo: function(w,h)
3053     {
3054         // skip.. ?? why??
3055
3056         this.dialogEl.setWidth(w);
3057         if (this.diff === false) {
3058             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3059         }
3060
3061         this.bodyEl.setHeight(h - this.diff);
3062
3063         this.fireEvent('resize', this);
3064
3065     },
3066     setContentSize  : function(w, h)
3067     {
3068
3069     },
3070     onButtonClick: function(btn,e)
3071     {
3072         //Roo.log([a,b,c]);
3073         this.fireEvent('btnclick', btn.name, e);
3074     },
3075      /**
3076      * Set the title of the Dialog
3077      * @param {String} str new Title
3078      */
3079     setTitle: function(str) {
3080         this.titleEl.dom.innerHTML = str;
3081     },
3082     /**
3083      * Set the body of the Dialog
3084      * @param {String} str new Title
3085      */
3086     setBody: function(str) {
3087         this.bodyEl.dom.innerHTML = str;
3088     },
3089     /**
3090      * Set the body of the Dialog using the template
3091      * @param {Obj} data - apply this data to the template and replace the body contents.
3092      */
3093     applyBody: function(obj)
3094     {
3095         if (!this.tmpl) {
3096             Roo.log("Error - using apply Body without a template");
3097             //code
3098         }
3099         this.tmpl.overwrite(this.bodyEl, obj);
3100     },
3101     
3102     getChildHeight : function(child_nodes)
3103     {
3104         if(
3105             !child_nodes ||
3106             child_nodes.length == 0
3107         ) {
3108             return;
3109         }
3110         
3111         var child_height = 0;
3112         
3113         for(var i = 0; i < child_nodes.length; i++) {
3114             
3115             /*
3116             * for modal with tabs...
3117             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3118                 
3119                 var layout_childs = child_nodes[i].childNodes;
3120                 
3121                 for(var j = 0; j < layout_childs.length; j++) {
3122                     
3123                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3124                         
3125                         var layout_body_childs = layout_childs[j].childNodes;
3126                         
3127                         for(var k = 0; k < layout_body_childs.length; k++) {
3128                             
3129                             if(layout_body_childs[k].classList.contains('navbar')) {
3130                                 child_height += layout_body_childs[k].offsetHeight;
3131                                 continue;
3132                             }
3133                             
3134                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3135                                 
3136                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3137                                 
3138                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3139                                     
3140                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3141                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3142                                         continue;
3143                                     }
3144                                     
3145                                 }
3146                                 
3147                             }
3148                             
3149                         }
3150                     }
3151                 }
3152                 continue;
3153             }
3154             */
3155             
3156             child_height += child_nodes[i].offsetHeight;
3157             // Roo.log(child_nodes[i].offsetHeight);
3158         }
3159         
3160         return child_height;
3161     }
3162
3163 });
3164
3165
3166 Roo.apply(Roo.bootstrap.Modal,  {
3167     /**
3168          * Button config that displays a single OK button
3169          * @type Object
3170          */
3171         OK :  [{
3172             name : 'ok',
3173             weight : 'primary',
3174             html : 'OK'
3175         }],
3176         /**
3177          * Button config that displays Yes and No buttons
3178          * @type Object
3179          */
3180         YESNO : [
3181             {
3182                 name  : 'no',
3183                 html : 'No'
3184             },
3185             {
3186                 name  :'yes',
3187                 weight : 'primary',
3188                 html : 'Yes'
3189             }
3190         ],
3191
3192         /**
3193          * Button config that displays OK and Cancel buttons
3194          * @type Object
3195          */
3196         OKCANCEL : [
3197             {
3198                name : 'cancel',
3199                 html : 'Cancel'
3200             },
3201             {
3202                 name : 'ok',
3203                 weight : 'primary',
3204                 html : 'OK'
3205             }
3206         ],
3207         /**
3208          * Button config that displays Yes, No and Cancel buttons
3209          * @type Object
3210          */
3211         YESNOCANCEL : [
3212             {
3213                 name : 'yes',
3214                 weight : 'primary',
3215                 html : 'Yes'
3216             },
3217             {
3218                 name : 'no',
3219                 html : 'No'
3220             },
3221             {
3222                 name : 'cancel',
3223                 html : 'Cancel'
3224             }
3225         ],
3226         
3227         zIndex : 10001
3228 });
3229 /*
3230  * - LGPL
3231  *
3232  * messagebox - can be used as a replace
3233  * 
3234  */
3235 /**
3236  * @class Roo.MessageBox
3237  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3238  * Example usage:
3239  *<pre><code>
3240 // Basic alert:
3241 Roo.Msg.alert('Status', 'Changes saved successfully.');
3242
3243 // Prompt for user data:
3244 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3245     if (btn == 'ok'){
3246         // process text value...
3247     }
3248 });
3249
3250 // Show a dialog using config options:
3251 Roo.Msg.show({
3252    title:'Save Changes?',
3253    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3254    buttons: Roo.Msg.YESNOCANCEL,
3255    fn: processResult,
3256    animEl: 'elId'
3257 });
3258 </code></pre>
3259  * @singleton
3260  */
3261 Roo.bootstrap.MessageBox = function(){
3262     var dlg, opt, mask, waitTimer;
3263     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3264     var buttons, activeTextEl, bwidth;
3265
3266     
3267     // private
3268     var handleButton = function(button){
3269         dlg.hide();
3270         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3271     };
3272
3273     // private
3274     var handleHide = function(){
3275         if(opt && opt.cls){
3276             dlg.el.removeClass(opt.cls);
3277         }
3278         //if(waitTimer){
3279         //    Roo.TaskMgr.stop(waitTimer);
3280         //    waitTimer = null;
3281         //}
3282     };
3283
3284     // private
3285     var updateButtons = function(b){
3286         var width = 0;
3287         if(!b){
3288             buttons["ok"].hide();
3289             buttons["cancel"].hide();
3290             buttons["yes"].hide();
3291             buttons["no"].hide();
3292             //dlg.footer.dom.style.display = 'none';
3293             return width;
3294         }
3295         dlg.footerEl.dom.style.display = '';
3296         for(var k in buttons){
3297             if(typeof buttons[k] != "function"){
3298                 if(b[k]){
3299                     buttons[k].show();
3300                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3301                     width += buttons[k].el.getWidth()+15;
3302                 }else{
3303                     buttons[k].hide();
3304                 }
3305             }
3306         }
3307         return width;
3308     };
3309
3310     // private
3311     var handleEsc = function(d, k, e){
3312         if(opt && opt.closable !== false){
3313             dlg.hide();
3314         }
3315         if(e){
3316             e.stopEvent();
3317         }
3318     };
3319
3320     return {
3321         /**
3322          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3323          * @return {Roo.BasicDialog} The BasicDialog element
3324          */
3325         getDialog : function(){
3326            if(!dlg){
3327                 dlg = new Roo.bootstrap.Modal( {
3328                     //draggable: true,
3329                     //resizable:false,
3330                     //constraintoviewport:false,
3331                     //fixedcenter:true,
3332                     //collapsible : false,
3333                     //shim:true,
3334                     //modal: true,
3335                 //    width: 'auto',
3336                   //  height:100,
3337                     //buttonAlign:"center",
3338                     closeClick : function(){
3339                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3340                             handleButton("no");
3341                         }else{
3342                             handleButton("cancel");
3343                         }
3344                     }
3345                 });
3346                 dlg.render();
3347                 dlg.on("hide", handleHide);
3348                 mask = dlg.mask;
3349                 //dlg.addKeyListener(27, handleEsc);
3350                 buttons = {};
3351                 this.buttons = buttons;
3352                 var bt = this.buttonText;
3353                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3354                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3355                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3356                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3357                 //Roo.log(buttons);
3358                 bodyEl = dlg.bodyEl.createChild({
3359
3360                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3361                         '<textarea class="roo-mb-textarea"></textarea>' +
3362                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3363                 });
3364                 msgEl = bodyEl.dom.firstChild;
3365                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3366                 textboxEl.enableDisplayMode();
3367                 textboxEl.addKeyListener([10,13], function(){
3368                     if(dlg.isVisible() && opt && opt.buttons){
3369                         if(opt.buttons.ok){
3370                             handleButton("ok");
3371                         }else if(opt.buttons.yes){
3372                             handleButton("yes");
3373                         }
3374                     }
3375                 });
3376                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3377                 textareaEl.enableDisplayMode();
3378                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3379                 progressEl.enableDisplayMode();
3380                 
3381                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3382                 var pf = progressEl.dom.firstChild;
3383                 if (pf) {
3384                     pp = Roo.get(pf.firstChild);
3385                     pp.setHeight(pf.offsetHeight);
3386                 }
3387                 
3388             }
3389             return dlg;
3390         },
3391
3392         /**
3393          * Updates the message box body text
3394          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3395          * the XHTML-compliant non-breaking space character '&amp;#160;')
3396          * @return {Roo.MessageBox} This message box
3397          */
3398         updateText : function(text)
3399         {
3400             if(!dlg.isVisible() && !opt.width){
3401                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3402                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3403             }
3404             msgEl.innerHTML = text || '&#160;';
3405       
3406             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3407             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3408             var w = Math.max(
3409                     Math.min(opt.width || cw , this.maxWidth), 
3410                     Math.max(opt.minWidth || this.minWidth, bwidth)
3411             );
3412             if(opt.prompt){
3413                 activeTextEl.setWidth(w);
3414             }
3415             if(dlg.isVisible()){
3416                 dlg.fixedcenter = false;
3417             }
3418             // to big, make it scroll. = But as usual stupid IE does not support
3419             // !important..
3420             
3421             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3422                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3423                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3424             } else {
3425                 bodyEl.dom.style.height = '';
3426                 bodyEl.dom.style.overflowY = '';
3427             }
3428             if (cw > w) {
3429                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3430             } else {
3431                 bodyEl.dom.style.overflowX = '';
3432             }
3433             
3434             dlg.setContentSize(w, bodyEl.getHeight());
3435             if(dlg.isVisible()){
3436                 dlg.fixedcenter = true;
3437             }
3438             return this;
3439         },
3440
3441         /**
3442          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3443          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3444          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3445          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3446          * @return {Roo.MessageBox} This message box
3447          */
3448         updateProgress : function(value, text){
3449             if(text){
3450                 this.updateText(text);
3451             }
3452             
3453             if (pp) { // weird bug on my firefox - for some reason this is not defined
3454                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3455                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3456             }
3457             return this;
3458         },        
3459
3460         /**
3461          * Returns true if the message box is currently displayed
3462          * @return {Boolean} True if the message box is visible, else false
3463          */
3464         isVisible : function(){
3465             return dlg && dlg.isVisible();  
3466         },
3467
3468         /**
3469          * Hides the message box if it is displayed
3470          */
3471         hide : function(){
3472             if(this.isVisible()){
3473                 dlg.hide();
3474             }  
3475         },
3476
3477         /**
3478          * Displays a new message box, or reinitializes an existing message box, based on the config options
3479          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3480          * The following config object properties are supported:
3481          * <pre>
3482 Property    Type             Description
3483 ----------  ---------------  ------------------------------------------------------------------------------------
3484 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3485                                    closes (defaults to undefined)
3486 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3487                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3488 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3489                                    progress and wait dialogs will ignore this property and always hide the
3490                                    close button as they can only be closed programmatically.
3491 cls               String           A custom CSS class to apply to the message box element
3492 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3493                                    displayed (defaults to 75)
3494 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3495                                    function will be btn (the name of the button that was clicked, if applicable,
3496                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3497                                    Progress and wait dialogs will ignore this option since they do not respond to
3498                                    user actions and can only be closed programmatically, so any required function
3499                                    should be called by the same code after it closes the dialog.
3500 icon              String           A CSS class that provides a background image to be used as an icon for
3501                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3502 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3503 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3504 modal             Boolean          False to allow user interaction with the page while the message box is
3505                                    displayed (defaults to true)
3506 msg               String           A string that will replace the existing message box body text (defaults
3507                                    to the XHTML-compliant non-breaking space character '&#160;')
3508 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3509 progress          Boolean          True to display a progress bar (defaults to false)
3510 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3511 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3512 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3513 title             String           The title text
3514 value             String           The string value to set into the active textbox element if displayed
3515 wait              Boolean          True to display a progress bar (defaults to false)
3516 width             Number           The width of the dialog in pixels
3517 </pre>
3518          *
3519          * Example usage:
3520          * <pre><code>
3521 Roo.Msg.show({
3522    title: 'Address',
3523    msg: 'Please enter your address:',
3524    width: 300,
3525    buttons: Roo.MessageBox.OKCANCEL,
3526    multiline: true,
3527    fn: saveAddress,
3528    animEl: 'addAddressBtn'
3529 });
3530 </code></pre>
3531          * @param {Object} config Configuration options
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         show : function(options)
3535         {
3536             
3537             // this causes nightmares if you show one dialog after another
3538             // especially on callbacks..
3539              
3540             if(this.isVisible()){
3541                 
3542                 this.hide();
3543                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3544                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3545                 Roo.log("New Dialog Message:" +  options.msg )
3546                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3547                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3548                 
3549             }
3550             var d = this.getDialog();
3551             opt = options;
3552             d.setTitle(opt.title || "&#160;");
3553             d.closeEl.setDisplayed(opt.closable !== false);
3554             activeTextEl = textboxEl;
3555             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3556             if(opt.prompt){
3557                 if(opt.multiline){
3558                     textboxEl.hide();
3559                     textareaEl.show();
3560                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3561                         opt.multiline : this.defaultTextHeight);
3562                     activeTextEl = textareaEl;
3563                 }else{
3564                     textboxEl.show();
3565                     textareaEl.hide();
3566                 }
3567             }else{
3568                 textboxEl.hide();
3569                 textareaEl.hide();
3570             }
3571             progressEl.setDisplayed(opt.progress === true);
3572             this.updateProgress(0);
3573             activeTextEl.dom.value = opt.value || "";
3574             if(opt.prompt){
3575                 dlg.setDefaultButton(activeTextEl);
3576             }else{
3577                 var bs = opt.buttons;
3578                 var db = null;
3579                 if(bs && bs.ok){
3580                     db = buttons["ok"];
3581                 }else if(bs && bs.yes){
3582                     db = buttons["yes"];
3583                 }
3584                 dlg.setDefaultButton(db);
3585             }
3586             bwidth = updateButtons(opt.buttons);
3587             this.updateText(opt.msg);
3588             if(opt.cls){
3589                 d.el.addClass(opt.cls);
3590             }
3591             d.proxyDrag = opt.proxyDrag === true;
3592             d.modal = opt.modal !== false;
3593             d.mask = opt.modal !== false ? mask : false;
3594             if(!d.isVisible()){
3595                 // force it to the end of the z-index stack so it gets a cursor in FF
3596                 document.body.appendChild(dlg.el.dom);
3597                 d.animateTarget = null;
3598                 d.show(options.animEl);
3599             }
3600             return this;
3601         },
3602
3603         /**
3604          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3605          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3606          * and closing the message box when the process is complete.
3607          * @param {String} title The title bar text
3608          * @param {String} msg The message box body text
3609          * @return {Roo.MessageBox} This message box
3610          */
3611         progress : function(title, msg){
3612             this.show({
3613                 title : title,
3614                 msg : msg,
3615                 buttons: false,
3616                 progress:true,
3617                 closable:false,
3618                 minWidth: this.minProgressWidth,
3619                 modal : true
3620             });
3621             return this;
3622         },
3623
3624         /**
3625          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3626          * If a callback function is passed it will be called after the user clicks the button, and the
3627          * id of the button that was clicked will be passed as the only parameter to the callback
3628          * (could also be the top-right close button).
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3632          * @param {Object} scope (optional) The scope of the callback function
3633          * @return {Roo.MessageBox} This message box
3634          */
3635         alert : function(title, msg, fn, scope)
3636         {
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OK,
3641                 fn: fn,
3642                 closable : false,
3643                 scope : scope,
3644                 modal : true
3645             });
3646             return this;
3647         },
3648
3649         /**
3650          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3651          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3652          * You are responsible for closing the message box when the process is complete.
3653          * @param {String} msg The message box body text
3654          * @param {String} title (optional) The title bar text
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         wait : function(msg, title){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: false,
3662                 closable:false,
3663                 progress:true,
3664                 modal:true,
3665                 width:300,
3666                 wait:true
3667             });
3668             waitTimer = Roo.TaskMgr.start({
3669                 run: function(i){
3670                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3671                 },
3672                 interval: 1000
3673             });
3674             return this;
3675         },
3676
3677         /**
3678          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3679          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3680          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3681          * @param {String} title The title bar text
3682          * @param {String} msg The message box body text
3683          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3684          * @param {Object} scope (optional) The scope of the callback function
3685          * @return {Roo.MessageBox} This message box
3686          */
3687         confirm : function(title, msg, fn, scope){
3688             this.show({
3689                 title : title,
3690                 msg : msg,
3691                 buttons: this.YESNO,
3692                 fn: fn,
3693                 scope : scope,
3694                 modal : true
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3701          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3702          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3703          * (could also be the top-right close button) and the text that was entered will be passed as the two
3704          * parameters to the callback.
3705          * @param {String} title The title bar text
3706          * @param {String} msg The message box body text
3707          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3708          * @param {Object} scope (optional) The scope of the callback function
3709          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3710          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3711          * @return {Roo.MessageBox} This message box
3712          */
3713         prompt : function(title, msg, fn, scope, multiline){
3714             this.show({
3715                 title : title,
3716                 msg : msg,
3717                 buttons: this.OKCANCEL,
3718                 fn: fn,
3719                 minWidth:250,
3720                 scope : scope,
3721                 prompt:true,
3722                 multiline: multiline,
3723                 modal : true
3724             });
3725             return this;
3726         },
3727
3728         /**
3729          * Button config that displays a single OK button
3730          * @type Object
3731          */
3732         OK : {ok:true},
3733         /**
3734          * Button config that displays Yes and No buttons
3735          * @type Object
3736          */
3737         YESNO : {yes:true, no:true},
3738         /**
3739          * Button config that displays OK and Cancel buttons
3740          * @type Object
3741          */
3742         OKCANCEL : {ok:true, cancel:true},
3743         /**
3744          * Button config that displays Yes, No and Cancel buttons
3745          * @type Object
3746          */
3747         YESNOCANCEL : {yes:true, no:true, cancel:true},
3748
3749         /**
3750          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3751          * @type Number
3752          */
3753         defaultTextHeight : 75,
3754         /**
3755          * The maximum width in pixels of the message box (defaults to 600)
3756          * @type Number
3757          */
3758         maxWidth : 600,
3759         /**
3760          * The minimum width in pixels of the message box (defaults to 100)
3761          * @type Number
3762          */
3763         minWidth : 100,
3764         /**
3765          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3766          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3767          * @type Number
3768          */
3769         minProgressWidth : 250,
3770         /**
3771          * An object containing the default button text strings that can be overriden for localized language support.
3772          * Supported properties are: ok, cancel, yes and no.
3773          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3774          * @type Object
3775          */
3776         buttonText : {
3777             ok : "OK",
3778             cancel : "Cancel",
3779             yes : "Yes",
3780             no : "No"
3781         }
3782     };
3783 }();
3784
3785 /**
3786  * Shorthand for {@link Roo.MessageBox}
3787  */
3788 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3789 Roo.Msg = Roo.Msg || Roo.MessageBox;
3790 /*
3791  * - LGPL
3792  *
3793  * navbar
3794  * 
3795  */
3796
3797 /**
3798  * @class Roo.bootstrap.Navbar
3799  * @extends Roo.bootstrap.Component
3800  * Bootstrap Navbar class
3801
3802  * @constructor
3803  * Create a new Navbar
3804  * @param {Object} config The config object
3805  */
3806
3807
3808 Roo.bootstrap.Navbar = function(config){
3809     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3810     this.addEvents({
3811         // raw events
3812         /**
3813          * @event beforetoggle
3814          * Fire before toggle the menu
3815          * @param {Roo.EventObject} e
3816          */
3817         "beforetoggle" : true
3818     });
3819 };
3820
3821 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3822     
3823     
3824    
3825     // private
3826     navItems : false,
3827     loadMask : false,
3828     
3829     
3830     getAutoCreate : function(){
3831         
3832         
3833         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3834         
3835     },
3836     
3837     initEvents :function ()
3838     {
3839         //Roo.log(this.el.select('.navbar-toggle',true));
3840         this.el.select('.navbar-toggle',true).on('click', function() {
3841             if(this.fireEvent('beforetoggle', this) !== false){
3842                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3843             }
3844             
3845         }, this);
3846         
3847         var mark = {
3848             tag: "div",
3849             cls:"x-dlg-mask"
3850         };
3851         
3852         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3853         
3854         var size = this.el.getSize();
3855         this.maskEl.setSize(size.width, size.height);
3856         this.maskEl.enableDisplayMode("block");
3857         this.maskEl.hide();
3858         
3859         if(this.loadMask){
3860             this.maskEl.show();
3861         }
3862     },
3863     
3864     
3865     getChildContainer : function()
3866     {
3867         if (this.el.select('.collapse').getCount()) {
3868             return this.el.select('.collapse',true).first();
3869         }
3870         
3871         return this.el;
3872     },
3873     
3874     mask : function()
3875     {
3876         this.maskEl.show();
3877     },
3878     
3879     unmask : function()
3880     {
3881         this.maskEl.hide();
3882     } 
3883     
3884     
3885     
3886     
3887 });
3888
3889
3890
3891  
3892
3893  /*
3894  * - LGPL
3895  *
3896  * navbar
3897  * 
3898  */
3899
3900 /**
3901  * @class Roo.bootstrap.NavSimplebar
3902  * @extends Roo.bootstrap.Navbar
3903  * Bootstrap Sidebar class
3904  *
3905  * @cfg {Boolean} inverse is inverted color
3906  * 
3907  * @cfg {String} type (nav | pills | tabs)
3908  * @cfg {Boolean} arrangement stacked | justified
3909  * @cfg {String} align (left | right) alignment
3910  * 
3911  * @cfg {Boolean} main (true|false) main nav bar? default false
3912  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3913  * 
3914  * @cfg {String} tag (header|footer|nav|div) default is nav 
3915
3916  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3917  * 
3918  * 
3919  * @constructor
3920  * Create a new Sidebar
3921  * @param {Object} config The config object
3922  */
3923
3924
3925 Roo.bootstrap.NavSimplebar = function(config){
3926     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3927 };
3928
3929 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3930     
3931     inverse: false,
3932     
3933     type: false,
3934     arrangement: '',
3935     align : false,
3936     
3937     weight : 'light',
3938     
3939     main : false,
3940     
3941     
3942     tag : false,
3943     
3944     
3945     getAutoCreate : function(){
3946         
3947         
3948         var cfg = {
3949             tag : this.tag || 'div',
3950             cls : 'navbar navbar-expand-lg'
3951         };
3952         if (['light','white'].indexOf(this.weight) > -1) {
3953             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3954         }
3955         cfg.cls += ' bg-' + this.weight;
3956         
3957           
3958         
3959         cfg.cn = [
3960             {
3961                 cls: 'nav',
3962                 tag : 'ul'
3963             }
3964         ];
3965         
3966          
3967         this.type = this.type || 'nav';
3968         if (['tabs','pills'].indexOf(this.type)!==-1) {
3969             cfg.cn[0].cls += ' nav-' + this.type
3970         
3971         
3972         } else {
3973             if (this.type!=='nav') {
3974                 Roo.log('nav type must be nav/tabs/pills')
3975             }
3976             cfg.cn[0].cls += ' navbar-nav'
3977         }
3978         
3979         
3980         
3981         
3982         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3983             cfg.cn[0].cls += ' nav-' + this.arrangement;
3984         }
3985         
3986         
3987         if (this.align === 'right') {
3988             cfg.cn[0].cls += ' navbar-right';
3989         }
3990         
3991         if (this.inverse) {
3992             cfg.cls += ' navbar-inverse';
3993             
3994         }
3995         
3996         
3997         return cfg;
3998     
3999         
4000     }
4001     
4002     
4003     
4004 });
4005
4006
4007
4008  
4009
4010  
4011        /*
4012  * - LGPL
4013  *
4014  * navbar
4015  * navbar-fixed-top
4016  * navbar-expand-md  fixed-top 
4017  */
4018
4019 /**
4020  * @class Roo.bootstrap.NavHeaderbar
4021  * @extends Roo.bootstrap.NavSimplebar
4022  * Bootstrap Sidebar class
4023  *
4024  * @cfg {String} brand what is brand
4025  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4026  * @cfg {String} brand_href href of the brand
4027  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4028  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4029  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4030  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4031  * 
4032  * @constructor
4033  * Create a new Sidebar
4034  * @param {Object} config The config object
4035  */
4036
4037
4038 Roo.bootstrap.NavHeaderbar = function(config){
4039     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4040       
4041 };
4042
4043 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4044     
4045     position: '',
4046     brand: '',
4047     brand_href: false,
4048     srButton : true,
4049     autohide : false,
4050     desktopCenter : false,
4051    
4052     
4053     getAutoCreate : function(){
4054         
4055         var   cfg = {
4056             tag: this.nav || 'nav',
4057             cls: 'navbar navbar-expand-md',
4058             role: 'navigation',
4059             cn: []
4060         };
4061         
4062         var cn = cfg.cn;
4063         if (this.desktopCenter) {
4064             cn.push({cls : 'container', cn : []});
4065             cn = cn[0].cn;
4066         }
4067         
4068         if(this.srButton){
4069             cn.push({
4070                 tag: 'div',
4071                 cls: 'navbar-header',
4072                 cn: [
4073                     {
4074                         tag: 'button',
4075                         type: 'button',
4076                         cls: 'navbar-toggle navbar-toggler',
4077                         'data-toggle': 'collapse',
4078                         cn: [
4079                             {
4080                                 tag: 'span',
4081                                 cls: 'sr-only',
4082                                 html: 'Toggle navigation'
4083                             },
4084                             {
4085                                 tag: 'span',
4086                                 cls: 'icon-bar navbar-toggler-icon'
4087                             },
4088                             {
4089                                 tag: 'span',
4090                                 cls: 'icon-bar'
4091                             },
4092                             {
4093                                 tag: 'span',
4094                                 cls: 'icon-bar'
4095                             }
4096                         ]
4097                     }
4098                 ]
4099             });
4100         }
4101         
4102         cn.push({
4103             tag: 'div',
4104             cls: 'collapse navbar-collapse',
4105             cn : []
4106         });
4107         
4108         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4109         
4110         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4111             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4112             
4113             // tag can override this..
4114             
4115             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4116         }
4117         
4118         if (this.brand !== '') {
4119             cn[0].cn.push({
4120                 tag: 'a',
4121                 href: this.brand_href ? this.brand_href : '#',
4122                 cls: 'navbar-brand',
4123                 cn: [
4124                 this.brand
4125                 ]
4126             });
4127         }
4128         
4129         if(this.main){
4130             cfg.cls += ' main-nav';
4131         }
4132         
4133         
4134         return cfg;
4135
4136         
4137     },
4138     getHeaderChildContainer : function()
4139     {
4140         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4141             return this.el.select('.navbar-header',true).first();
4142         }
4143         
4144         return this.getChildContainer();
4145     },
4146     
4147     
4148     initEvents : function()
4149     {
4150         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4151         
4152         if (this.autohide) {
4153             
4154             var prevScroll = 0;
4155             var ft = this.el;
4156             
4157             Roo.get(document).on('scroll',function(e) {
4158                 var ns = Roo.get(document).getScroll().top;
4159                 var os = prevScroll;
4160                 prevScroll = ns;
4161                 
4162                 if(ns > os){
4163                     ft.removeClass('slideDown');
4164                     ft.addClass('slideUp');
4165                     return;
4166                 }
4167                 ft.removeClass('slideUp');
4168                 ft.addClass('slideDown');
4169                  
4170               
4171           },this);
4172         }
4173     }    
4174     
4175 });
4176
4177
4178
4179  
4180
4181  /*
4182  * - LGPL
4183  *
4184  * navbar
4185  * 
4186  */
4187
4188 /**
4189  * @class Roo.bootstrap.NavSidebar
4190  * @extends Roo.bootstrap.Navbar
4191  * Bootstrap Sidebar class
4192  * 
4193  * @constructor
4194  * Create a new Sidebar
4195  * @param {Object} config The config object
4196  */
4197
4198
4199 Roo.bootstrap.NavSidebar = function(config){
4200     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4201 };
4202
4203 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4204     
4205     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4206     
4207     getAutoCreate : function(){
4208         
4209         
4210         return  {
4211             tag: 'div',
4212             cls: 'sidebar sidebar-nav'
4213         };
4214     
4215         
4216     }
4217     
4218     
4219     
4220 });
4221
4222
4223
4224  
4225
4226  /*
4227  * - LGPL
4228  *
4229  * nav group
4230  * 
4231  */
4232
4233 /**
4234  * @class Roo.bootstrap.NavGroup
4235  * @extends Roo.bootstrap.Component
4236  * Bootstrap NavGroup class
4237  * @cfg {String} align (left|right)
4238  * @cfg {Boolean} inverse
4239  * @cfg {String} type (nav|pills|tab) default nav
4240  * @cfg {String} navId - reference Id for navbar.
4241
4242  * 
4243  * @constructor
4244  * Create a new nav group
4245  * @param {Object} config The config object
4246  */
4247
4248 Roo.bootstrap.NavGroup = function(config){
4249     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4250     this.navItems = [];
4251    
4252     Roo.bootstrap.NavGroup.register(this);
4253      this.addEvents({
4254         /**
4255              * @event changed
4256              * Fires when the active item changes
4257              * @param {Roo.bootstrap.NavGroup} this
4258              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4259              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4260          */
4261         'changed': true
4262      });
4263     
4264 };
4265
4266 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4267     
4268     align: '',
4269     inverse: false,
4270     form: false,
4271     type: 'nav',
4272     navId : '',
4273     // private
4274     
4275     navItems : false, 
4276     
4277     getAutoCreate : function()
4278     {
4279         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4280         
4281         cfg = {
4282             tag : 'ul',
4283             cls: 'nav' 
4284         };
4285         
4286         if (['tabs','pills'].indexOf(this.type)!==-1) {
4287             cfg.cls += ' nav-' + this.type
4288         } else {
4289             if (this.type!=='nav') {
4290                 Roo.log('nav type must be nav/tabs/pills')
4291             }
4292             cfg.cls += ' navbar-nav'
4293         }
4294         
4295         if (this.parent() && this.parent().sidebar) {
4296             cfg = {
4297                 tag: 'ul',
4298                 cls: 'dashboard-menu sidebar-menu'
4299             };
4300             
4301             return cfg;
4302         }
4303         
4304         if (this.form === true) {
4305             cfg = {
4306                 tag: 'form',
4307                 cls: 'navbar-form'
4308             };
4309             
4310             if (this.align === 'right') {
4311                 cfg.cls += ' navbar-right ml-md-auto';
4312             } else {
4313                 cfg.cls += ' navbar-left';
4314             }
4315         }
4316         
4317         if (this.align === 'right') {
4318             cfg.cls += ' navbar-right ml-md-auto';
4319         } else {
4320             cfg.cls += ' mr-auto';
4321         }
4322         
4323         if (this.inverse) {
4324             cfg.cls += ' navbar-inverse';
4325             
4326         }
4327         
4328         
4329         return cfg;
4330     },
4331     /**
4332     * sets the active Navigation item
4333     * @param {Roo.bootstrap.NavItem} the new current navitem
4334     */
4335     setActiveItem : function(item)
4336     {
4337         var prev = false;
4338         Roo.each(this.navItems, function(v){
4339             if (v == item) {
4340                 return ;
4341             }
4342             if (v.isActive()) {
4343                 v.setActive(false, true);
4344                 prev = v;
4345                 
4346             }
4347             
4348         });
4349
4350         item.setActive(true, true);
4351         this.fireEvent('changed', this, item, prev);
4352         
4353         
4354     },
4355     /**
4356     * gets the active Navigation item
4357     * @return {Roo.bootstrap.NavItem} the current navitem
4358     */
4359     getActive : function()
4360     {
4361         
4362         var prev = false;
4363         Roo.each(this.navItems, function(v){
4364             
4365             if (v.isActive()) {
4366                 prev = v;
4367                 
4368             }
4369             
4370         });
4371         return prev;
4372     },
4373     
4374     indexOfNav : function()
4375     {
4376         
4377         var prev = false;
4378         Roo.each(this.navItems, function(v,i){
4379             
4380             if (v.isActive()) {
4381                 prev = i;
4382                 
4383             }
4384             
4385         });
4386         return prev;
4387     },
4388     /**
4389     * adds a Navigation item
4390     * @param {Roo.bootstrap.NavItem} the navitem to add
4391     */
4392     addItem : function(cfg)
4393     {
4394         var cn = new Roo.bootstrap.NavItem(cfg);
4395         this.register(cn);
4396         cn.parentId = this.id;
4397         cn.onRender(this.el, null);
4398         return cn;
4399     },
4400     /**
4401     * register a Navigation item
4402     * @param {Roo.bootstrap.NavItem} the navitem to add
4403     */
4404     register : function(item)
4405     {
4406         this.navItems.push( item);
4407         item.navId = this.navId;
4408     
4409     },
4410     
4411     /**
4412     * clear all the Navigation item
4413     */
4414    
4415     clearAll : function()
4416     {
4417         this.navItems = [];
4418         this.el.dom.innerHTML = '';
4419     },
4420     
4421     getNavItem: function(tabId)
4422     {
4423         var ret = false;
4424         Roo.each(this.navItems, function(e) {
4425             if (e.tabId == tabId) {
4426                ret =  e;
4427                return false;
4428             }
4429             return true;
4430             
4431         });
4432         return ret;
4433     },
4434     
4435     setActiveNext : function()
4436     {
4437         var i = this.indexOfNav(this.getActive());
4438         if (i > this.navItems.length) {
4439             return;
4440         }
4441         this.setActiveItem(this.navItems[i+1]);
4442     },
4443     setActivePrev : function()
4444     {
4445         var i = this.indexOfNav(this.getActive());
4446         if (i  < 1) {
4447             return;
4448         }
4449         this.setActiveItem(this.navItems[i-1]);
4450     },
4451     clearWasActive : function(except) {
4452         Roo.each(this.navItems, function(e) {
4453             if (e.tabId != except.tabId && e.was_active) {
4454                e.was_active = false;
4455                return false;
4456             }
4457             return true;
4458             
4459         });
4460     },
4461     getWasActive : function ()
4462     {
4463         var r = false;
4464         Roo.each(this.navItems, function(e) {
4465             if (e.was_active) {
4466                r = e;
4467                return false;
4468             }
4469             return true;
4470             
4471         });
4472         return r;
4473     }
4474     
4475     
4476 });
4477
4478  
4479 Roo.apply(Roo.bootstrap.NavGroup, {
4480     
4481     groups: {},
4482      /**
4483     * register a Navigation Group
4484     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4485     */
4486     register : function(navgrp)
4487     {
4488         this.groups[navgrp.navId] = navgrp;
4489         
4490     },
4491     /**
4492     * fetch a Navigation Group based on the navigation ID
4493     * @param {string} the navgroup to add
4494     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4495     */
4496     get: function(navId) {
4497         if (typeof(this.groups[navId]) == 'undefined') {
4498             return false;
4499             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4500         }
4501         return this.groups[navId] ;
4502     }
4503     
4504     
4505     
4506 });
4507
4508  /*
4509  * - LGPL
4510  *
4511  * row
4512  * 
4513  */
4514
4515 /**
4516  * @class Roo.bootstrap.NavItem
4517  * @extends Roo.bootstrap.Component
4518  * Bootstrap Navbar.NavItem class
4519  * @cfg {String} href  link to
4520  * @cfg {String} html content of button
4521  * @cfg {String} badge text inside badge
4522  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4523  * @cfg {String} glyphicon name of glyphicon
4524  * @cfg {String} icon name of font awesome icon
4525  * @cfg {Boolean} active Is item active
4526  * @cfg {Boolean} disabled Is item disabled
4527  
4528  * @cfg {Boolean} preventDefault (true | false) default false
4529  * @cfg {String} tabId the tab that this item activates.
4530  * @cfg {String} tagtype (a|span) render as a href or span?
4531  * @cfg {Boolean} animateRef (true|false) link to element default false  
4532   
4533  * @constructor
4534  * Create a new Navbar Item
4535  * @param {Object} config The config object
4536  */
4537 Roo.bootstrap.NavItem = function(config){
4538     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4539     this.addEvents({
4540         // raw events
4541         /**
4542          * @event click
4543          * The raw click event for the entire grid.
4544          * @param {Roo.EventObject} e
4545          */
4546         "click" : true,
4547          /**
4548             * @event changed
4549             * Fires when the active item active state changes
4550             * @param {Roo.bootstrap.NavItem} this
4551             * @param {boolean} state the new state
4552              
4553          */
4554         'changed': true,
4555         /**
4556             * @event scrollto
4557             * Fires when scroll to element
4558             * @param {Roo.bootstrap.NavItem} this
4559             * @param {Object} options
4560             * @param {Roo.EventObject} e
4561              
4562          */
4563         'scrollto': true
4564     });
4565    
4566 };
4567
4568 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4569     
4570     href: false,
4571     html: '',
4572     badge: '',
4573     icon: false,
4574     glyphicon: false,
4575     active: false,
4576     preventDefault : false,
4577     tabId : false,
4578     tagtype : 'a',
4579     disabled : false,
4580     animateRef : false,
4581     was_active : false,
4582     
4583     getAutoCreate : function(){
4584          
4585         var cfg = {
4586             tag: 'li',
4587             cls: 'nav-item'
4588             
4589         };
4590         
4591         if (this.active) {
4592             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4593         }
4594         if (this.disabled) {
4595             cfg.cls += ' disabled';
4596         }
4597         
4598         if (this.href || this.html || this.glyphicon || this.icon) {
4599             cfg.cn = [
4600                 {
4601                     tag: this.tagtype,
4602                     href : this.href || "#",
4603                     html: this.html || ''
4604                 }
4605             ];
4606             if (this.tagtype == 'a') {
4607                 cfg.cn[0].cls = 'nav-link';
4608             }
4609             if (this.icon) {
4610                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4611             }
4612
4613             if(this.glyphicon) {
4614                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4615             }
4616             
4617             if (this.menu) {
4618                 
4619                 cfg.cn[0].html += " <span class='caret'></span>";
4620              
4621             }
4622             
4623             if (this.badge !== '') {
4624                  
4625                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4626             }
4627         }
4628         
4629         
4630         
4631         return cfg;
4632     },
4633     initEvents: function() 
4634     {
4635         if (typeof (this.menu) != 'undefined') {
4636             this.menu.parentType = this.xtype;
4637             this.menu.triggerEl = this.el;
4638             this.menu = this.addxtype(Roo.apply({}, this.menu));
4639         }
4640         
4641         this.el.select('a',true).on('click', this.onClick, this);
4642         
4643         if(this.tagtype == 'span'){
4644             this.el.select('span',true).on('click', this.onClick, this);
4645         }
4646        
4647         // at this point parent should be available..
4648         this.parent().register(this);
4649     },
4650     
4651     onClick : function(e)
4652     {
4653         if (e.getTarget('.dropdown-menu-item')) {
4654             // did you click on a menu itemm.... - then don't trigger onclick..
4655             return;
4656         }
4657         
4658         if(
4659                 this.preventDefault || 
4660                 this.href == '#' 
4661         ){
4662             Roo.log("NavItem - prevent Default?");
4663             e.preventDefault();
4664         }
4665         
4666         if (this.disabled) {
4667             return;
4668         }
4669         
4670         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4671         if (tg && tg.transition) {
4672             Roo.log("waiting for the transitionend");
4673             return;
4674         }
4675         
4676         
4677         
4678         //Roo.log("fire event clicked");
4679         if(this.fireEvent('click', this, e) === false){
4680             return;
4681         };
4682         
4683         if(this.tagtype == 'span'){
4684             return;
4685         }
4686         
4687         //Roo.log(this.href);
4688         var ael = this.el.select('a',true).first();
4689         //Roo.log(ael);
4690         
4691         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4692             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4693             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4694                 return; // ignore... - it's a 'hash' to another page.
4695             }
4696             Roo.log("NavItem - prevent Default?");
4697             e.preventDefault();
4698             this.scrollToElement(e);
4699         }
4700         
4701         
4702         var p =  this.parent();
4703    
4704         if (['tabs','pills'].indexOf(p.type)!==-1) {
4705             if (typeof(p.setActiveItem) !== 'undefined') {
4706                 p.setActiveItem(this);
4707             }
4708         }
4709         
4710         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4711         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4712             // remove the collapsed menu expand...
4713             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4714         }
4715     },
4716     
4717     isActive: function () {
4718         return this.active
4719     },
4720     setActive : function(state, fire, is_was_active)
4721     {
4722         if (this.active && !state && this.navId) {
4723             this.was_active = true;
4724             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4725             if (nv) {
4726                 nv.clearWasActive(this);
4727             }
4728             
4729         }
4730         this.active = state;
4731         
4732         if (!state ) {
4733             this.el.removeClass('active');
4734         } else if (!this.el.hasClass('active')) {
4735             this.el.addClass('active');
4736         }
4737         if (fire) {
4738             this.fireEvent('changed', this, state);
4739         }
4740         
4741         // show a panel if it's registered and related..
4742         
4743         if (!this.navId || !this.tabId || !state || is_was_active) {
4744             return;
4745         }
4746         
4747         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4748         if (!tg) {
4749             return;
4750         }
4751         var pan = tg.getPanelByName(this.tabId);
4752         if (!pan) {
4753             return;
4754         }
4755         // if we can not flip to new panel - go back to old nav highlight..
4756         if (false == tg.showPanel(pan)) {
4757             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4758             if (nv) {
4759                 var onav = nv.getWasActive();
4760                 if (onav) {
4761                     onav.setActive(true, false, true);
4762                 }
4763             }
4764             
4765         }
4766         
4767         
4768         
4769     },
4770      // this should not be here...
4771     setDisabled : function(state)
4772     {
4773         this.disabled = state;
4774         if (!state ) {
4775             this.el.removeClass('disabled');
4776         } else if (!this.el.hasClass('disabled')) {
4777             this.el.addClass('disabled');
4778         }
4779         
4780     },
4781     
4782     /**
4783      * Fetch the element to display the tooltip on.
4784      * @return {Roo.Element} defaults to this.el
4785      */
4786     tooltipEl : function()
4787     {
4788         return this.el.select('' + this.tagtype + '', true).first();
4789     },
4790     
4791     scrollToElement : function(e)
4792     {
4793         var c = document.body;
4794         
4795         /*
4796          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4797          */
4798         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4799             c = document.documentElement;
4800         }
4801         
4802         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4803         
4804         if(!target){
4805             return;
4806         }
4807
4808         var o = target.calcOffsetsTo(c);
4809         
4810         var options = {
4811             target : target,
4812             value : o[1]
4813         };
4814         
4815         this.fireEvent('scrollto', this, options, e);
4816         
4817         Roo.get(c).scrollTo('top', options.value, true);
4818         
4819         return;
4820     }
4821 });
4822  
4823
4824  /*
4825  * - LGPL
4826  *
4827  * sidebar item
4828  *
4829  *  li
4830  *    <span> icon </span>
4831  *    <span> text </span>
4832  *    <span>badge </span>
4833  */
4834
4835 /**
4836  * @class Roo.bootstrap.NavSidebarItem
4837  * @extends Roo.bootstrap.NavItem
4838  * Bootstrap Navbar.NavSidebarItem class
4839  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4840  * {Boolean} open is the menu open
4841  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4842  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4843  * {String} buttonSize (sm|md|lg)the extra classes for the button
4844  * {Boolean} showArrow show arrow next to the text (default true)
4845  * @constructor
4846  * Create a new Navbar Button
4847  * @param {Object} config The config object
4848  */
4849 Roo.bootstrap.NavSidebarItem = function(config){
4850     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4851     this.addEvents({
4852         // raw events
4853         /**
4854          * @event click
4855          * The raw click event for the entire grid.
4856          * @param {Roo.EventObject} e
4857          */
4858         "click" : true,
4859          /**
4860             * @event changed
4861             * Fires when the active item active state changes
4862             * @param {Roo.bootstrap.NavSidebarItem} this
4863             * @param {boolean} state the new state
4864              
4865          */
4866         'changed': true
4867     });
4868    
4869 };
4870
4871 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4872     
4873     badgeWeight : 'default',
4874     
4875     open: false,
4876     
4877     buttonView : false,
4878     
4879     buttonWeight : 'default',
4880     
4881     buttonSize : 'md',
4882     
4883     showArrow : true,
4884     
4885     getAutoCreate : function(){
4886         
4887         
4888         var a = {
4889                 tag: 'a',
4890                 href : this.href || '#',
4891                 cls: '',
4892                 html : '',
4893                 cn : []
4894         };
4895         
4896         if(this.buttonView){
4897             a = {
4898                 tag: 'button',
4899                 href : this.href || '#',
4900                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4901                 html : this.html,
4902                 cn : []
4903             };
4904         }
4905         
4906         var cfg = {
4907             tag: 'li',
4908             cls: '',
4909             cn: [ a ]
4910         };
4911         
4912         if (this.active) {
4913             cfg.cls += ' active';
4914         }
4915         
4916         if (this.disabled) {
4917             cfg.cls += ' disabled';
4918         }
4919         if (this.open) {
4920             cfg.cls += ' open x-open';
4921         }
4922         // left icon..
4923         if (this.glyphicon || this.icon) {
4924             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4925             a.cn.push({ tag : 'i', cls : c }) ;
4926         }
4927         
4928         if(!this.buttonView){
4929             var span = {
4930                 tag: 'span',
4931                 html : this.html || ''
4932             };
4933
4934             a.cn.push(span);
4935             
4936         }
4937         
4938         if (this.badge !== '') {
4939             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4940         }
4941         
4942         if (this.menu) {
4943             
4944             if(this.showArrow){
4945                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4946             }
4947             
4948             a.cls += ' dropdown-toggle treeview' ;
4949         }
4950         
4951         return cfg;
4952     },
4953     
4954     initEvents : function()
4955     { 
4956         if (typeof (this.menu) != 'undefined') {
4957             this.menu.parentType = this.xtype;
4958             this.menu.triggerEl = this.el;
4959             this.menu = this.addxtype(Roo.apply({}, this.menu));
4960         }
4961         
4962         this.el.on('click', this.onClick, this);
4963         
4964         if(this.badge !== ''){
4965             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4966         }
4967         
4968     },
4969     
4970     onClick : function(e)
4971     {
4972         if(this.disabled){
4973             e.preventDefault();
4974             return;
4975         }
4976         
4977         if(this.preventDefault){
4978             e.preventDefault();
4979         }
4980         
4981         this.fireEvent('click', this);
4982     },
4983     
4984     disable : function()
4985     {
4986         this.setDisabled(true);
4987     },
4988     
4989     enable : function()
4990     {
4991         this.setDisabled(false);
4992     },
4993     
4994     setDisabled : function(state)
4995     {
4996         if(this.disabled == state){
4997             return;
4998         }
4999         
5000         this.disabled = state;
5001         
5002         if (state) {
5003             this.el.addClass('disabled');
5004             return;
5005         }
5006         
5007         this.el.removeClass('disabled');
5008         
5009         return;
5010     },
5011     
5012     setActive : function(state)
5013     {
5014         if(this.active == state){
5015             return;
5016         }
5017         
5018         this.active = state;
5019         
5020         if (state) {
5021             this.el.addClass('active');
5022             return;
5023         }
5024         
5025         this.el.removeClass('active');
5026         
5027         return;
5028     },
5029     
5030     isActive: function () 
5031     {
5032         return this.active;
5033     },
5034     
5035     setBadge : function(str)
5036     {
5037         if(!this.badgeEl){
5038             return;
5039         }
5040         
5041         this.badgeEl.dom.innerHTML = str;
5042     }
5043     
5044    
5045      
5046  
5047 });
5048  
5049
5050  /*
5051  * - LGPL
5052  *
5053  * row
5054  * 
5055  */
5056
5057 /**
5058  * @class Roo.bootstrap.Row
5059  * @extends Roo.bootstrap.Component
5060  * Bootstrap Row class (contains columns...)
5061  * 
5062  * @constructor
5063  * Create a new Row
5064  * @param {Object} config The config object
5065  */
5066
5067 Roo.bootstrap.Row = function(config){
5068     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5069 };
5070
5071 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5072     
5073     getAutoCreate : function(){
5074        return {
5075             cls: 'row clearfix'
5076        };
5077     }
5078     
5079     
5080 });
5081
5082  
5083
5084  /*
5085  * - LGPL
5086  *
5087  * element
5088  * 
5089  */
5090
5091 /**
5092  * @class Roo.bootstrap.Element
5093  * @extends Roo.bootstrap.Component
5094  * Bootstrap Element class
5095  * @cfg {String} html contents of the element
5096  * @cfg {String} tag tag of the element
5097  * @cfg {String} cls class of the element
5098  * @cfg {Boolean} preventDefault (true|false) default false
5099  * @cfg {Boolean} clickable (true|false) default false
5100  * 
5101  * @constructor
5102  * Create a new Element
5103  * @param {Object} config The config object
5104  */
5105
5106 Roo.bootstrap.Element = function(config){
5107     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5108     
5109     this.addEvents({
5110         // raw events
5111         /**
5112          * @event click
5113          * When a element is chick
5114          * @param {Roo.bootstrap.Element} this
5115          * @param {Roo.EventObject} e
5116          */
5117         "click" : true
5118     });
5119 };
5120
5121 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5122     
5123     tag: 'div',
5124     cls: '',
5125     html: '',
5126     preventDefault: false, 
5127     clickable: false,
5128     
5129     getAutoCreate : function(){
5130         
5131         var cfg = {
5132             tag: this.tag,
5133             // cls: this.cls, double assign in parent class Component.js :: onRender
5134             html: this.html
5135         };
5136         
5137         return cfg;
5138     },
5139     
5140     initEvents: function() 
5141     {
5142         Roo.bootstrap.Element.superclass.initEvents.call(this);
5143         
5144         if(this.clickable){
5145             this.el.on('click', this.onClick, this);
5146         }
5147         
5148     },
5149     
5150     onClick : function(e)
5151     {
5152         if(this.preventDefault){
5153             e.preventDefault();
5154         }
5155         
5156         this.fireEvent('click', this, e);
5157     },
5158     
5159     getValue : function()
5160     {
5161         return this.el.dom.innerHTML;
5162     },
5163     
5164     setValue : function(value)
5165     {
5166         this.el.dom.innerHTML = value;
5167     }
5168    
5169 });
5170
5171  
5172
5173  /*
5174  * - LGPL
5175  *
5176  * pagination
5177  * 
5178  */
5179
5180 /**
5181  * @class Roo.bootstrap.Pagination
5182  * @extends Roo.bootstrap.Component
5183  * Bootstrap Pagination class
5184  * @cfg {String} size xs | sm | md | lg
5185  * @cfg {Boolean} inverse false | true
5186  * 
5187  * @constructor
5188  * Create a new Pagination
5189  * @param {Object} config The config object
5190  */
5191
5192 Roo.bootstrap.Pagination = function(config){
5193     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5194 };
5195
5196 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5197     
5198     cls: false,
5199     size: false,
5200     inverse: false,
5201     
5202     getAutoCreate : function(){
5203         var cfg = {
5204             tag: 'ul',
5205                 cls: 'pagination'
5206         };
5207         if (this.inverse) {
5208             cfg.cls += ' inverse';
5209         }
5210         if (this.html) {
5211             cfg.html=this.html;
5212         }
5213         if (this.cls) {
5214             cfg.cls += " " + this.cls;
5215         }
5216         return cfg;
5217     }
5218    
5219 });
5220
5221  
5222
5223  /*
5224  * - LGPL
5225  *
5226  * Pagination item
5227  * 
5228  */
5229
5230
5231 /**
5232  * @class Roo.bootstrap.PaginationItem
5233  * @extends Roo.bootstrap.Component
5234  * Bootstrap PaginationItem class
5235  * @cfg {String} html text
5236  * @cfg {String} href the link
5237  * @cfg {Boolean} preventDefault (true | false) default true
5238  * @cfg {Boolean} active (true | false) default false
5239  * @cfg {Boolean} disabled default false
5240  * 
5241  * 
5242  * @constructor
5243  * Create a new PaginationItem
5244  * @param {Object} config The config object
5245  */
5246
5247
5248 Roo.bootstrap.PaginationItem = function(config){
5249     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5250     this.addEvents({
5251         // raw events
5252         /**
5253          * @event click
5254          * The raw click event for the entire grid.
5255          * @param {Roo.EventObject} e
5256          */
5257         "click" : true
5258     });
5259 };
5260
5261 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5262     
5263     href : false,
5264     html : false,
5265     preventDefault: true,
5266     active : false,
5267     cls : false,
5268     disabled: false,
5269     
5270     getAutoCreate : function(){
5271         var cfg= {
5272             tag: 'li',
5273             cn: [
5274                 {
5275                     tag : 'a',
5276                     href : this.href ? this.href : '#',
5277                     html : this.html ? this.html : ''
5278                 }
5279             ]
5280         };
5281         
5282         if(this.cls){
5283             cfg.cls = this.cls;
5284         }
5285         
5286         if(this.disabled){
5287             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5288         }
5289         
5290         if(this.active){
5291             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5292         }
5293         
5294         return cfg;
5295     },
5296     
5297     initEvents: function() {
5298         
5299         this.el.on('click', this.onClick, this);
5300         
5301     },
5302     onClick : function(e)
5303     {
5304         Roo.log('PaginationItem on click ');
5305         if(this.preventDefault){
5306             e.preventDefault();
5307         }
5308         
5309         if(this.disabled){
5310             return;
5311         }
5312         
5313         this.fireEvent('click', this, e);
5314     }
5315    
5316 });
5317
5318  
5319
5320  /*
5321  * - LGPL
5322  *
5323  * slider
5324  * 
5325  */
5326
5327
5328 /**
5329  * @class Roo.bootstrap.Slider
5330  * @extends Roo.bootstrap.Component
5331  * Bootstrap Slider class
5332  *    
5333  * @constructor
5334  * Create a new Slider
5335  * @param {Object} config The config object
5336  */
5337
5338 Roo.bootstrap.Slider = function(config){
5339     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5340 };
5341
5342 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5343     
5344     getAutoCreate : function(){
5345         
5346         var cfg = {
5347             tag: 'div',
5348             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5349             cn: [
5350                 {
5351                     tag: 'a',
5352                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5353                 }
5354             ]
5355         };
5356         
5357         return cfg;
5358     }
5359    
5360 });
5361
5362  /*
5363  * Based on:
5364  * Ext JS Library 1.1.1
5365  * Copyright(c) 2006-2007, Ext JS, LLC.
5366  *
5367  * Originally Released Under LGPL - original licence link has changed is not relivant.
5368  *
5369  * Fork - LGPL
5370  * <script type="text/javascript">
5371  */
5372  
5373
5374 /**
5375  * @class Roo.grid.ColumnModel
5376  * @extends Roo.util.Observable
5377  * This is the default implementation of a ColumnModel used by the Grid. It defines
5378  * the columns in the grid.
5379  * <br>Usage:<br>
5380  <pre><code>
5381  var colModel = new Roo.grid.ColumnModel([
5382         {header: "Ticker", width: 60, sortable: true, locked: true},
5383         {header: "Company Name", width: 150, sortable: true},
5384         {header: "Market Cap.", width: 100, sortable: true},
5385         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5386         {header: "Employees", width: 100, sortable: true, resizable: false}
5387  ]);
5388  </code></pre>
5389  * <p>
5390  
5391  * The config options listed for this class are options which may appear in each
5392  * individual column definition.
5393  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5394  * @constructor
5395  * @param {Object} config An Array of column config objects. See this class's
5396  * config objects for details.
5397 */
5398 Roo.grid.ColumnModel = function(config){
5399         /**
5400      * The config passed into the constructor
5401      */
5402     this.config = config;
5403     this.lookup = {};
5404
5405     // if no id, create one
5406     // if the column does not have a dataIndex mapping,
5407     // map it to the order it is in the config
5408     for(var i = 0, len = config.length; i < len; i++){
5409         var c = config[i];
5410         if(typeof c.dataIndex == "undefined"){
5411             c.dataIndex = i;
5412         }
5413         if(typeof c.renderer == "string"){
5414             c.renderer = Roo.util.Format[c.renderer];
5415         }
5416         if(typeof c.id == "undefined"){
5417             c.id = Roo.id();
5418         }
5419         if(c.editor && c.editor.xtype){
5420             c.editor  = Roo.factory(c.editor, Roo.grid);
5421         }
5422         if(c.editor && c.editor.isFormField){
5423             c.editor = new Roo.grid.GridEditor(c.editor);
5424         }
5425         this.lookup[c.id] = c;
5426     }
5427
5428     /**
5429      * The width of columns which have no width specified (defaults to 100)
5430      * @type Number
5431      */
5432     this.defaultWidth = 100;
5433
5434     /**
5435      * Default sortable of columns which have no sortable specified (defaults to false)
5436      * @type Boolean
5437      */
5438     this.defaultSortable = false;
5439
5440     this.addEvents({
5441         /**
5442              * @event widthchange
5443              * Fires when the width of a column changes.
5444              * @param {ColumnModel} this
5445              * @param {Number} columnIndex The column index
5446              * @param {Number} newWidth The new width
5447              */
5448             "widthchange": true,
5449         /**
5450              * @event headerchange
5451              * Fires when the text of a header changes.
5452              * @param {ColumnModel} this
5453              * @param {Number} columnIndex The column index
5454              * @param {Number} newText The new header text
5455              */
5456             "headerchange": true,
5457         /**
5458              * @event hiddenchange
5459              * Fires when a column is hidden or "unhidden".
5460              * @param {ColumnModel} this
5461              * @param {Number} columnIndex The column index
5462              * @param {Boolean} hidden true if hidden, false otherwise
5463              */
5464             "hiddenchange": true,
5465             /**
5466          * @event columnmoved
5467          * Fires when a column is moved.
5468          * @param {ColumnModel} this
5469          * @param {Number} oldIndex
5470          * @param {Number} newIndex
5471          */
5472         "columnmoved" : true,
5473         /**
5474          * @event columlockchange
5475          * Fires when a column's locked state is changed
5476          * @param {ColumnModel} this
5477          * @param {Number} colIndex
5478          * @param {Boolean} locked true if locked
5479          */
5480         "columnlockchange" : true
5481     });
5482     Roo.grid.ColumnModel.superclass.constructor.call(this);
5483 };
5484 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5485     /**
5486      * @cfg {String} header The header text to display in the Grid view.
5487      */
5488     /**
5489      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5490      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5491      * specified, the column's index is used as an index into the Record's data Array.
5492      */
5493     /**
5494      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5495      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5496      */
5497     /**
5498      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5499      * Defaults to the value of the {@link #defaultSortable} property.
5500      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5501      */
5502     /**
5503      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5504      */
5505     /**
5506      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5507      */
5508     /**
5509      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5510      */
5511     /**
5512      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5513      */
5514     /**
5515      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5516      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5517      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5518      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5519      */
5520        /**
5521      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5522      */
5523     /**
5524      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5525      */
5526     /**
5527      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5528      */
5529     /**
5530      * @cfg {String} cursor (Optional)
5531      */
5532     /**
5533      * @cfg {String} tooltip (Optional)
5534      */
5535     /**
5536      * @cfg {Number} xs (Optional)
5537      */
5538     /**
5539      * @cfg {Number} sm (Optional)
5540      */
5541     /**
5542      * @cfg {Number} md (Optional)
5543      */
5544     /**
5545      * @cfg {Number} lg (Optional)
5546      */
5547     /**
5548      * Returns the id of the column at the specified index.
5549      * @param {Number} index The column index
5550      * @return {String} the id
5551      */
5552     getColumnId : function(index){
5553         return this.config[index].id;
5554     },
5555
5556     /**
5557      * Returns the column for a specified id.
5558      * @param {String} id The column id
5559      * @return {Object} the column
5560      */
5561     getColumnById : function(id){
5562         return this.lookup[id];
5563     },
5564
5565     
5566     /**
5567      * Returns the column for a specified dataIndex.
5568      * @param {String} dataIndex The column dataIndex
5569      * @return {Object|Boolean} the column or false if not found
5570      */
5571     getColumnByDataIndex: function(dataIndex){
5572         var index = this.findColumnIndex(dataIndex);
5573         return index > -1 ? this.config[index] : false;
5574     },
5575     
5576     /**
5577      * Returns the index for a specified column id.
5578      * @param {String} id The column id
5579      * @return {Number} the index, or -1 if not found
5580      */
5581     getIndexById : function(id){
5582         for(var i = 0, len = this.config.length; i < len; i++){
5583             if(this.config[i].id == id){
5584                 return i;
5585             }
5586         }
5587         return -1;
5588     },
5589     
5590     /**
5591      * Returns the index for a specified column dataIndex.
5592      * @param {String} dataIndex The column dataIndex
5593      * @return {Number} the index, or -1 if not found
5594      */
5595     
5596     findColumnIndex : function(dataIndex){
5597         for(var i = 0, len = this.config.length; i < len; i++){
5598             if(this.config[i].dataIndex == dataIndex){
5599                 return i;
5600             }
5601         }
5602         return -1;
5603     },
5604     
5605     
5606     moveColumn : function(oldIndex, newIndex){
5607         var c = this.config[oldIndex];
5608         this.config.splice(oldIndex, 1);
5609         this.config.splice(newIndex, 0, c);
5610         this.dataMap = null;
5611         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5612     },
5613
5614     isLocked : function(colIndex){
5615         return this.config[colIndex].locked === true;
5616     },
5617
5618     setLocked : function(colIndex, value, suppressEvent){
5619         if(this.isLocked(colIndex) == value){
5620             return;
5621         }
5622         this.config[colIndex].locked = value;
5623         if(!suppressEvent){
5624             this.fireEvent("columnlockchange", this, colIndex, value);
5625         }
5626     },
5627
5628     getTotalLockedWidth : function(){
5629         var totalWidth = 0;
5630         for(var i = 0; i < this.config.length; i++){
5631             if(this.isLocked(i) && !this.isHidden(i)){
5632                 this.totalWidth += this.getColumnWidth(i);
5633             }
5634         }
5635         return totalWidth;
5636     },
5637
5638     getLockedCount : function(){
5639         for(var i = 0, len = this.config.length; i < len; i++){
5640             if(!this.isLocked(i)){
5641                 return i;
5642             }
5643         }
5644         
5645         return this.config.length;
5646     },
5647
5648     /**
5649      * Returns the number of columns.
5650      * @return {Number}
5651      */
5652     getColumnCount : function(visibleOnly){
5653         if(visibleOnly === true){
5654             var c = 0;
5655             for(var i = 0, len = this.config.length; i < len; i++){
5656                 if(!this.isHidden(i)){
5657                     c++;
5658                 }
5659             }
5660             return c;
5661         }
5662         return this.config.length;
5663     },
5664
5665     /**
5666      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5667      * @param {Function} fn
5668      * @param {Object} scope (optional)
5669      * @return {Array} result
5670      */
5671     getColumnsBy : function(fn, scope){
5672         var r = [];
5673         for(var i = 0, len = this.config.length; i < len; i++){
5674             var c = this.config[i];
5675             if(fn.call(scope||this, c, i) === true){
5676                 r[r.length] = c;
5677             }
5678         }
5679         return r;
5680     },
5681
5682     /**
5683      * Returns true if the specified column is sortable.
5684      * @param {Number} col The column index
5685      * @return {Boolean}
5686      */
5687     isSortable : function(col){
5688         if(typeof this.config[col].sortable == "undefined"){
5689             return this.defaultSortable;
5690         }
5691         return this.config[col].sortable;
5692     },
5693
5694     /**
5695      * Returns the rendering (formatting) function defined for the column.
5696      * @param {Number} col The column index.
5697      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5698      */
5699     getRenderer : function(col){
5700         if(!this.config[col].renderer){
5701             return Roo.grid.ColumnModel.defaultRenderer;
5702         }
5703         return this.config[col].renderer;
5704     },
5705
5706     /**
5707      * Sets the rendering (formatting) function for a column.
5708      * @param {Number} col The column index
5709      * @param {Function} fn The function to use to process the cell's raw data
5710      * to return HTML markup for the grid view. The render function is called with
5711      * the following parameters:<ul>
5712      * <li>Data value.</li>
5713      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5714      * <li>css A CSS style string to apply to the table cell.</li>
5715      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5716      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5717      * <li>Row index</li>
5718      * <li>Column index</li>
5719      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5720      */
5721     setRenderer : function(col, fn){
5722         this.config[col].renderer = fn;
5723     },
5724
5725     /**
5726      * Returns the width for the specified column.
5727      * @param {Number} col The column index
5728      * @return {Number}
5729      */
5730     getColumnWidth : function(col){
5731         return this.config[col].width * 1 || this.defaultWidth;
5732     },
5733
5734     /**
5735      * Sets the width for a column.
5736      * @param {Number} col The column index
5737      * @param {Number} width The new width
5738      */
5739     setColumnWidth : function(col, width, suppressEvent){
5740         this.config[col].width = width;
5741         this.totalWidth = null;
5742         if(!suppressEvent){
5743              this.fireEvent("widthchange", this, col, width);
5744         }
5745     },
5746
5747     /**
5748      * Returns the total width of all columns.
5749      * @param {Boolean} includeHidden True to include hidden column widths
5750      * @return {Number}
5751      */
5752     getTotalWidth : function(includeHidden){
5753         if(!this.totalWidth){
5754             this.totalWidth = 0;
5755             for(var i = 0, len = this.config.length; i < len; i++){
5756                 if(includeHidden || !this.isHidden(i)){
5757                     this.totalWidth += this.getColumnWidth(i);
5758                 }
5759             }
5760         }
5761         return this.totalWidth;
5762     },
5763
5764     /**
5765      * Returns the header for the specified column.
5766      * @param {Number} col The column index
5767      * @return {String}
5768      */
5769     getColumnHeader : function(col){
5770         return this.config[col].header;
5771     },
5772
5773     /**
5774      * Sets the header for a column.
5775      * @param {Number} col The column index
5776      * @param {String} header The new header
5777      */
5778     setColumnHeader : function(col, header){
5779         this.config[col].header = header;
5780         this.fireEvent("headerchange", this, col, header);
5781     },
5782
5783     /**
5784      * Returns the tooltip for the specified column.
5785      * @param {Number} col The column index
5786      * @return {String}
5787      */
5788     getColumnTooltip : function(col){
5789             return this.config[col].tooltip;
5790     },
5791     /**
5792      * Sets the tooltip for a column.
5793      * @param {Number} col The column index
5794      * @param {String} tooltip The new tooltip
5795      */
5796     setColumnTooltip : function(col, tooltip){
5797             this.config[col].tooltip = tooltip;
5798     },
5799
5800     /**
5801      * Returns the dataIndex for the specified column.
5802      * @param {Number} col The column index
5803      * @return {Number}
5804      */
5805     getDataIndex : function(col){
5806         return this.config[col].dataIndex;
5807     },
5808
5809     /**
5810      * Sets the dataIndex for a column.
5811      * @param {Number} col The column index
5812      * @param {Number} dataIndex The new dataIndex
5813      */
5814     setDataIndex : function(col, dataIndex){
5815         this.config[col].dataIndex = dataIndex;
5816     },
5817
5818     
5819     
5820     /**
5821      * Returns true if the cell is editable.
5822      * @param {Number} colIndex The column index
5823      * @param {Number} rowIndex The row index - this is nto actually used..?
5824      * @return {Boolean}
5825      */
5826     isCellEditable : function(colIndex, rowIndex){
5827         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5828     },
5829
5830     /**
5831      * Returns the editor defined for the cell/column.
5832      * return false or null to disable editing.
5833      * @param {Number} colIndex The column index
5834      * @param {Number} rowIndex The row index
5835      * @return {Object}
5836      */
5837     getCellEditor : function(colIndex, rowIndex){
5838         return this.config[colIndex].editor;
5839     },
5840
5841     /**
5842      * Sets if a column is editable.
5843      * @param {Number} col The column index
5844      * @param {Boolean} editable True if the column is editable
5845      */
5846     setEditable : function(col, editable){
5847         this.config[col].editable = editable;
5848     },
5849
5850
5851     /**
5852      * Returns true if the column is hidden.
5853      * @param {Number} colIndex The column index
5854      * @return {Boolean}
5855      */
5856     isHidden : function(colIndex){
5857         return this.config[colIndex].hidden;
5858     },
5859
5860
5861     /**
5862      * Returns true if the column width cannot be changed
5863      */
5864     isFixed : function(colIndex){
5865         return this.config[colIndex].fixed;
5866     },
5867
5868     /**
5869      * Returns true if the column can be resized
5870      * @return {Boolean}
5871      */
5872     isResizable : function(colIndex){
5873         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5874     },
5875     /**
5876      * Sets if a column is hidden.
5877      * @param {Number} colIndex The column index
5878      * @param {Boolean} hidden True if the column is hidden
5879      */
5880     setHidden : function(colIndex, hidden){
5881         this.config[colIndex].hidden = hidden;
5882         this.totalWidth = null;
5883         this.fireEvent("hiddenchange", this, colIndex, hidden);
5884     },
5885
5886     /**
5887      * Sets the editor for a column.
5888      * @param {Number} col The column index
5889      * @param {Object} editor The editor object
5890      */
5891     setEditor : function(col, editor){
5892         this.config[col].editor = editor;
5893     }
5894 });
5895
5896 Roo.grid.ColumnModel.defaultRenderer = function(value)
5897 {
5898     if(typeof value == "object") {
5899         return value;
5900     }
5901         if(typeof value == "string" && value.length < 1){
5902             return "&#160;";
5903         }
5904     
5905         return String.format("{0}", value);
5906 };
5907
5908 // Alias for backwards compatibility
5909 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5910 /*
5911  * Based on:
5912  * Ext JS Library 1.1.1
5913  * Copyright(c) 2006-2007, Ext JS, LLC.
5914  *
5915  * Originally Released Under LGPL - original licence link has changed is not relivant.
5916  *
5917  * Fork - LGPL
5918  * <script type="text/javascript">
5919  */
5920  
5921 /**
5922  * @class Roo.LoadMask
5923  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5924  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5925  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5926  * element's UpdateManager load indicator and will be destroyed after the initial load.
5927  * @constructor
5928  * Create a new LoadMask
5929  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5930  * @param {Object} config The config object
5931  */
5932 Roo.LoadMask = function(el, config){
5933     this.el = Roo.get(el);
5934     Roo.apply(this, config);
5935     if(this.store){
5936         this.store.on('beforeload', this.onBeforeLoad, this);
5937         this.store.on('load', this.onLoad, this);
5938         this.store.on('loadexception', this.onLoadException, this);
5939         this.removeMask = false;
5940     }else{
5941         var um = this.el.getUpdateManager();
5942         um.showLoadIndicator = false; // disable the default indicator
5943         um.on('beforeupdate', this.onBeforeLoad, this);
5944         um.on('update', this.onLoad, this);
5945         um.on('failure', this.onLoad, this);
5946         this.removeMask = true;
5947     }
5948 };
5949
5950 Roo.LoadMask.prototype = {
5951     /**
5952      * @cfg {Boolean} removeMask
5953      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5954      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5955      */
5956     /**
5957      * @cfg {String} msg
5958      * The text to display in a centered loading message box (defaults to 'Loading...')
5959      */
5960     msg : 'Loading...',
5961     /**
5962      * @cfg {String} msgCls
5963      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5964      */
5965     msgCls : 'x-mask-loading',
5966
5967     /**
5968      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5969      * @type Boolean
5970      */
5971     disabled: false,
5972
5973     /**
5974      * Disables the mask to prevent it from being displayed
5975      */
5976     disable : function(){
5977        this.disabled = true;
5978     },
5979
5980     /**
5981      * Enables the mask so that it can be displayed
5982      */
5983     enable : function(){
5984         this.disabled = false;
5985     },
5986     
5987     onLoadException : function()
5988     {
5989         Roo.log(arguments);
5990         
5991         if (typeof(arguments[3]) != 'undefined') {
5992             Roo.MessageBox.alert("Error loading",arguments[3]);
5993         } 
5994         /*
5995         try {
5996             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5997                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5998             }   
5999         } catch(e) {
6000             
6001         }
6002         */
6003     
6004         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6005     },
6006     // private
6007     onLoad : function()
6008     {
6009         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6010     },
6011
6012     // private
6013     onBeforeLoad : function(){
6014         if(!this.disabled){
6015             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6016         }
6017     },
6018
6019     // private
6020     destroy : function(){
6021         if(this.store){
6022             this.store.un('beforeload', this.onBeforeLoad, this);
6023             this.store.un('load', this.onLoad, this);
6024             this.store.un('loadexception', this.onLoadException, this);
6025         }else{
6026             var um = this.el.getUpdateManager();
6027             um.un('beforeupdate', this.onBeforeLoad, this);
6028             um.un('update', this.onLoad, this);
6029             um.un('failure', this.onLoad, this);
6030         }
6031     }
6032 };/*
6033  * - LGPL
6034  *
6035  * table
6036  * 
6037  */
6038
6039 /**
6040  * @class Roo.bootstrap.Table
6041  * @extends Roo.bootstrap.Component
6042  * Bootstrap Table class
6043  * @cfg {String} cls table class
6044  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6045  * @cfg {String} bgcolor Specifies the background color for a table
6046  * @cfg {Number} border Specifies whether the table cells should have borders or not
6047  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6048  * @cfg {Number} cellspacing Specifies the space between cells
6049  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6050  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6051  * @cfg {String} sortable Specifies that the table should be sortable
6052  * @cfg {String} summary Specifies a summary of the content of a table
6053  * @cfg {Number} width Specifies the width of a table
6054  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6055  * 
6056  * @cfg {boolean} striped Should the rows be alternative striped
6057  * @cfg {boolean} bordered Add borders to the table
6058  * @cfg {boolean} hover Add hover highlighting
6059  * @cfg {boolean} condensed Format condensed
6060  * @cfg {boolean} responsive Format condensed
6061  * @cfg {Boolean} loadMask (true|false) default false
6062  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6063  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6064  * @cfg {Boolean} rowSelection (true|false) default false
6065  * @cfg {Boolean} cellSelection (true|false) default false
6066  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6067  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6068  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6069  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6070  
6071  * 
6072  * @constructor
6073  * Create a new Table
6074  * @param {Object} config The config object
6075  */
6076
6077 Roo.bootstrap.Table = function(config){
6078     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6079     
6080   
6081     
6082     // BC...
6083     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6084     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6085     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6086     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6087     
6088     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6089     if (this.sm) {
6090         this.sm.grid = this;
6091         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6092         this.sm = this.selModel;
6093         this.sm.xmodule = this.xmodule || false;
6094     }
6095     
6096     if (this.cm && typeof(this.cm.config) == 'undefined') {
6097         this.colModel = new Roo.grid.ColumnModel(this.cm);
6098         this.cm = this.colModel;
6099         this.cm.xmodule = this.xmodule || false;
6100     }
6101     if (this.store) {
6102         this.store= Roo.factory(this.store, Roo.data);
6103         this.ds = this.store;
6104         this.ds.xmodule = this.xmodule || false;
6105          
6106     }
6107     if (this.footer && this.store) {
6108         this.footer.dataSource = this.ds;
6109         this.footer = Roo.factory(this.footer);
6110     }
6111     
6112     /** @private */
6113     this.addEvents({
6114         /**
6115          * @event cellclick
6116          * Fires when a cell is clicked
6117          * @param {Roo.bootstrap.Table} this
6118          * @param {Roo.Element} el
6119          * @param {Number} rowIndex
6120          * @param {Number} columnIndex
6121          * @param {Roo.EventObject} e
6122          */
6123         "cellclick" : true,
6124         /**
6125          * @event celldblclick
6126          * Fires when a cell is double clicked
6127          * @param {Roo.bootstrap.Table} this
6128          * @param {Roo.Element} el
6129          * @param {Number} rowIndex
6130          * @param {Number} columnIndex
6131          * @param {Roo.EventObject} e
6132          */
6133         "celldblclick" : true,
6134         /**
6135          * @event rowclick
6136          * Fires when a row is clicked
6137          * @param {Roo.bootstrap.Table} this
6138          * @param {Roo.Element} el
6139          * @param {Number} rowIndex
6140          * @param {Roo.EventObject} e
6141          */
6142         "rowclick" : true,
6143         /**
6144          * @event rowdblclick
6145          * Fires when a row is double clicked
6146          * @param {Roo.bootstrap.Table} this
6147          * @param {Roo.Element} el
6148          * @param {Number} rowIndex
6149          * @param {Roo.EventObject} e
6150          */
6151         "rowdblclick" : true,
6152         /**
6153          * @event mouseover
6154          * Fires when a mouseover occur
6155          * @param {Roo.bootstrap.Table} this
6156          * @param {Roo.Element} el
6157          * @param {Number} rowIndex
6158          * @param {Number} columnIndex
6159          * @param {Roo.EventObject} e
6160          */
6161         "mouseover" : true,
6162         /**
6163          * @event mouseout
6164          * Fires when a mouseout occur
6165          * @param {Roo.bootstrap.Table} this
6166          * @param {Roo.Element} el
6167          * @param {Number} rowIndex
6168          * @param {Number} columnIndex
6169          * @param {Roo.EventObject} e
6170          */
6171         "mouseout" : true,
6172         /**
6173          * @event rowclass
6174          * Fires when a row is rendered, so you can change add a style to it.
6175          * @param {Roo.bootstrap.Table} this
6176          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6177          */
6178         'rowclass' : true,
6179           /**
6180          * @event rowsrendered
6181          * Fires when all the  rows have been rendered
6182          * @param {Roo.bootstrap.Table} this
6183          */
6184         'rowsrendered' : true,
6185         /**
6186          * @event contextmenu
6187          * The raw contextmenu event for the entire grid.
6188          * @param {Roo.EventObject} e
6189          */
6190         "contextmenu" : true,
6191         /**
6192          * @event rowcontextmenu
6193          * Fires when a row is right clicked
6194          * @param {Roo.bootstrap.Table} this
6195          * @param {Number} rowIndex
6196          * @param {Roo.EventObject} e
6197          */
6198         "rowcontextmenu" : true,
6199         /**
6200          * @event cellcontextmenu
6201          * Fires when a cell is right clicked
6202          * @param {Roo.bootstrap.Table} this
6203          * @param {Number} rowIndex
6204          * @param {Number} cellIndex
6205          * @param {Roo.EventObject} e
6206          */
6207          "cellcontextmenu" : true,
6208          /**
6209          * @event headercontextmenu
6210          * Fires when a header is right clicked
6211          * @param {Roo.bootstrap.Table} this
6212          * @param {Number} columnIndex
6213          * @param {Roo.EventObject} e
6214          */
6215         "headercontextmenu" : true
6216     });
6217 };
6218
6219 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6220     
6221     cls: false,
6222     align: false,
6223     bgcolor: false,
6224     border: false,
6225     cellpadding: false,
6226     cellspacing: false,
6227     frame: false,
6228     rules: false,
6229     sortable: false,
6230     summary: false,
6231     width: false,
6232     striped : false,
6233     scrollBody : false,
6234     bordered: false,
6235     hover:  false,
6236     condensed : false,
6237     responsive : false,
6238     sm : false,
6239     cm : false,
6240     store : false,
6241     loadMask : false,
6242     footerShow : true,
6243     headerShow : true,
6244   
6245     rowSelection : false,
6246     cellSelection : false,
6247     layout : false,
6248     
6249     // Roo.Element - the tbody
6250     mainBody: false,
6251     // Roo.Element - thead element
6252     mainHead: false,
6253     
6254     container: false, // used by gridpanel...
6255     
6256     lazyLoad : false,
6257     
6258     CSS : Roo.util.CSS,
6259     
6260     auto_hide_footer : false,
6261     
6262     getAutoCreate : function()
6263     {
6264         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6265         
6266         cfg = {
6267             tag: 'table',
6268             cls : 'table',
6269             cn : []
6270         };
6271         if (this.scrollBody) {
6272             cfg.cls += ' table-body-fixed';
6273         }    
6274         if (this.striped) {
6275             cfg.cls += ' table-striped';
6276         }
6277         
6278         if (this.hover) {
6279             cfg.cls += ' table-hover';
6280         }
6281         if (this.bordered) {
6282             cfg.cls += ' table-bordered';
6283         }
6284         if (this.condensed) {
6285             cfg.cls += ' table-condensed';
6286         }
6287         if (this.responsive) {
6288             cfg.cls += ' table-responsive';
6289         }
6290         
6291         if (this.cls) {
6292             cfg.cls+=  ' ' +this.cls;
6293         }
6294         
6295         // this lot should be simplifed...
6296         var _t = this;
6297         var cp = [
6298             'align',
6299             'bgcolor',
6300             'border',
6301             'cellpadding',
6302             'cellspacing',
6303             'frame',
6304             'rules',
6305             'sortable',
6306             'summary',
6307             'width'
6308         ].forEach(function(k) {
6309             if (_t[k]) {
6310                 cfg[k] = _t[k];
6311             }
6312         });
6313         
6314         
6315         if (this.layout) {
6316             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6317         }
6318         
6319         if(this.store || this.cm){
6320             if(this.headerShow){
6321                 cfg.cn.push(this.renderHeader());
6322             }
6323             
6324             cfg.cn.push(this.renderBody());
6325             
6326             if(this.footerShow){
6327                 cfg.cn.push(this.renderFooter());
6328             }
6329             // where does this come from?
6330             //cfg.cls+=  ' TableGrid';
6331         }
6332         
6333         return { cn : [ cfg ] };
6334     },
6335     
6336     initEvents : function()
6337     {   
6338         if(!this.store || !this.cm){
6339             return;
6340         }
6341         if (this.selModel) {
6342             this.selModel.initEvents();
6343         }
6344         
6345         
6346         //Roo.log('initEvents with ds!!!!');
6347         
6348         this.mainBody = this.el.select('tbody', true).first();
6349         this.mainHead = this.el.select('thead', true).first();
6350         this.mainFoot = this.el.select('tfoot', true).first();
6351         
6352         
6353         
6354         var _this = this;
6355         
6356         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6357             e.on('click', _this.sort, _this);
6358         });
6359         
6360         this.mainBody.on("click", this.onClick, this);
6361         this.mainBody.on("dblclick", this.onDblClick, this);
6362         
6363         // why is this done????? = it breaks dialogs??
6364         //this.parent().el.setStyle('position', 'relative');
6365         
6366         
6367         if (this.footer) {
6368             this.footer.parentId = this.id;
6369             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6370             
6371             if(this.lazyLoad){
6372                 this.el.select('tfoot tr td').first().addClass('hide');
6373             }
6374         } 
6375         
6376         if(this.loadMask) {
6377             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6378         }
6379         
6380         this.store.on('load', this.onLoad, this);
6381         this.store.on('beforeload', this.onBeforeLoad, this);
6382         this.store.on('update', this.onUpdate, this);
6383         this.store.on('add', this.onAdd, this);
6384         this.store.on("clear", this.clear, this);
6385         
6386         this.el.on("contextmenu", this.onContextMenu, this);
6387         
6388         this.mainBody.on('scroll', this.onBodyScroll, this);
6389         
6390         this.cm.on("headerchange", this.onHeaderChange, this);
6391         
6392         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6393         
6394     },
6395     
6396     onContextMenu : function(e, t)
6397     {
6398         this.processEvent("contextmenu", e);
6399     },
6400     
6401     processEvent : function(name, e)
6402     {
6403         if (name != 'touchstart' ) {
6404             this.fireEvent(name, e);    
6405         }
6406         
6407         var t = e.getTarget();
6408         
6409         var cell = Roo.get(t);
6410         
6411         if(!cell){
6412             return;
6413         }
6414         
6415         if(cell.findParent('tfoot', false, true)){
6416             return;
6417         }
6418         
6419         if(cell.findParent('thead', false, true)){
6420             
6421             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6422                 cell = Roo.get(t).findParent('th', false, true);
6423                 if (!cell) {
6424                     Roo.log("failed to find th in thead?");
6425                     Roo.log(e.getTarget());
6426                     return;
6427                 }
6428             }
6429             
6430             var cellIndex = cell.dom.cellIndex;
6431             
6432             var ename = name == 'touchstart' ? 'click' : name;
6433             this.fireEvent("header" + ename, this, cellIndex, e);
6434             
6435             return;
6436         }
6437         
6438         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6439             cell = Roo.get(t).findParent('td', false, true);
6440             if (!cell) {
6441                 Roo.log("failed to find th in tbody?");
6442                 Roo.log(e.getTarget());
6443                 return;
6444             }
6445         }
6446         
6447         var row = cell.findParent('tr', false, true);
6448         var cellIndex = cell.dom.cellIndex;
6449         var rowIndex = row.dom.rowIndex - 1;
6450         
6451         if(row !== false){
6452             
6453             this.fireEvent("row" + name, this, rowIndex, e);
6454             
6455             if(cell !== false){
6456             
6457                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6458             }
6459         }
6460         
6461     },
6462     
6463     onMouseover : function(e, el)
6464     {
6465         var cell = Roo.get(el);
6466         
6467         if(!cell){
6468             return;
6469         }
6470         
6471         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6472             cell = cell.findParent('td', false, true);
6473         }
6474         
6475         var row = cell.findParent('tr', false, true);
6476         var cellIndex = cell.dom.cellIndex;
6477         var rowIndex = row.dom.rowIndex - 1; // start from 0
6478         
6479         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6480         
6481     },
6482     
6483     onMouseout : function(e, el)
6484     {
6485         var cell = Roo.get(el);
6486         
6487         if(!cell){
6488             return;
6489         }
6490         
6491         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6492             cell = cell.findParent('td', false, true);
6493         }
6494         
6495         var row = cell.findParent('tr', false, true);
6496         var cellIndex = cell.dom.cellIndex;
6497         var rowIndex = row.dom.rowIndex - 1; // start from 0
6498         
6499         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6500         
6501     },
6502     
6503     onClick : function(e, el)
6504     {
6505         var cell = Roo.get(el);
6506         
6507         if(!cell || (!this.cellSelection && !this.rowSelection)){
6508             return;
6509         }
6510         
6511         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6512             cell = cell.findParent('td', false, true);
6513         }
6514         
6515         if(!cell || typeof(cell) == 'undefined'){
6516             return;
6517         }
6518         
6519         var row = cell.findParent('tr', false, true);
6520         
6521         if(!row || typeof(row) == 'undefined'){
6522             return;
6523         }
6524         
6525         var cellIndex = cell.dom.cellIndex;
6526         var rowIndex = this.getRowIndex(row);
6527         
6528         // why??? - should these not be based on SelectionModel?
6529         if(this.cellSelection){
6530             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6531         }
6532         
6533         if(this.rowSelection){
6534             this.fireEvent('rowclick', this, row, rowIndex, e);
6535         }
6536         
6537         
6538     },
6539         
6540     onDblClick : function(e,el)
6541     {
6542         var cell = Roo.get(el);
6543         
6544         if(!cell || (!this.cellSelection && !this.rowSelection)){
6545             return;
6546         }
6547         
6548         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549             cell = cell.findParent('td', false, true);
6550         }
6551         
6552         if(!cell || typeof(cell) == 'undefined'){
6553             return;
6554         }
6555         
6556         var row = cell.findParent('tr', false, true);
6557         
6558         if(!row || typeof(row) == 'undefined'){
6559             return;
6560         }
6561         
6562         var cellIndex = cell.dom.cellIndex;
6563         var rowIndex = this.getRowIndex(row);
6564         
6565         if(this.cellSelection){
6566             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6567         }
6568         
6569         if(this.rowSelection){
6570             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6571         }
6572     },
6573     
6574     sort : function(e,el)
6575     {
6576         var col = Roo.get(el);
6577         
6578         if(!col.hasClass('sortable')){
6579             return;
6580         }
6581         
6582         var sort = col.attr('sort');
6583         var dir = 'ASC';
6584         
6585         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6586             dir = 'DESC';
6587         }
6588         
6589         this.store.sortInfo = {field : sort, direction : dir};
6590         
6591         if (this.footer) {
6592             Roo.log("calling footer first");
6593             this.footer.onClick('first');
6594         } else {
6595         
6596             this.store.load({ params : { start : 0 } });
6597         }
6598     },
6599     
6600     renderHeader : function()
6601     {
6602         var header = {
6603             tag: 'thead',
6604             cn : []
6605         };
6606         
6607         var cm = this.cm;
6608         this.totalWidth = 0;
6609         
6610         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6611             
6612             var config = cm.config[i];
6613             
6614             var c = {
6615                 tag: 'th',
6616                 cls : 'x-hcol-' + i,
6617                 style : '',
6618                 html: cm.getColumnHeader(i)
6619             };
6620             
6621             var hh = '';
6622             
6623             if(typeof(config.sortable) != 'undefined' && config.sortable){
6624                 c.cls = 'sortable';
6625                 c.html = '<i class="glyphicon"></i>' + c.html;
6626             }
6627             
6628             if(typeof(config.lgHeader) != 'undefined'){
6629                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6630             }
6631             
6632             if(typeof(config.mdHeader) != 'undefined'){
6633                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6634             }
6635             
6636             if(typeof(config.smHeader) != 'undefined'){
6637                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6638             }
6639             
6640             if(typeof(config.xsHeader) != 'undefined'){
6641                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6642             }
6643             
6644             if(hh.length){
6645                 c.html = hh;
6646             }
6647             
6648             if(typeof(config.tooltip) != 'undefined'){
6649                 c.tooltip = config.tooltip;
6650             }
6651             
6652             if(typeof(config.colspan) != 'undefined'){
6653                 c.colspan = config.colspan;
6654             }
6655             
6656             if(typeof(config.hidden) != 'undefined' && config.hidden){
6657                 c.style += ' display:none;';
6658             }
6659             
6660             if(typeof(config.dataIndex) != 'undefined'){
6661                 c.sort = config.dataIndex;
6662             }
6663             
6664            
6665             
6666             if(typeof(config.align) != 'undefined' && config.align.length){
6667                 c.style += ' text-align:' + config.align + ';';
6668             }
6669             
6670             if(typeof(config.width) != 'undefined'){
6671                 c.style += ' width:' + config.width + 'px;';
6672                 this.totalWidth += config.width;
6673             } else {
6674                 this.totalWidth += 100; // assume minimum of 100 per column?
6675             }
6676             
6677             if(typeof(config.cls) != 'undefined'){
6678                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6679             }
6680             
6681             ['xs','sm','md','lg'].map(function(size){
6682                 
6683                 if(typeof(config[size]) == 'undefined'){
6684                     return;
6685                 }
6686                 
6687                 if (!config[size]) { // 0 = hidden
6688                     c.cls += ' hidden-' + size;
6689                     return;
6690                 }
6691                 
6692                 c.cls += ' col-' + size + '-' + config[size];
6693
6694             });
6695             
6696             header.cn.push(c)
6697         }
6698         
6699         return header;
6700     },
6701     
6702     renderBody : function()
6703     {
6704         var body = {
6705             tag: 'tbody',
6706             cn : [
6707                 {
6708                     tag: 'tr',
6709                     cn : [
6710                         {
6711                             tag : 'td',
6712                             colspan :  this.cm.getColumnCount()
6713                         }
6714                     ]
6715                 }
6716             ]
6717         };
6718         
6719         return body;
6720     },
6721     
6722     renderFooter : function()
6723     {
6724         var footer = {
6725             tag: 'tfoot',
6726             cn : [
6727                 {
6728                     tag: 'tr',
6729                     cn : [
6730                         {
6731                             tag : 'td',
6732                             colspan :  this.cm.getColumnCount()
6733                         }
6734                     ]
6735                 }
6736             ]
6737         };
6738         
6739         return footer;
6740     },
6741     
6742     
6743     
6744     onLoad : function()
6745     {
6746 //        Roo.log('ds onload');
6747         this.clear();
6748         
6749         var _this = this;
6750         var cm = this.cm;
6751         var ds = this.store;
6752         
6753         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6754             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6755             if (_this.store.sortInfo) {
6756                     
6757                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6758                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6759                 }
6760                 
6761                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6762                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6763                 }
6764             }
6765         });
6766         
6767         var tbody =  this.mainBody;
6768               
6769         if(ds.getCount() > 0){
6770             ds.data.each(function(d,rowIndex){
6771                 var row =  this.renderRow(cm, ds, rowIndex);
6772                 
6773                 tbody.createChild(row);
6774                 
6775                 var _this = this;
6776                 
6777                 if(row.cellObjects.length){
6778                     Roo.each(row.cellObjects, function(r){
6779                         _this.renderCellObject(r);
6780                     })
6781                 }
6782                 
6783             }, this);
6784         }
6785         
6786         var tfoot = this.el.select('tfoot', true).first();
6787         
6788         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6789             
6790             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6791             
6792             var total = this.ds.getTotalCount();
6793             
6794             if(this.footer.pageSize < total){
6795                 this.mainFoot.show();
6796             }
6797         }
6798         
6799         Roo.each(this.el.select('tbody td', true).elements, function(e){
6800             e.on('mouseover', _this.onMouseover, _this);
6801         });
6802         
6803         Roo.each(this.el.select('tbody td', true).elements, function(e){
6804             e.on('mouseout', _this.onMouseout, _this);
6805         });
6806         this.fireEvent('rowsrendered', this);
6807         
6808         this.autoSize();
6809     },
6810     
6811     
6812     onUpdate : function(ds,record)
6813     {
6814         this.refreshRow(record);
6815         this.autoSize();
6816     },
6817     
6818     onRemove : function(ds, record, index, isUpdate){
6819         if(isUpdate !== true){
6820             this.fireEvent("beforerowremoved", this, index, record);
6821         }
6822         var bt = this.mainBody.dom;
6823         
6824         var rows = this.el.select('tbody > tr', true).elements;
6825         
6826         if(typeof(rows[index]) != 'undefined'){
6827             bt.removeChild(rows[index].dom);
6828         }
6829         
6830 //        if(bt.rows[index]){
6831 //            bt.removeChild(bt.rows[index]);
6832 //        }
6833         
6834         if(isUpdate !== true){
6835             //this.stripeRows(index);
6836             //this.syncRowHeights(index, index);
6837             //this.layout();
6838             this.fireEvent("rowremoved", this, index, record);
6839         }
6840     },
6841     
6842     onAdd : function(ds, records, rowIndex)
6843     {
6844         //Roo.log('on Add called');
6845         // - note this does not handle multiple adding very well..
6846         var bt = this.mainBody.dom;
6847         for (var i =0 ; i < records.length;i++) {
6848             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6849             //Roo.log(records[i]);
6850             //Roo.log(this.store.getAt(rowIndex+i));
6851             this.insertRow(this.store, rowIndex + i, false);
6852             return;
6853         }
6854         
6855     },
6856     
6857     
6858     refreshRow : function(record){
6859         var ds = this.store, index;
6860         if(typeof record == 'number'){
6861             index = record;
6862             record = ds.getAt(index);
6863         }else{
6864             index = ds.indexOf(record);
6865         }
6866         this.insertRow(ds, index, true);
6867         this.autoSize();
6868         this.onRemove(ds, record, index+1, true);
6869         this.autoSize();
6870         //this.syncRowHeights(index, index);
6871         //this.layout();
6872         this.fireEvent("rowupdated", this, index, record);
6873     },
6874     
6875     insertRow : function(dm, rowIndex, isUpdate){
6876         
6877         if(!isUpdate){
6878             this.fireEvent("beforerowsinserted", this, rowIndex);
6879         }
6880             //var s = this.getScrollState();
6881         var row = this.renderRow(this.cm, this.store, rowIndex);
6882         // insert before rowIndex..
6883         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6884         
6885         var _this = this;
6886                 
6887         if(row.cellObjects.length){
6888             Roo.each(row.cellObjects, function(r){
6889                 _this.renderCellObject(r);
6890             })
6891         }
6892             
6893         if(!isUpdate){
6894             this.fireEvent("rowsinserted", this, rowIndex);
6895             //this.syncRowHeights(firstRow, lastRow);
6896             //this.stripeRows(firstRow);
6897             //this.layout();
6898         }
6899         
6900     },
6901     
6902     
6903     getRowDom : function(rowIndex)
6904     {
6905         var rows = this.el.select('tbody > tr', true).elements;
6906         
6907         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6908         
6909     },
6910     // returns the object tree for a tr..
6911   
6912     
6913     renderRow : function(cm, ds, rowIndex) 
6914     {
6915         var d = ds.getAt(rowIndex);
6916         
6917         var row = {
6918             tag : 'tr',
6919             cls : 'x-row-' + rowIndex,
6920             cn : []
6921         };
6922             
6923         var cellObjects = [];
6924         
6925         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6926             var config = cm.config[i];
6927             
6928             var renderer = cm.getRenderer(i);
6929             var value = '';
6930             var id = false;
6931             
6932             if(typeof(renderer) !== 'undefined'){
6933                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6934             }
6935             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6936             // and are rendered into the cells after the row is rendered - using the id for the element.
6937             
6938             if(typeof(value) === 'object'){
6939                 id = Roo.id();
6940                 cellObjects.push({
6941                     container : id,
6942                     cfg : value 
6943                 })
6944             }
6945             
6946             var rowcfg = {
6947                 record: d,
6948                 rowIndex : rowIndex,
6949                 colIndex : i,
6950                 rowClass : ''
6951             };
6952
6953             this.fireEvent('rowclass', this, rowcfg);
6954             
6955             var td = {
6956                 tag: 'td',
6957                 cls : rowcfg.rowClass + ' x-col-' + i,
6958                 style: '',
6959                 html: (typeof(value) === 'object') ? '' : value
6960             };
6961             
6962             if (id) {
6963                 td.id = id;
6964             }
6965             
6966             if(typeof(config.colspan) != 'undefined'){
6967                 td.colspan = config.colspan;
6968             }
6969             
6970             if(typeof(config.hidden) != 'undefined' && config.hidden){
6971                 td.style += ' display:none;';
6972             }
6973             
6974             if(typeof(config.align) != 'undefined' && config.align.length){
6975                 td.style += ' text-align:' + config.align + ';';
6976             }
6977             if(typeof(config.valign) != 'undefined' && config.valign.length){
6978                 td.style += ' vertical-align:' + config.valign + ';';
6979             }
6980             
6981             if(typeof(config.width) != 'undefined'){
6982                 td.style += ' width:' +  config.width + 'px;';
6983             }
6984             
6985             if(typeof(config.cursor) != 'undefined'){
6986                 td.style += ' cursor:' +  config.cursor + ';';
6987             }
6988             
6989             if(typeof(config.cls) != 'undefined'){
6990                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6991             }
6992             
6993             ['xs','sm','md','lg'].map(function(size){
6994                 
6995                 if(typeof(config[size]) == 'undefined'){
6996                     return;
6997                 }
6998                 
6999                 if (!config[size]) { // 0 = hidden
7000                     td.cls += ' hidden-' + size;
7001                     return;
7002                 }
7003                 
7004                 td.cls += ' col-' + size + '-' + config[size];
7005
7006             });
7007             
7008             row.cn.push(td);
7009            
7010         }
7011         
7012         row.cellObjects = cellObjects;
7013         
7014         return row;
7015           
7016     },
7017     
7018     
7019     
7020     onBeforeLoad : function()
7021     {
7022         
7023     },
7024      /**
7025      * Remove all rows
7026      */
7027     clear : function()
7028     {
7029         this.el.select('tbody', true).first().dom.innerHTML = '';
7030     },
7031     /**
7032      * Show or hide a row.
7033      * @param {Number} rowIndex to show or hide
7034      * @param {Boolean} state hide
7035      */
7036     setRowVisibility : function(rowIndex, state)
7037     {
7038         var bt = this.mainBody.dom;
7039         
7040         var rows = this.el.select('tbody > tr', true).elements;
7041         
7042         if(typeof(rows[rowIndex]) == 'undefined'){
7043             return;
7044         }
7045         rows[rowIndex].dom.style.display = state ? '' : 'none';
7046     },
7047     
7048     
7049     getSelectionModel : function(){
7050         if(!this.selModel){
7051             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7052         }
7053         return this.selModel;
7054     },
7055     /*
7056      * Render the Roo.bootstrap object from renderder
7057      */
7058     renderCellObject : function(r)
7059     {
7060         var _this = this;
7061         
7062         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7063         
7064         var t = r.cfg.render(r.container);
7065         
7066         if(r.cfg.cn){
7067             Roo.each(r.cfg.cn, function(c){
7068                 var child = {
7069                     container: t.getChildContainer(),
7070                     cfg: c
7071                 };
7072                 _this.renderCellObject(child);
7073             })
7074         }
7075     },
7076     
7077     getRowIndex : function(row)
7078     {
7079         var rowIndex = -1;
7080         
7081         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7082             if(el != row){
7083                 return;
7084             }
7085             
7086             rowIndex = index;
7087         });
7088         
7089         return rowIndex;
7090     },
7091      /**
7092      * Returns the grid's underlying element = used by panel.Grid
7093      * @return {Element} The element
7094      */
7095     getGridEl : function(){
7096         return this.el;
7097     },
7098      /**
7099      * Forces a resize - used by panel.Grid
7100      * @return {Element} The element
7101      */
7102     autoSize : function()
7103     {
7104         //var ctr = Roo.get(this.container.dom.parentElement);
7105         var ctr = Roo.get(this.el.dom);
7106         
7107         var thd = this.getGridEl().select('thead',true).first();
7108         var tbd = this.getGridEl().select('tbody', true).first();
7109         var tfd = this.getGridEl().select('tfoot', true).first();
7110         
7111         var cw = ctr.getWidth();
7112         
7113         if (tbd) {
7114             
7115             tbd.setSize(ctr.getWidth(),
7116                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7117             );
7118             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7119             cw -= barsize;
7120         }
7121         cw = Math.max(cw, this.totalWidth);
7122         this.getGridEl().select('tr',true).setWidth(cw);
7123         // resize 'expandable coloumn?
7124         
7125         return; // we doe not have a view in this design..
7126         
7127     },
7128     onBodyScroll: function()
7129     {
7130         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7131         if(this.mainHead){
7132             this.mainHead.setStyle({
7133                 'position' : 'relative',
7134                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7135             });
7136         }
7137         
7138         if(this.lazyLoad){
7139             
7140             var scrollHeight = this.mainBody.dom.scrollHeight;
7141             
7142             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7143             
7144             var height = this.mainBody.getHeight();
7145             
7146             if(scrollHeight - height == scrollTop) {
7147                 
7148                 var total = this.ds.getTotalCount();
7149                 
7150                 if(this.footer.cursor + this.footer.pageSize < total){
7151                     
7152                     this.footer.ds.load({
7153                         params : {
7154                             start : this.footer.cursor + this.footer.pageSize,
7155                             limit : this.footer.pageSize
7156                         },
7157                         add : true
7158                     });
7159                 }
7160             }
7161             
7162         }
7163     },
7164     
7165     onHeaderChange : function()
7166     {
7167         var header = this.renderHeader();
7168         var table = this.el.select('table', true).first();
7169         
7170         this.mainHead.remove();
7171         this.mainHead = table.createChild(header, this.mainBody, false);
7172     },
7173     
7174     onHiddenChange : function(colModel, colIndex, hidden)
7175     {
7176         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7177         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7178         
7179         this.CSS.updateRule(thSelector, "display", "");
7180         this.CSS.updateRule(tdSelector, "display", "");
7181         
7182         if(hidden){
7183             this.CSS.updateRule(thSelector, "display", "none");
7184             this.CSS.updateRule(tdSelector, "display", "none");
7185         }
7186         
7187         this.onHeaderChange();
7188         this.onLoad();
7189     },
7190     
7191     setColumnWidth: function(col_index, width)
7192     {
7193         // width = "md-2 xs-2..."
7194         if(!this.colModel.config[col_index]) {
7195             return;
7196         }
7197         
7198         var w = width.split(" ");
7199         
7200         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7201         
7202         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7203         
7204         
7205         for(var j = 0; j < w.length; j++) {
7206             
7207             if(!w[j]) {
7208                 continue;
7209             }
7210             
7211             var size_cls = w[j].split("-");
7212             
7213             if(!Number.isInteger(size_cls[1] * 1)) {
7214                 continue;
7215             }
7216             
7217             if(!this.colModel.config[col_index][size_cls[0]]) {
7218                 continue;
7219             }
7220             
7221             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7222                 continue;
7223             }
7224             
7225             h_row[0].classList.replace(
7226                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7227                 "col-"+size_cls[0]+"-"+size_cls[1]
7228             );
7229             
7230             for(var i = 0; i < rows.length; i++) {
7231                 
7232                 var size_cls = w[j].split("-");
7233                 
7234                 if(!Number.isInteger(size_cls[1] * 1)) {
7235                     continue;
7236                 }
7237                 
7238                 if(!this.colModel.config[col_index][size_cls[0]]) {
7239                     continue;
7240                 }
7241                 
7242                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7243                     continue;
7244                 }
7245                 
7246                 rows[i].classList.replace(
7247                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7248                     "col-"+size_cls[0]+"-"+size_cls[1]
7249                 );
7250             }
7251             
7252             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7253         }
7254     }
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * table cell
7263  * 
7264  */
7265
7266 /**
7267  * @class Roo.bootstrap.TableCell
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap TableCell class
7270  * @cfg {String} html cell contain text
7271  * @cfg {String} cls cell class
7272  * @cfg {String} tag cell tag (td|th) default td
7273  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7274  * @cfg {String} align Aligns the content in a cell
7275  * @cfg {String} axis Categorizes cells
7276  * @cfg {String} bgcolor Specifies the background color of a cell
7277  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7278  * @cfg {Number} colspan Specifies the number of columns a cell should span
7279  * @cfg {String} headers Specifies one or more header cells a cell is related to
7280  * @cfg {Number} height Sets the height of a cell
7281  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7282  * @cfg {Number} rowspan Sets the number of rows a cell should span
7283  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7284  * @cfg {String} valign Vertical aligns the content in a cell
7285  * @cfg {Number} width Specifies the width of a cell
7286  * 
7287  * @constructor
7288  * Create a new TableCell
7289  * @param {Object} config The config object
7290  */
7291
7292 Roo.bootstrap.TableCell = function(config){
7293     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7294 };
7295
7296 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7297     
7298     html: false,
7299     cls: false,
7300     tag: false,
7301     abbr: false,
7302     align: false,
7303     axis: false,
7304     bgcolor: false,
7305     charoff: false,
7306     colspan: false,
7307     headers: false,
7308     height: false,
7309     nowrap: false,
7310     rowspan: false,
7311     scope: false,
7312     valign: false,
7313     width: false,
7314     
7315     
7316     getAutoCreate : function(){
7317         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7318         
7319         cfg = {
7320             tag: 'td'
7321         };
7322         
7323         if(this.tag){
7324             cfg.tag = this.tag;
7325         }
7326         
7327         if (this.html) {
7328             cfg.html=this.html
7329         }
7330         if (this.cls) {
7331             cfg.cls=this.cls
7332         }
7333         if (this.abbr) {
7334             cfg.abbr=this.abbr
7335         }
7336         if (this.align) {
7337             cfg.align=this.align
7338         }
7339         if (this.axis) {
7340             cfg.axis=this.axis
7341         }
7342         if (this.bgcolor) {
7343             cfg.bgcolor=this.bgcolor
7344         }
7345         if (this.charoff) {
7346             cfg.charoff=this.charoff
7347         }
7348         if (this.colspan) {
7349             cfg.colspan=this.colspan
7350         }
7351         if (this.headers) {
7352             cfg.headers=this.headers
7353         }
7354         if (this.height) {
7355             cfg.height=this.height
7356         }
7357         if (this.nowrap) {
7358             cfg.nowrap=this.nowrap
7359         }
7360         if (this.rowspan) {
7361             cfg.rowspan=this.rowspan
7362         }
7363         if (this.scope) {
7364             cfg.scope=this.scope
7365         }
7366         if (this.valign) {
7367             cfg.valign=this.valign
7368         }
7369         if (this.width) {
7370             cfg.width=this.width
7371         }
7372         
7373         
7374         return cfg;
7375     }
7376    
7377 });
7378
7379  
7380
7381  /*
7382  * - LGPL
7383  *
7384  * table row
7385  * 
7386  */
7387
7388 /**
7389  * @class Roo.bootstrap.TableRow
7390  * @extends Roo.bootstrap.Component
7391  * Bootstrap TableRow class
7392  * @cfg {String} cls row class
7393  * @cfg {String} align Aligns the content in a table row
7394  * @cfg {String} bgcolor Specifies a background color for a table row
7395  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7396  * @cfg {String} valign Vertical aligns the content in a table row
7397  * 
7398  * @constructor
7399  * Create a new TableRow
7400  * @param {Object} config The config object
7401  */
7402
7403 Roo.bootstrap.TableRow = function(config){
7404     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7405 };
7406
7407 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7408     
7409     cls: false,
7410     align: false,
7411     bgcolor: false,
7412     charoff: false,
7413     valign: false,
7414     
7415     getAutoCreate : function(){
7416         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7417         
7418         cfg = {
7419             tag: 'tr'
7420         };
7421             
7422         if(this.cls){
7423             cfg.cls = this.cls;
7424         }
7425         if(this.align){
7426             cfg.align = this.align;
7427         }
7428         if(this.bgcolor){
7429             cfg.bgcolor = this.bgcolor;
7430         }
7431         if(this.charoff){
7432             cfg.charoff = this.charoff;
7433         }
7434         if(this.valign){
7435             cfg.valign = this.valign;
7436         }
7437         
7438         return cfg;
7439     }
7440    
7441 });
7442
7443  
7444
7445  /*
7446  * - LGPL
7447  *
7448  * table body
7449  * 
7450  */
7451
7452 /**
7453  * @class Roo.bootstrap.TableBody
7454  * @extends Roo.bootstrap.Component
7455  * Bootstrap TableBody class
7456  * @cfg {String} cls element class
7457  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7458  * @cfg {String} align Aligns the content inside the element
7459  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7460  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7461  * 
7462  * @constructor
7463  * Create a new TableBody
7464  * @param {Object} config The config object
7465  */
7466
7467 Roo.bootstrap.TableBody = function(config){
7468     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7469 };
7470
7471 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7472     
7473     cls: false,
7474     tag: false,
7475     align: false,
7476     charoff: false,
7477     valign: false,
7478     
7479     getAutoCreate : function(){
7480         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7481         
7482         cfg = {
7483             tag: 'tbody'
7484         };
7485             
7486         if (this.cls) {
7487             cfg.cls=this.cls
7488         }
7489         if(this.tag){
7490             cfg.tag = this.tag;
7491         }
7492         
7493         if(this.align){
7494             cfg.align = this.align;
7495         }
7496         if(this.charoff){
7497             cfg.charoff = this.charoff;
7498         }
7499         if(this.valign){
7500             cfg.valign = this.valign;
7501         }
7502         
7503         return cfg;
7504     }
7505     
7506     
7507 //    initEvents : function()
7508 //    {
7509 //        
7510 //        if(!this.store){
7511 //            return;
7512 //        }
7513 //        
7514 //        this.store = Roo.factory(this.store, Roo.data);
7515 //        this.store.on('load', this.onLoad, this);
7516 //        
7517 //        this.store.load();
7518 //        
7519 //    },
7520 //    
7521 //    onLoad: function () 
7522 //    {   
7523 //        this.fireEvent('load', this);
7524 //    }
7525 //    
7526 //   
7527 });
7528
7529  
7530
7531  /*
7532  * Based on:
7533  * Ext JS Library 1.1.1
7534  * Copyright(c) 2006-2007, Ext JS, LLC.
7535  *
7536  * Originally Released Under LGPL - original licence link has changed is not relivant.
7537  *
7538  * Fork - LGPL
7539  * <script type="text/javascript">
7540  */
7541
7542 // as we use this in bootstrap.
7543 Roo.namespace('Roo.form');
7544  /**
7545  * @class Roo.form.Action
7546  * Internal Class used to handle form actions
7547  * @constructor
7548  * @param {Roo.form.BasicForm} el The form element or its id
7549  * @param {Object} config Configuration options
7550  */
7551
7552  
7553  
7554 // define the action interface
7555 Roo.form.Action = function(form, options){
7556     this.form = form;
7557     this.options = options || {};
7558 };
7559 /**
7560  * Client Validation Failed
7561  * @const 
7562  */
7563 Roo.form.Action.CLIENT_INVALID = 'client';
7564 /**
7565  * Server Validation Failed
7566  * @const 
7567  */
7568 Roo.form.Action.SERVER_INVALID = 'server';
7569  /**
7570  * Connect to Server Failed
7571  * @const 
7572  */
7573 Roo.form.Action.CONNECT_FAILURE = 'connect';
7574 /**
7575  * Reading Data from Server Failed
7576  * @const 
7577  */
7578 Roo.form.Action.LOAD_FAILURE = 'load';
7579
7580 Roo.form.Action.prototype = {
7581     type : 'default',
7582     failureType : undefined,
7583     response : undefined,
7584     result : undefined,
7585
7586     // interface method
7587     run : function(options){
7588
7589     },
7590
7591     // interface method
7592     success : function(response){
7593
7594     },
7595
7596     // interface method
7597     handleResponse : function(response){
7598
7599     },
7600
7601     // default connection failure
7602     failure : function(response){
7603         
7604         this.response = response;
7605         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7606         this.form.afterAction(this, false);
7607     },
7608
7609     processResponse : function(response){
7610         this.response = response;
7611         if(!response.responseText){
7612             return true;
7613         }
7614         this.result = this.handleResponse(response);
7615         return this.result;
7616     },
7617
7618     // utility functions used internally
7619     getUrl : function(appendParams){
7620         var url = this.options.url || this.form.url || this.form.el.dom.action;
7621         if(appendParams){
7622             var p = this.getParams();
7623             if(p){
7624                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7625             }
7626         }
7627         return url;
7628     },
7629
7630     getMethod : function(){
7631         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7632     },
7633
7634     getParams : function(){
7635         var bp = this.form.baseParams;
7636         var p = this.options.params;
7637         if(p){
7638             if(typeof p == "object"){
7639                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7640             }else if(typeof p == 'string' && bp){
7641                 p += '&' + Roo.urlEncode(bp);
7642             }
7643         }else if(bp){
7644             p = Roo.urlEncode(bp);
7645         }
7646         return p;
7647     },
7648
7649     createCallback : function(){
7650         return {
7651             success: this.success,
7652             failure: this.failure,
7653             scope: this,
7654             timeout: (this.form.timeout*1000),
7655             upload: this.form.fileUpload ? this.success : undefined
7656         };
7657     }
7658 };
7659
7660 Roo.form.Action.Submit = function(form, options){
7661     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7662 };
7663
7664 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7665     type : 'submit',
7666
7667     haveProgress : false,
7668     uploadComplete : false,
7669     
7670     // uploadProgress indicator.
7671     uploadProgress : function()
7672     {
7673         if (!this.form.progressUrl) {
7674             return;
7675         }
7676         
7677         if (!this.haveProgress) {
7678             Roo.MessageBox.progress("Uploading", "Uploading");
7679         }
7680         if (this.uploadComplete) {
7681            Roo.MessageBox.hide();
7682            return;
7683         }
7684         
7685         this.haveProgress = true;
7686    
7687         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7688         
7689         var c = new Roo.data.Connection();
7690         c.request({
7691             url : this.form.progressUrl,
7692             params: {
7693                 id : uid
7694             },
7695             method: 'GET',
7696             success : function(req){
7697                //console.log(data);
7698                 var rdata = false;
7699                 var edata;
7700                 try  {
7701                    rdata = Roo.decode(req.responseText)
7702                 } catch (e) {
7703                     Roo.log("Invalid data from server..");
7704                     Roo.log(edata);
7705                     return;
7706                 }
7707                 if (!rdata || !rdata.success) {
7708                     Roo.log(rdata);
7709                     Roo.MessageBox.alert(Roo.encode(rdata));
7710                     return;
7711                 }
7712                 var data = rdata.data;
7713                 
7714                 if (this.uploadComplete) {
7715                    Roo.MessageBox.hide();
7716                    return;
7717                 }
7718                    
7719                 if (data){
7720                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7721                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7722                     );
7723                 }
7724                 this.uploadProgress.defer(2000,this);
7725             },
7726        
7727             failure: function(data) {
7728                 Roo.log('progress url failed ');
7729                 Roo.log(data);
7730             },
7731             scope : this
7732         });
7733            
7734     },
7735     
7736     
7737     run : function()
7738     {
7739         // run get Values on the form, so it syncs any secondary forms.
7740         this.form.getValues();
7741         
7742         var o = this.options;
7743         var method = this.getMethod();
7744         var isPost = method == 'POST';
7745         if(o.clientValidation === false || this.form.isValid()){
7746             
7747             if (this.form.progressUrl) {
7748                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7749                     (new Date() * 1) + '' + Math.random());
7750                     
7751             } 
7752             
7753             
7754             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7755                 form:this.form.el.dom,
7756                 url:this.getUrl(!isPost),
7757                 method: method,
7758                 params:isPost ? this.getParams() : null,
7759                 isUpload: this.form.fileUpload
7760             }));
7761             
7762             this.uploadProgress();
7763
7764         }else if (o.clientValidation !== false){ // client validation failed
7765             this.failureType = Roo.form.Action.CLIENT_INVALID;
7766             this.form.afterAction(this, false);
7767         }
7768     },
7769
7770     success : function(response)
7771     {
7772         this.uploadComplete= true;
7773         if (this.haveProgress) {
7774             Roo.MessageBox.hide();
7775         }
7776         
7777         
7778         var result = this.processResponse(response);
7779         if(result === true || result.success){
7780             this.form.afterAction(this, true);
7781             return;
7782         }
7783         if(result.errors){
7784             this.form.markInvalid(result.errors);
7785             this.failureType = Roo.form.Action.SERVER_INVALID;
7786         }
7787         this.form.afterAction(this, false);
7788     },
7789     failure : function(response)
7790     {
7791         this.uploadComplete= true;
7792         if (this.haveProgress) {
7793             Roo.MessageBox.hide();
7794         }
7795         
7796         this.response = response;
7797         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7798         this.form.afterAction(this, false);
7799     },
7800     
7801     handleResponse : function(response){
7802         if(this.form.errorReader){
7803             var rs = this.form.errorReader.read(response);
7804             var errors = [];
7805             if(rs.records){
7806                 for(var i = 0, len = rs.records.length; i < len; i++) {
7807                     var r = rs.records[i];
7808                     errors[i] = r.data;
7809                 }
7810             }
7811             if(errors.length < 1){
7812                 errors = null;
7813             }
7814             return {
7815                 success : rs.success,
7816                 errors : errors
7817             };
7818         }
7819         var ret = false;
7820         try {
7821             ret = Roo.decode(response.responseText);
7822         } catch (e) {
7823             ret = {
7824                 success: false,
7825                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7826                 errors : []
7827             };
7828         }
7829         return ret;
7830         
7831     }
7832 });
7833
7834
7835 Roo.form.Action.Load = function(form, options){
7836     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7837     this.reader = this.form.reader;
7838 };
7839
7840 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7841     type : 'load',
7842
7843     run : function(){
7844         
7845         Roo.Ajax.request(Roo.apply(
7846                 this.createCallback(), {
7847                     method:this.getMethod(),
7848                     url:this.getUrl(false),
7849                     params:this.getParams()
7850         }));
7851     },
7852
7853     success : function(response){
7854         
7855         var result = this.processResponse(response);
7856         if(result === true || !result.success || !result.data){
7857             this.failureType = Roo.form.Action.LOAD_FAILURE;
7858             this.form.afterAction(this, false);
7859             return;
7860         }
7861         this.form.clearInvalid();
7862         this.form.setValues(result.data);
7863         this.form.afterAction(this, true);
7864     },
7865
7866     handleResponse : function(response){
7867         if(this.form.reader){
7868             var rs = this.form.reader.read(response);
7869             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7870             return {
7871                 success : rs.success,
7872                 data : data
7873             };
7874         }
7875         return Roo.decode(response.responseText);
7876     }
7877 });
7878
7879 Roo.form.Action.ACTION_TYPES = {
7880     'load' : Roo.form.Action.Load,
7881     'submit' : Roo.form.Action.Submit
7882 };/*
7883  * - LGPL
7884  *
7885  * form
7886  *
7887  */
7888
7889 /**
7890  * @class Roo.bootstrap.Form
7891  * @extends Roo.bootstrap.Component
7892  * Bootstrap Form class
7893  * @cfg {String} method  GET | POST (default POST)
7894  * @cfg {String} labelAlign top | left (default top)
7895  * @cfg {String} align left  | right - for navbars
7896  * @cfg {Boolean} loadMask load mask when submit (default true)
7897
7898  *
7899  * @constructor
7900  * Create a new Form
7901  * @param {Object} config The config object
7902  */
7903
7904
7905 Roo.bootstrap.Form = function(config){
7906     
7907     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7908     
7909     Roo.bootstrap.Form.popover.apply();
7910     
7911     this.addEvents({
7912         /**
7913          * @event clientvalidation
7914          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7915          * @param {Form} this
7916          * @param {Boolean} valid true if the form has passed client-side validation
7917          */
7918         clientvalidation: true,
7919         /**
7920          * @event beforeaction
7921          * Fires before any action is performed. Return false to cancel the action.
7922          * @param {Form} this
7923          * @param {Action} action The action to be performed
7924          */
7925         beforeaction: true,
7926         /**
7927          * @event actionfailed
7928          * Fires when an action fails.
7929          * @param {Form} this
7930          * @param {Action} action The action that failed
7931          */
7932         actionfailed : true,
7933         /**
7934          * @event actioncomplete
7935          * Fires when an action is completed.
7936          * @param {Form} this
7937          * @param {Action} action The action that completed
7938          */
7939         actioncomplete : true
7940     });
7941 };
7942
7943 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7944
7945      /**
7946      * @cfg {String} method
7947      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7948      */
7949     method : 'POST',
7950     /**
7951      * @cfg {String} url
7952      * The URL to use for form actions if one isn't supplied in the action options.
7953      */
7954     /**
7955      * @cfg {Boolean} fileUpload
7956      * Set to true if this form is a file upload.
7957      */
7958
7959     /**
7960      * @cfg {Object} baseParams
7961      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7962      */
7963
7964     /**
7965      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7966      */
7967     timeout: 30,
7968     /**
7969      * @cfg {Sting} align (left|right) for navbar forms
7970      */
7971     align : 'left',
7972
7973     // private
7974     activeAction : null,
7975
7976     /**
7977      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7978      * element by passing it or its id or mask the form itself by passing in true.
7979      * @type Mixed
7980      */
7981     waitMsgTarget : false,
7982
7983     loadMask : true,
7984     
7985     /**
7986      * @cfg {Boolean} errorMask (true|false) default false
7987      */
7988     errorMask : false,
7989     
7990     /**
7991      * @cfg {Number} maskOffset Default 100
7992      */
7993     maskOffset : 100,
7994     
7995     /**
7996      * @cfg {Boolean} maskBody
7997      */
7998     maskBody : false,
7999
8000     getAutoCreate : function(){
8001
8002         var cfg = {
8003             tag: 'form',
8004             method : this.method || 'POST',
8005             id : this.id || Roo.id(),
8006             cls : ''
8007         };
8008         if (this.parent().xtype.match(/^Nav/)) {
8009             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8010
8011         }
8012
8013         if (this.labelAlign == 'left' ) {
8014             cfg.cls += ' form-horizontal';
8015         }
8016
8017
8018         return cfg;
8019     },
8020     initEvents : function()
8021     {
8022         this.el.on('submit', this.onSubmit, this);
8023         // this was added as random key presses on the form where triggering form submit.
8024         this.el.on('keypress', function(e) {
8025             if (e.getCharCode() != 13) {
8026                 return true;
8027             }
8028             // we might need to allow it for textareas.. and some other items.
8029             // check e.getTarget().
8030
8031             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8032                 return true;
8033             }
8034
8035             Roo.log("keypress blocked");
8036
8037             e.preventDefault();
8038             return false;
8039         });
8040         
8041     },
8042     // private
8043     onSubmit : function(e){
8044         e.stopEvent();
8045     },
8046
8047      /**
8048      * Returns true if client-side validation on the form is successful.
8049      * @return Boolean
8050      */
8051     isValid : function(){
8052         var items = this.getItems();
8053         var valid = true;
8054         var target = false;
8055         
8056         items.each(function(f){
8057             
8058             if(f.validate()){
8059                 return;
8060             }
8061             
8062             Roo.log('invalid field: ' + f.name);
8063             
8064             valid = false;
8065
8066             if(!target && f.el.isVisible(true)){
8067                 target = f;
8068             }
8069            
8070         });
8071         
8072         if(this.errorMask && !valid){
8073             Roo.bootstrap.Form.popover.mask(this, target);
8074         }
8075         
8076         return valid;
8077     },
8078     
8079     /**
8080      * Returns true if any fields in this form have changed since their original load.
8081      * @return Boolean
8082      */
8083     isDirty : function(){
8084         var dirty = false;
8085         var items = this.getItems();
8086         items.each(function(f){
8087            if(f.isDirty()){
8088                dirty = true;
8089                return false;
8090            }
8091            return true;
8092         });
8093         return dirty;
8094     },
8095      /**
8096      * Performs a predefined action (submit or load) or custom actions you define on this form.
8097      * @param {String} actionName The name of the action type
8098      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8099      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8100      * accept other config options):
8101      * <pre>
8102 Property          Type             Description
8103 ----------------  ---------------  ----------------------------------------------------------------------------------
8104 url               String           The url for the action (defaults to the form's url)
8105 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8106 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8107 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8108                                    validate the form on the client (defaults to false)
8109      * </pre>
8110      * @return {BasicForm} this
8111      */
8112     doAction : function(action, options){
8113         if(typeof action == 'string'){
8114             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8115         }
8116         if(this.fireEvent('beforeaction', this, action) !== false){
8117             this.beforeAction(action);
8118             action.run.defer(100, action);
8119         }
8120         return this;
8121     },
8122
8123     // private
8124     beforeAction : function(action){
8125         var o = action.options;
8126         
8127         if(this.loadMask){
8128             
8129             if(this.maskBody){
8130                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8131             } else {
8132                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8133             }
8134         }
8135         // not really supported yet.. ??
8136
8137         //if(this.waitMsgTarget === true){
8138         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8139         //}else if(this.waitMsgTarget){
8140         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8141         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8142         //}else {
8143         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8144        // }
8145
8146     },
8147
8148     // private
8149     afterAction : function(action, success){
8150         this.activeAction = null;
8151         var o = action.options;
8152
8153         if(this.loadMask){
8154             
8155             if(this.maskBody){
8156                 Roo.get(document.body).unmask();
8157             } else {
8158                 this.el.unmask();
8159             }
8160         }
8161         
8162         //if(this.waitMsgTarget === true){
8163 //            this.el.unmask();
8164         //}else if(this.waitMsgTarget){
8165         //    this.waitMsgTarget.unmask();
8166         //}else{
8167         //    Roo.MessageBox.updateProgress(1);
8168         //    Roo.MessageBox.hide();
8169        // }
8170         //
8171         if(success){
8172             if(o.reset){
8173                 this.reset();
8174             }
8175             Roo.callback(o.success, o.scope, [this, action]);
8176             this.fireEvent('actioncomplete', this, action);
8177
8178         }else{
8179
8180             // failure condition..
8181             // we have a scenario where updates need confirming.
8182             // eg. if a locking scenario exists..
8183             // we look for { errors : { needs_confirm : true }} in the response.
8184             if (
8185                 (typeof(action.result) != 'undefined')  &&
8186                 (typeof(action.result.errors) != 'undefined')  &&
8187                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8188            ){
8189                 var _t = this;
8190                 Roo.log("not supported yet");
8191                  /*
8192
8193                 Roo.MessageBox.confirm(
8194                     "Change requires confirmation",
8195                     action.result.errorMsg,
8196                     function(r) {
8197                         if (r != 'yes') {
8198                             return;
8199                         }
8200                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8201                     }
8202
8203                 );
8204                 */
8205
8206
8207                 return;
8208             }
8209
8210             Roo.callback(o.failure, o.scope, [this, action]);
8211             // show an error message if no failed handler is set..
8212             if (!this.hasListener('actionfailed')) {
8213                 Roo.log("need to add dialog support");
8214                 /*
8215                 Roo.MessageBox.alert("Error",
8216                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8217                         action.result.errorMsg :
8218                         "Saving Failed, please check your entries or try again"
8219                 );
8220                 */
8221             }
8222
8223             this.fireEvent('actionfailed', this, action);
8224         }
8225
8226     },
8227     /**
8228      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8229      * @param {String} id The value to search for
8230      * @return Field
8231      */
8232     findField : function(id){
8233         var items = this.getItems();
8234         var field = items.get(id);
8235         if(!field){
8236              items.each(function(f){
8237                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8238                     field = f;
8239                     return false;
8240                 }
8241                 return true;
8242             });
8243         }
8244         return field || null;
8245     },
8246      /**
8247      * Mark fields in this form invalid in bulk.
8248      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8249      * @return {BasicForm} this
8250      */
8251     markInvalid : function(errors){
8252         if(errors instanceof Array){
8253             for(var i = 0, len = errors.length; i < len; i++){
8254                 var fieldError = errors[i];
8255                 var f = this.findField(fieldError.id);
8256                 if(f){
8257                     f.markInvalid(fieldError.msg);
8258                 }
8259             }
8260         }else{
8261             var field, id;
8262             for(id in errors){
8263                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8264                     field.markInvalid(errors[id]);
8265                 }
8266             }
8267         }
8268         //Roo.each(this.childForms || [], function (f) {
8269         //    f.markInvalid(errors);
8270         //});
8271
8272         return this;
8273     },
8274
8275     /**
8276      * Set values for fields in this form in bulk.
8277      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8278      * @return {BasicForm} this
8279      */
8280     setValues : function(values){
8281         if(values instanceof Array){ // array of objects
8282             for(var i = 0, len = values.length; i < len; i++){
8283                 var v = values[i];
8284                 var f = this.findField(v.id);
8285                 if(f){
8286                     f.setValue(v.value);
8287                     if(this.trackResetOnLoad){
8288                         f.originalValue = f.getValue();
8289                     }
8290                 }
8291             }
8292         }else{ // object hash
8293             var field, id;
8294             for(id in values){
8295                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8296
8297                     if (field.setFromData &&
8298                         field.valueField &&
8299                         field.displayField &&
8300                         // combos' with local stores can
8301                         // be queried via setValue()
8302                         // to set their value..
8303                         (field.store && !field.store.isLocal)
8304                         ) {
8305                         // it's a combo
8306                         var sd = { };
8307                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8308                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8309                         field.setFromData(sd);
8310
8311                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8312                         
8313                         field.setFromData(values);
8314                         
8315                     } else {
8316                         field.setValue(values[id]);
8317                     }
8318
8319
8320                     if(this.trackResetOnLoad){
8321                         field.originalValue = field.getValue();
8322                     }
8323                 }
8324             }
8325         }
8326
8327         //Roo.each(this.childForms || [], function (f) {
8328         //    f.setValues(values);
8329         //});
8330
8331         return this;
8332     },
8333
8334     /**
8335      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8336      * they are returned as an array.
8337      * @param {Boolean} asString
8338      * @return {Object}
8339      */
8340     getValues : function(asString){
8341         //if (this.childForms) {
8342             // copy values from the child forms
8343         //    Roo.each(this.childForms, function (f) {
8344         //        this.setValues(f.getValues());
8345         //    }, this);
8346         //}
8347
8348
8349
8350         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8351         if(asString === true){
8352             return fs;
8353         }
8354         return Roo.urlDecode(fs);
8355     },
8356
8357     /**
8358      * Returns the fields in this form as an object with key/value pairs.
8359      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8360      * @return {Object}
8361      */
8362     getFieldValues : function(with_hidden)
8363     {
8364         var items = this.getItems();
8365         var ret = {};
8366         items.each(function(f){
8367             
8368             if (!f.getName()) {
8369                 return;
8370             }
8371             
8372             var v = f.getValue();
8373             
8374             if (f.inputType =='radio') {
8375                 if (typeof(ret[f.getName()]) == 'undefined') {
8376                     ret[f.getName()] = ''; // empty..
8377                 }
8378
8379                 if (!f.el.dom.checked) {
8380                     return;
8381
8382                 }
8383                 v = f.el.dom.value;
8384
8385             }
8386             
8387             if(f.xtype == 'MoneyField'){
8388                 ret[f.currencyName] = f.getCurrency();
8389             }
8390
8391             // not sure if this supported any more..
8392             if ((typeof(v) == 'object') && f.getRawValue) {
8393                 v = f.getRawValue() ; // dates..
8394             }
8395             // combo boxes where name != hiddenName...
8396             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8397                 ret[f.name] = f.getRawValue();
8398             }
8399             ret[f.getName()] = v;
8400         });
8401
8402         return ret;
8403     },
8404
8405     /**
8406      * Clears all invalid messages in this form.
8407      * @return {BasicForm} this
8408      */
8409     clearInvalid : function(){
8410         var items = this.getItems();
8411
8412         items.each(function(f){
8413            f.clearInvalid();
8414         });
8415
8416         return this;
8417     },
8418
8419     /**
8420      * Resets this form.
8421      * @return {BasicForm} this
8422      */
8423     reset : function(){
8424         var items = this.getItems();
8425         items.each(function(f){
8426             f.reset();
8427         });
8428
8429         Roo.each(this.childForms || [], function (f) {
8430             f.reset();
8431         });
8432
8433
8434         return this;
8435     },
8436     
8437     getItems : function()
8438     {
8439         var r=new Roo.util.MixedCollection(false, function(o){
8440             return o.id || (o.id = Roo.id());
8441         });
8442         var iter = function(el) {
8443             if (el.inputEl) {
8444                 r.add(el);
8445             }
8446             if (!el.items) {
8447                 return;
8448             }
8449             Roo.each(el.items,function(e) {
8450                 iter(e);
8451             });
8452         };
8453
8454         iter(this);
8455         return r;
8456     },
8457     
8458     hideFields : function(items)
8459     {
8460         Roo.each(items, function(i){
8461             
8462             var f = this.findField(i);
8463             
8464             if(!f){
8465                 return;
8466             }
8467             
8468             f.hide();
8469             
8470         }, this);
8471     },
8472     
8473     showFields : function(items)
8474     {
8475         Roo.each(items, function(i){
8476             
8477             var f = this.findField(i);
8478             
8479             if(!f){
8480                 return;
8481             }
8482             
8483             f.show();
8484             
8485         }, this);
8486     }
8487
8488 });
8489
8490 Roo.apply(Roo.bootstrap.Form, {
8491     
8492     popover : {
8493         
8494         padding : 5,
8495         
8496         isApplied : false,
8497         
8498         isMasked : false,
8499         
8500         form : false,
8501         
8502         target : false,
8503         
8504         toolTip : false,
8505         
8506         intervalID : false,
8507         
8508         maskEl : false,
8509         
8510         apply : function()
8511         {
8512             if(this.isApplied){
8513                 return;
8514             }
8515             
8516             this.maskEl = {
8517                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8518                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8519                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8520                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8521             };
8522             
8523             this.maskEl.top.enableDisplayMode("block");
8524             this.maskEl.left.enableDisplayMode("block");
8525             this.maskEl.bottom.enableDisplayMode("block");
8526             this.maskEl.right.enableDisplayMode("block");
8527             
8528             this.toolTip = new Roo.bootstrap.Tooltip({
8529                 cls : 'roo-form-error-popover',
8530                 alignment : {
8531                     'left' : ['r-l', [-2,0], 'right'],
8532                     'right' : ['l-r', [2,0], 'left'],
8533                     'bottom' : ['tl-bl', [0,2], 'top'],
8534                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8535                 }
8536             });
8537             
8538             this.toolTip.render(Roo.get(document.body));
8539
8540             this.toolTip.el.enableDisplayMode("block");
8541             
8542             Roo.get(document.body).on('click', function(){
8543                 this.unmask();
8544             }, this);
8545             
8546             Roo.get(document.body).on('touchstart', function(){
8547                 this.unmask();
8548             }, this);
8549             
8550             this.isApplied = true
8551         },
8552         
8553         mask : function(form, target)
8554         {
8555             this.form = form;
8556             
8557             this.target = target;
8558             
8559             if(!this.form.errorMask || !target.el){
8560                 return;
8561             }
8562             
8563             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8564             
8565             Roo.log(scrollable);
8566             
8567             var ot = this.target.el.calcOffsetsTo(scrollable);
8568             
8569             var scrollTo = ot[1] - this.form.maskOffset;
8570             
8571             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8572             
8573             scrollable.scrollTo('top', scrollTo);
8574             
8575             var box = this.target.el.getBox();
8576             Roo.log(box);
8577             var zIndex = Roo.bootstrap.Modal.zIndex++;
8578
8579             
8580             this.maskEl.top.setStyle('position', 'absolute');
8581             this.maskEl.top.setStyle('z-index', zIndex);
8582             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8583             this.maskEl.top.setLeft(0);
8584             this.maskEl.top.setTop(0);
8585             this.maskEl.top.show();
8586             
8587             this.maskEl.left.setStyle('position', 'absolute');
8588             this.maskEl.left.setStyle('z-index', zIndex);
8589             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8590             this.maskEl.left.setLeft(0);
8591             this.maskEl.left.setTop(box.y - this.padding);
8592             this.maskEl.left.show();
8593
8594             this.maskEl.bottom.setStyle('position', 'absolute');
8595             this.maskEl.bottom.setStyle('z-index', zIndex);
8596             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8597             this.maskEl.bottom.setLeft(0);
8598             this.maskEl.bottom.setTop(box.bottom + this.padding);
8599             this.maskEl.bottom.show();
8600
8601             this.maskEl.right.setStyle('position', 'absolute');
8602             this.maskEl.right.setStyle('z-index', zIndex);
8603             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8604             this.maskEl.right.setLeft(box.right + this.padding);
8605             this.maskEl.right.setTop(box.y - this.padding);
8606             this.maskEl.right.show();
8607
8608             this.toolTip.bindEl = this.target.el;
8609
8610             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8611
8612             var tip = this.target.blankText;
8613
8614             if(this.target.getValue() !== '' ) {
8615                 
8616                 if (this.target.invalidText.length) {
8617                     tip = this.target.invalidText;
8618                 } else if (this.target.regexText.length){
8619                     tip = this.target.regexText;
8620                 }
8621             }
8622
8623             this.toolTip.show(tip);
8624
8625             this.intervalID = window.setInterval(function() {
8626                 Roo.bootstrap.Form.popover.unmask();
8627             }, 10000);
8628
8629             window.onwheel = function(){ return false;};
8630             
8631             (function(){ this.isMasked = true; }).defer(500, this);
8632             
8633         },
8634         
8635         unmask : function()
8636         {
8637             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8638                 return;
8639             }
8640             
8641             this.maskEl.top.setStyle('position', 'absolute');
8642             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8643             this.maskEl.top.hide();
8644
8645             this.maskEl.left.setStyle('position', 'absolute');
8646             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8647             this.maskEl.left.hide();
8648
8649             this.maskEl.bottom.setStyle('position', 'absolute');
8650             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8651             this.maskEl.bottom.hide();
8652
8653             this.maskEl.right.setStyle('position', 'absolute');
8654             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8655             this.maskEl.right.hide();
8656             
8657             this.toolTip.hide();
8658             
8659             this.toolTip.el.hide();
8660             
8661             window.onwheel = function(){ return true;};
8662             
8663             if(this.intervalID){
8664                 window.clearInterval(this.intervalID);
8665                 this.intervalID = false;
8666             }
8667             
8668             this.isMasked = false;
8669             
8670         }
8671         
8672     }
8673     
8674 });
8675
8676 /*
8677  * Based on:
8678  * Ext JS Library 1.1.1
8679  * Copyright(c) 2006-2007, Ext JS, LLC.
8680  *
8681  * Originally Released Under LGPL - original licence link has changed is not relivant.
8682  *
8683  * Fork - LGPL
8684  * <script type="text/javascript">
8685  */
8686 /**
8687  * @class Roo.form.VTypes
8688  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8689  * @singleton
8690  */
8691 Roo.form.VTypes = function(){
8692     // closure these in so they are only created once.
8693     var alpha = /^[a-zA-Z_]+$/;
8694     var alphanum = /^[a-zA-Z0-9_]+$/;
8695     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8696     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8697
8698     // All these messages and functions are configurable
8699     return {
8700         /**
8701          * The function used to validate email addresses
8702          * @param {String} value The email address
8703          */
8704         'email' : function(v){
8705             return email.test(v);
8706         },
8707         /**
8708          * The error text to display when the email validation function returns false
8709          * @type String
8710          */
8711         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8712         /**
8713          * The keystroke filter mask to be applied on email input
8714          * @type RegExp
8715          */
8716         'emailMask' : /[a-z0-9_\.\-@]/i,
8717
8718         /**
8719          * The function used to validate URLs
8720          * @param {String} value The URL
8721          */
8722         'url' : function(v){
8723             return url.test(v);
8724         },
8725         /**
8726          * The error text to display when the url validation function returns false
8727          * @type String
8728          */
8729         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8730         
8731         /**
8732          * The function used to validate alpha values
8733          * @param {String} value The value
8734          */
8735         'alpha' : function(v){
8736             return alpha.test(v);
8737         },
8738         /**
8739          * The error text to display when the alpha validation function returns false
8740          * @type String
8741          */
8742         'alphaText' : 'This field should only contain letters and _',
8743         /**
8744          * The keystroke filter mask to be applied on alpha input
8745          * @type RegExp
8746          */
8747         'alphaMask' : /[a-z_]/i,
8748
8749         /**
8750          * The function used to validate alphanumeric values
8751          * @param {String} value The value
8752          */
8753         'alphanum' : function(v){
8754             return alphanum.test(v);
8755         },
8756         /**
8757          * The error text to display when the alphanumeric validation function returns false
8758          * @type String
8759          */
8760         'alphanumText' : 'This field should only contain letters, numbers and _',
8761         /**
8762          * The keystroke filter mask to be applied on alphanumeric input
8763          * @type RegExp
8764          */
8765         'alphanumMask' : /[a-z0-9_]/i
8766     };
8767 }();/*
8768  * - LGPL
8769  *
8770  * Input
8771  * 
8772  */
8773
8774 /**
8775  * @class Roo.bootstrap.Input
8776  * @extends Roo.bootstrap.Component
8777  * Bootstrap Input class
8778  * @cfg {Boolean} disabled is it disabled
8779  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8780  * @cfg {String} name name of the input
8781  * @cfg {string} fieldLabel - the label associated
8782  * @cfg {string} placeholder - placeholder to put in text.
8783  * @cfg {string}  before - input group add on before
8784  * @cfg {string} after - input group add on after
8785  * @cfg {string} size - (lg|sm) or leave empty..
8786  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8787  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8788  * @cfg {Number} md colspan out of 12 for computer-sized screens
8789  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8790  * @cfg {string} value default value of the input
8791  * @cfg {Number} labelWidth set the width of label 
8792  * @cfg {Number} labellg set the width of label (1-12)
8793  * @cfg {Number} labelmd set the width of label (1-12)
8794  * @cfg {Number} labelsm set the width of label (1-12)
8795  * @cfg {Number} labelxs set the width of label (1-12)
8796  * @cfg {String} labelAlign (top|left)
8797  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8798  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8799  * @cfg {String} indicatorpos (left|right) default left
8800  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8801  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8802
8803  * @cfg {String} align (left|center|right) Default left
8804  * @cfg {Boolean} forceFeedback (true|false) Default false
8805  * 
8806  * @constructor
8807  * Create a new Input
8808  * @param {Object} config The config object
8809  */
8810
8811 Roo.bootstrap.Input = function(config){
8812     
8813     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8814     
8815     this.addEvents({
8816         /**
8817          * @event focus
8818          * Fires when this field receives input focus.
8819          * @param {Roo.form.Field} this
8820          */
8821         focus : true,
8822         /**
8823          * @event blur
8824          * Fires when this field loses input focus.
8825          * @param {Roo.form.Field} this
8826          */
8827         blur : true,
8828         /**
8829          * @event specialkey
8830          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8831          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8832          * @param {Roo.form.Field} this
8833          * @param {Roo.EventObject} e The event object
8834          */
8835         specialkey : true,
8836         /**
8837          * @event change
8838          * Fires just before the field blurs if the field value has changed.
8839          * @param {Roo.form.Field} this
8840          * @param {Mixed} newValue The new value
8841          * @param {Mixed} oldValue The original value
8842          */
8843         change : true,
8844         /**
8845          * @event invalid
8846          * Fires after the field has been marked as invalid.
8847          * @param {Roo.form.Field} this
8848          * @param {String} msg The validation message
8849          */
8850         invalid : true,
8851         /**
8852          * @event valid
8853          * Fires after the field has been validated with no errors.
8854          * @param {Roo.form.Field} this
8855          */
8856         valid : true,
8857          /**
8858          * @event keyup
8859          * Fires after the key up
8860          * @param {Roo.form.Field} this
8861          * @param {Roo.EventObject}  e The event Object
8862          */
8863         keyup : true
8864     });
8865 };
8866
8867 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8868      /**
8869      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8870       automatic validation (defaults to "keyup").
8871      */
8872     validationEvent : "keyup",
8873      /**
8874      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8875      */
8876     validateOnBlur : true,
8877     /**
8878      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8879      */
8880     validationDelay : 250,
8881      /**
8882      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8883      */
8884     focusClass : "x-form-focus",  // not needed???
8885     
8886        
8887     /**
8888      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8889      */
8890     invalidClass : "has-warning",
8891     
8892     /**
8893      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8894      */
8895     validClass : "has-success",
8896     
8897     /**
8898      * @cfg {Boolean} hasFeedback (true|false) default true
8899      */
8900     hasFeedback : true,
8901     
8902     /**
8903      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8904      */
8905     invalidFeedbackClass : "glyphicon-warning-sign",
8906     
8907     /**
8908      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8909      */
8910     validFeedbackClass : "glyphicon-ok",
8911     
8912     /**
8913      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8914      */
8915     selectOnFocus : false,
8916     
8917      /**
8918      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8919      */
8920     maskRe : null,
8921        /**
8922      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8923      */
8924     vtype : null,
8925     
8926       /**
8927      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8928      */
8929     disableKeyFilter : false,
8930     
8931        /**
8932      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8933      */
8934     disabled : false,
8935      /**
8936      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8937      */
8938     allowBlank : true,
8939     /**
8940      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8941      */
8942     blankText : "Please complete this mandatory field",
8943     
8944      /**
8945      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8946      */
8947     minLength : 0,
8948     /**
8949      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8950      */
8951     maxLength : Number.MAX_VALUE,
8952     /**
8953      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8954      */
8955     minLengthText : "The minimum length for this field is {0}",
8956     /**
8957      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8958      */
8959     maxLengthText : "The maximum length for this field is {0}",
8960   
8961     
8962     /**
8963      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8964      * If available, this function will be called only after the basic validators all return true, and will be passed the
8965      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8966      */
8967     validator : null,
8968     /**
8969      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8970      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8971      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8972      */
8973     regex : null,
8974     /**
8975      * @cfg {String} regexText -- Depricated - use Invalid Text
8976      */
8977     regexText : "",
8978     
8979     /**
8980      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8981      */
8982     invalidText : "",
8983     
8984     
8985     
8986     autocomplete: false,
8987     
8988     
8989     fieldLabel : '',
8990     inputType : 'text',
8991     
8992     name : false,
8993     placeholder: false,
8994     before : false,
8995     after : false,
8996     size : false,
8997     hasFocus : false,
8998     preventMark: false,
8999     isFormField : true,
9000     value : '',
9001     labelWidth : 2,
9002     labelAlign : false,
9003     readOnly : false,
9004     align : false,
9005     formatedValue : false,
9006     forceFeedback : false,
9007     
9008     indicatorpos : 'left',
9009     
9010     labellg : 0,
9011     labelmd : 0,
9012     labelsm : 0,
9013     labelxs : 0,
9014     
9015     capture : '',
9016     accept : '',
9017     
9018     parentLabelAlign : function()
9019     {
9020         var parent = this;
9021         while (parent.parent()) {
9022             parent = parent.parent();
9023             if (typeof(parent.labelAlign) !='undefined') {
9024                 return parent.labelAlign;
9025             }
9026         }
9027         return 'left';
9028         
9029     },
9030     
9031     getAutoCreate : function()
9032     {
9033         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9034         
9035         var id = Roo.id();
9036         
9037         var cfg = {};
9038         
9039         if(this.inputType != 'hidden'){
9040             cfg.cls = 'form-group' //input-group
9041         }
9042         
9043         var input =  {
9044             tag: 'input',
9045             id : id,
9046             type : this.inputType,
9047             value : this.value,
9048             cls : 'form-control',
9049             placeholder : this.placeholder || '',
9050             autocomplete : this.autocomplete || 'new-password'
9051         };
9052         
9053         if(this.capture.length){
9054             input.capture = this.capture;
9055         }
9056         
9057         if(this.accept.length){
9058             input.accept = this.accept + "/*";
9059         }
9060         
9061         if(this.align){
9062             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9063         }
9064         
9065         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9066             input.maxLength = this.maxLength;
9067         }
9068         
9069         if (this.disabled) {
9070             input.disabled=true;
9071         }
9072         
9073         if (this.readOnly) {
9074             input.readonly=true;
9075         }
9076         
9077         if (this.name) {
9078             input.name = this.name;
9079         }
9080         
9081         if (this.size) {
9082             input.cls += ' input-' + this.size;
9083         }
9084         
9085         var settings=this;
9086         ['xs','sm','md','lg'].map(function(size){
9087             if (settings[size]) {
9088                 cfg.cls += ' col-' + size + '-' + settings[size];
9089             }
9090         });
9091         
9092         var inputblock = input;
9093         
9094         var feedback = {
9095             tag: 'span',
9096             cls: 'glyphicon form-control-feedback'
9097         };
9098             
9099         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9100             
9101             inputblock = {
9102                 cls : 'has-feedback',
9103                 cn :  [
9104                     input,
9105                     feedback
9106                 ] 
9107             };  
9108         }
9109         
9110         if (this.before || this.after) {
9111             
9112             inputblock = {
9113                 cls : 'input-group',
9114                 cn :  [] 
9115             };
9116             
9117             if (this.before && typeof(this.before) == 'string') {
9118                 
9119                 inputblock.cn.push({
9120                     tag :'span',
9121                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9122                     html : this.before
9123                 });
9124             }
9125             if (this.before && typeof(this.before) == 'object') {
9126                 this.before = Roo.factory(this.before);
9127                 
9128                 inputblock.cn.push({
9129                     tag :'span',
9130                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9131                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9132                 });
9133             }
9134             
9135             inputblock.cn.push(input);
9136             
9137             if (this.after && typeof(this.after) == 'string') {
9138                 inputblock.cn.push({
9139                     tag :'span',
9140                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9141                     html : this.after
9142                 });
9143             }
9144             if (this.after && typeof(this.after) == 'object') {
9145                 this.after = Roo.factory(this.after);
9146                 
9147                 inputblock.cn.push({
9148                     tag :'span',
9149                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9150                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9151                 });
9152             }
9153             
9154             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9155                 inputblock.cls += ' has-feedback';
9156                 inputblock.cn.push(feedback);
9157             }
9158         };
9159         var indicator = {
9160             tag : 'i',
9161             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9162             tooltip : 'This field is required'
9163         };
9164         if (Roo.bootstrap.version == 4) {
9165             indicator = {
9166                 tag : 'i',
9167                 style : 'display-none'
9168             };
9169         }
9170         if (align ==='left' && this.fieldLabel.length) {
9171             
9172             cfg.cls += ' roo-form-group-label-left row';
9173             
9174             cfg.cn = [
9175                 indicator,
9176                 {
9177                     tag: 'label',
9178                     'for' :  id,
9179                     cls : 'control-label col-form-label',
9180                     html : this.fieldLabel
9181
9182                 },
9183                 {
9184                     cls : "", 
9185                     cn: [
9186                         inputblock
9187                     ]
9188                 }
9189             ];
9190             
9191             var labelCfg = cfg.cn[1];
9192             var contentCfg = cfg.cn[2];
9193             
9194             if(this.indicatorpos == 'right'){
9195                 cfg.cn = [
9196                     {
9197                         tag: 'label',
9198                         'for' :  id,
9199                         cls : 'control-label col-form-label',
9200                         cn : [
9201                             {
9202                                 tag : 'span',
9203                                 html : this.fieldLabel
9204                             },
9205                             indicator
9206                         ]
9207                     },
9208                     {
9209                         cls : "",
9210                         cn: [
9211                             inputblock
9212                         ]
9213                     }
9214
9215                 ];
9216                 
9217                 labelCfg = cfg.cn[0];
9218                 contentCfg = cfg.cn[1];
9219             
9220             }
9221             
9222             if(this.labelWidth > 12){
9223                 labelCfg.style = "width: " + this.labelWidth + 'px';
9224             }
9225             
9226             if(this.labelWidth < 13 && this.labelmd == 0){
9227                 this.labelmd = this.labelWidth;
9228             }
9229             
9230             if(this.labellg > 0){
9231                 labelCfg.cls += ' col-lg-' + this.labellg;
9232                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9233             }
9234             
9235             if(this.labelmd > 0){
9236                 labelCfg.cls += ' col-md-' + this.labelmd;
9237                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9238             }
9239             
9240             if(this.labelsm > 0){
9241                 labelCfg.cls += ' col-sm-' + this.labelsm;
9242                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9243             }
9244             
9245             if(this.labelxs > 0){
9246                 labelCfg.cls += ' col-xs-' + this.labelxs;
9247                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9248             }
9249             
9250             
9251         } else if ( this.fieldLabel.length) {
9252                 
9253             cfg.cn = [
9254                 {
9255                     tag : 'i',
9256                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9257                     tooltip : 'This field is required'
9258                 },
9259                 {
9260                     tag: 'label',
9261                    //cls : 'input-group-addon',
9262                     html : this.fieldLabel
9263
9264                 },
9265
9266                inputblock
9267
9268            ];
9269            
9270            if(this.indicatorpos == 'right'){
9271                 
9272                 cfg.cn = [
9273                     {
9274                         tag: 'label',
9275                        //cls : 'input-group-addon',
9276                         html : this.fieldLabel
9277
9278                     },
9279                     {
9280                         tag : 'i',
9281                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9282                         tooltip : 'This field is required'
9283                     },
9284
9285                    inputblock
9286
9287                ];
9288
9289             }
9290
9291         } else {
9292             
9293             cfg.cn = [
9294
9295                     inputblock
9296
9297             ];
9298                 
9299                 
9300         };
9301         
9302         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9303            cfg.cls += ' navbar-form';
9304         }
9305         
9306         if (this.parentType === 'NavGroup') {
9307            cfg.cls += ' navbar-form';
9308            cfg.tag = 'li';
9309         }
9310         
9311         return cfg;
9312         
9313     },
9314     /**
9315      * return the real input element.
9316      */
9317     inputEl: function ()
9318     {
9319         return this.el.select('input.form-control',true).first();
9320     },
9321     
9322     tooltipEl : function()
9323     {
9324         return this.inputEl();
9325     },
9326     
9327     indicatorEl : function()
9328     {
9329         if (Roo.bootstrap.version == 4) {
9330             return false; // not enabled in v4 yet.
9331         }
9332         
9333         var indicator = this.el.select('i.roo-required-indicator',true).first();
9334         
9335         if(!indicator){
9336             return false;
9337         }
9338         
9339         return indicator;
9340         
9341     },
9342     
9343     setDisabled : function(v)
9344     {
9345         var i  = this.inputEl().dom;
9346         if (!v) {
9347             i.removeAttribute('disabled');
9348             return;
9349             
9350         }
9351         i.setAttribute('disabled','true');
9352     },
9353     initEvents : function()
9354     {
9355           
9356         this.inputEl().on("keydown" , this.fireKey,  this);
9357         this.inputEl().on("focus", this.onFocus,  this);
9358         this.inputEl().on("blur", this.onBlur,  this);
9359         
9360         this.inputEl().relayEvent('keyup', this);
9361         
9362         this.indicator = this.indicatorEl();
9363         
9364         if(this.indicator){
9365             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9366         }
9367  
9368         // reference to original value for reset
9369         this.originalValue = this.getValue();
9370         //Roo.form.TextField.superclass.initEvents.call(this);
9371         if(this.validationEvent == 'keyup'){
9372             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9373             this.inputEl().on('keyup', this.filterValidation, this);
9374         }
9375         else if(this.validationEvent !== false){
9376             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9377         }
9378         
9379         if(this.selectOnFocus){
9380             this.on("focus", this.preFocus, this);
9381             
9382         }
9383         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9384             this.inputEl().on("keypress", this.filterKeys, this);
9385         } else {
9386             this.inputEl().relayEvent('keypress', this);
9387         }
9388        /* if(this.grow){
9389             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9390             this.el.on("click", this.autoSize,  this);
9391         }
9392         */
9393         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9394             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9395         }
9396         
9397         if (typeof(this.before) == 'object') {
9398             this.before.render(this.el.select('.roo-input-before',true).first());
9399         }
9400         if (typeof(this.after) == 'object') {
9401             this.after.render(this.el.select('.roo-input-after',true).first());
9402         }
9403         
9404         this.inputEl().on('change', this.onChange, this);
9405         
9406     },
9407     filterValidation : function(e){
9408         if(!e.isNavKeyPress()){
9409             this.validationTask.delay(this.validationDelay);
9410         }
9411     },
9412      /**
9413      * Validates the field value
9414      * @return {Boolean} True if the value is valid, else false
9415      */
9416     validate : function(){
9417         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9418         if(this.disabled || this.validateValue(this.getRawValue())){
9419             this.markValid();
9420             return true;
9421         }
9422         
9423         this.markInvalid();
9424         return false;
9425     },
9426     
9427     
9428     /**
9429      * Validates a value according to the field's validation rules and marks the field as invalid
9430      * if the validation fails
9431      * @param {Mixed} value The value to validate
9432      * @return {Boolean} True if the value is valid, else false
9433      */
9434     validateValue : function(value)
9435     {
9436         if(this.getVisibilityEl().hasClass('hidden')){
9437             return true;
9438         }
9439         
9440         if(value.length < 1)  { // if it's blank
9441             if(this.allowBlank){
9442                 return true;
9443             }
9444             return false;
9445         }
9446         
9447         if(value.length < this.minLength){
9448             return false;
9449         }
9450         if(value.length > this.maxLength){
9451             return false;
9452         }
9453         if(this.vtype){
9454             var vt = Roo.form.VTypes;
9455             if(!vt[this.vtype](value, this)){
9456                 return false;
9457             }
9458         }
9459         if(typeof this.validator == "function"){
9460             var msg = this.validator(value);
9461             if(msg !== true){
9462                 return false;
9463             }
9464             if (typeof(msg) == 'string') {
9465                 this.invalidText = msg;
9466             }
9467         }
9468         
9469         if(this.regex && !this.regex.test(value)){
9470             return false;
9471         }
9472         
9473         return true;
9474     },
9475     
9476      // private
9477     fireKey : function(e){
9478         //Roo.log('field ' + e.getKey());
9479         if(e.isNavKeyPress()){
9480             this.fireEvent("specialkey", this, e);
9481         }
9482     },
9483     focus : function (selectText){
9484         if(this.rendered){
9485             this.inputEl().focus();
9486             if(selectText === true){
9487                 this.inputEl().dom.select();
9488             }
9489         }
9490         return this;
9491     } ,
9492     
9493     onFocus : function(){
9494         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9495            // this.el.addClass(this.focusClass);
9496         }
9497         if(!this.hasFocus){
9498             this.hasFocus = true;
9499             this.startValue = this.getValue();
9500             this.fireEvent("focus", this);
9501         }
9502     },
9503     
9504     beforeBlur : Roo.emptyFn,
9505
9506     
9507     // private
9508     onBlur : function(){
9509         this.beforeBlur();
9510         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9511             //this.el.removeClass(this.focusClass);
9512         }
9513         this.hasFocus = false;
9514         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9515             this.validate();
9516         }
9517         var v = this.getValue();
9518         if(String(v) !== String(this.startValue)){
9519             this.fireEvent('change', this, v, this.startValue);
9520         }
9521         this.fireEvent("blur", this);
9522     },
9523     
9524     onChange : function(e)
9525     {
9526         var v = this.getValue();
9527         if(String(v) !== String(this.startValue)){
9528             this.fireEvent('change', this, v, this.startValue);
9529         }
9530         
9531     },
9532     
9533     /**
9534      * Resets the current field value to the originally loaded value and clears any validation messages
9535      */
9536     reset : function(){
9537         this.setValue(this.originalValue);
9538         this.validate();
9539     },
9540      /**
9541      * Returns the name of the field
9542      * @return {Mixed} name The name field
9543      */
9544     getName: function(){
9545         return this.name;
9546     },
9547      /**
9548      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9549      * @return {Mixed} value The field value
9550      */
9551     getValue : function(){
9552         
9553         var v = this.inputEl().getValue();
9554         
9555         return v;
9556     },
9557     /**
9558      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9559      * @return {Mixed} value The field value
9560      */
9561     getRawValue : function(){
9562         var v = this.inputEl().getValue();
9563         
9564         return v;
9565     },
9566     
9567     /**
9568      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9569      * @param {Mixed} value The value to set
9570      */
9571     setRawValue : function(v){
9572         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9573     },
9574     
9575     selectText : function(start, end){
9576         var v = this.getRawValue();
9577         if(v.length > 0){
9578             start = start === undefined ? 0 : start;
9579             end = end === undefined ? v.length : end;
9580             var d = this.inputEl().dom;
9581             if(d.setSelectionRange){
9582                 d.setSelectionRange(start, end);
9583             }else if(d.createTextRange){
9584                 var range = d.createTextRange();
9585                 range.moveStart("character", start);
9586                 range.moveEnd("character", v.length-end);
9587                 range.select();
9588             }
9589         }
9590     },
9591     
9592     /**
9593      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9594      * @param {Mixed} value The value to set
9595      */
9596     setValue : function(v){
9597         this.value = v;
9598         if(this.rendered){
9599             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9600             this.validate();
9601         }
9602     },
9603     
9604     /*
9605     processValue : function(value){
9606         if(this.stripCharsRe){
9607             var newValue = value.replace(this.stripCharsRe, '');
9608             if(newValue !== value){
9609                 this.setRawValue(newValue);
9610                 return newValue;
9611             }
9612         }
9613         return value;
9614     },
9615   */
9616     preFocus : function(){
9617         
9618         if(this.selectOnFocus){
9619             this.inputEl().dom.select();
9620         }
9621     },
9622     filterKeys : function(e){
9623         var k = e.getKey();
9624         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9625             return;
9626         }
9627         var c = e.getCharCode(), cc = String.fromCharCode(c);
9628         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9629             return;
9630         }
9631         if(!this.maskRe.test(cc)){
9632             e.stopEvent();
9633         }
9634     },
9635      /**
9636      * Clear any invalid styles/messages for this field
9637      */
9638     clearInvalid : function(){
9639         
9640         if(!this.el || this.preventMark){ // not rendered
9641             return;
9642         }
9643         
9644      
9645         this.el.removeClass(this.invalidClass);
9646         
9647         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9648             
9649             var feedback = this.el.select('.form-control-feedback', true).first();
9650             
9651             if(feedback){
9652                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9653             }
9654             
9655         }
9656         
9657         if(this.indicator){
9658             this.indicator.removeClass('visible');
9659             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9660         }
9661         
9662         this.fireEvent('valid', this);
9663     },
9664     
9665      /**
9666      * Mark this field as valid
9667      */
9668     markValid : function()
9669     {
9670         if(!this.el  || this.preventMark){ // not rendered...
9671             return;
9672         }
9673         
9674         this.el.removeClass([this.invalidClass, this.validClass]);
9675         
9676         var feedback = this.el.select('.form-control-feedback', true).first();
9677             
9678         if(feedback){
9679             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9680         }
9681         
9682         if(this.indicator){
9683             this.indicator.removeClass('visible');
9684             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9685         }
9686         
9687         if(this.disabled){
9688             return;
9689         }
9690         
9691         if(this.allowBlank && !this.getRawValue().length){
9692             return;
9693         }
9694         
9695         this.el.addClass(this.validClass);
9696         
9697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9698             
9699             var feedback = this.el.select('.form-control-feedback', true).first();
9700             
9701             if(feedback){
9702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9703                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9704             }
9705             
9706         }
9707         
9708         this.fireEvent('valid', this);
9709     },
9710     
9711      /**
9712      * Mark this field as invalid
9713      * @param {String} msg The validation message
9714      */
9715     markInvalid : function(msg)
9716     {
9717         if(!this.el  || this.preventMark){ // not rendered
9718             return;
9719         }
9720         
9721         this.el.removeClass([this.invalidClass, this.validClass]);
9722         
9723         var feedback = this.el.select('.form-control-feedback', true).first();
9724             
9725         if(feedback){
9726             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9727         }
9728
9729         if(this.disabled){
9730             return;
9731         }
9732         
9733         if(this.allowBlank && !this.getRawValue().length){
9734             return;
9735         }
9736         
9737         if(this.indicator){
9738             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9739             this.indicator.addClass('visible');
9740         }
9741         
9742         this.el.addClass(this.invalidClass);
9743         
9744         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9745             
9746             var feedback = this.el.select('.form-control-feedback', true).first();
9747             
9748             if(feedback){
9749                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9750                 
9751                 if(this.getValue().length || this.forceFeedback){
9752                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9753                 }
9754                 
9755             }
9756             
9757         }
9758         
9759         this.fireEvent('invalid', this, msg);
9760     },
9761     // private
9762     SafariOnKeyDown : function(event)
9763     {
9764         // this is a workaround for a password hang bug on chrome/ webkit.
9765         if (this.inputEl().dom.type != 'password') {
9766             return;
9767         }
9768         
9769         var isSelectAll = false;
9770         
9771         if(this.inputEl().dom.selectionEnd > 0){
9772             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9773         }
9774         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9775             event.preventDefault();
9776             this.setValue('');
9777             return;
9778         }
9779         
9780         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9781             
9782             event.preventDefault();
9783             // this is very hacky as keydown always get's upper case.
9784             //
9785             var cc = String.fromCharCode(event.getCharCode());
9786             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9787             
9788         }
9789     },
9790     adjustWidth : function(tag, w){
9791         tag = tag.toLowerCase();
9792         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9793             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9794                 if(tag == 'input'){
9795                     return w + 2;
9796                 }
9797                 if(tag == 'textarea'){
9798                     return w-2;
9799                 }
9800             }else if(Roo.isOpera){
9801                 if(tag == 'input'){
9802                     return w + 2;
9803                 }
9804                 if(tag == 'textarea'){
9805                     return w-2;
9806                 }
9807             }
9808         }
9809         return w;
9810     },
9811     
9812     setFieldLabel : function(v)
9813     {
9814         if(!this.rendered){
9815             return;
9816         }
9817         
9818         if(this.indicatorEl()){
9819             var ar = this.el.select('label > span',true);
9820             
9821             if (ar.elements.length) {
9822                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9823                 this.fieldLabel = v;
9824                 return;
9825             }
9826             
9827             var br = this.el.select('label',true);
9828             
9829             if(br.elements.length) {
9830                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9831                 this.fieldLabel = v;
9832                 return;
9833             }
9834             
9835             Roo.log('Cannot Found any of label > span || label in input');
9836             return;
9837         }
9838         
9839         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9840         this.fieldLabel = v;
9841         
9842         
9843     }
9844 });
9845
9846  
9847 /*
9848  * - LGPL
9849  *
9850  * Input
9851  * 
9852  */
9853
9854 /**
9855  * @class Roo.bootstrap.TextArea
9856  * @extends Roo.bootstrap.Input
9857  * Bootstrap TextArea class
9858  * @cfg {Number} cols Specifies the visible width of a text area
9859  * @cfg {Number} rows Specifies the visible number of lines in a text area
9860  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9861  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9862  * @cfg {string} html text
9863  * 
9864  * @constructor
9865  * Create a new TextArea
9866  * @param {Object} config The config object
9867  */
9868
9869 Roo.bootstrap.TextArea = function(config){
9870     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9871    
9872 };
9873
9874 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9875      
9876     cols : false,
9877     rows : 5,
9878     readOnly : false,
9879     warp : 'soft',
9880     resize : false,
9881     value: false,
9882     html: false,
9883     
9884     getAutoCreate : function(){
9885         
9886         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9887         
9888         var id = Roo.id();
9889         
9890         var cfg = {};
9891         
9892         if(this.inputType != 'hidden'){
9893             cfg.cls = 'form-group' //input-group
9894         }
9895         
9896         var input =  {
9897             tag: 'textarea',
9898             id : id,
9899             warp : this.warp,
9900             rows : this.rows,
9901             value : this.value || '',
9902             html: this.html || '',
9903             cls : 'form-control',
9904             placeholder : this.placeholder || '' 
9905             
9906         };
9907         
9908         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9909             input.maxLength = this.maxLength;
9910         }
9911         
9912         if(this.resize){
9913             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9914         }
9915         
9916         if(this.cols){
9917             input.cols = this.cols;
9918         }
9919         
9920         if (this.readOnly) {
9921             input.readonly = true;
9922         }
9923         
9924         if (this.name) {
9925             input.name = this.name;
9926         }
9927         
9928         if (this.size) {
9929             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9930         }
9931         
9932         var settings=this;
9933         ['xs','sm','md','lg'].map(function(size){
9934             if (settings[size]) {
9935                 cfg.cls += ' col-' + size + '-' + settings[size];
9936             }
9937         });
9938         
9939         var inputblock = input;
9940         
9941         if(this.hasFeedback && !this.allowBlank){
9942             
9943             var feedback = {
9944                 tag: 'span',
9945                 cls: 'glyphicon form-control-feedback'
9946             };
9947
9948             inputblock = {
9949                 cls : 'has-feedback',
9950                 cn :  [
9951                     input,
9952                     feedback
9953                 ] 
9954             };  
9955         }
9956         
9957         
9958         if (this.before || this.after) {
9959             
9960             inputblock = {
9961                 cls : 'input-group',
9962                 cn :  [] 
9963             };
9964             if (this.before) {
9965                 inputblock.cn.push({
9966                     tag :'span',
9967                     cls : 'input-group-addon',
9968                     html : this.before
9969                 });
9970             }
9971             
9972             inputblock.cn.push(input);
9973             
9974             if(this.hasFeedback && !this.allowBlank){
9975                 inputblock.cls += ' has-feedback';
9976                 inputblock.cn.push(feedback);
9977             }
9978             
9979             if (this.after) {
9980                 inputblock.cn.push({
9981                     tag :'span',
9982                     cls : 'input-group-addon',
9983                     html : this.after
9984                 });
9985             }
9986             
9987         }
9988         
9989         if (align ==='left' && this.fieldLabel.length) {
9990             cfg.cn = [
9991                 {
9992                     tag: 'label',
9993                     'for' :  id,
9994                     cls : 'control-label',
9995                     html : this.fieldLabel
9996                 },
9997                 {
9998                     cls : "",
9999                     cn: [
10000                         inputblock
10001                     ]
10002                 }
10003
10004             ];
10005             
10006             if(this.labelWidth > 12){
10007                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10008             }
10009
10010             if(this.labelWidth < 13 && this.labelmd == 0){
10011                 this.labelmd = this.labelWidth;
10012             }
10013
10014             if(this.labellg > 0){
10015                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10016                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10017             }
10018
10019             if(this.labelmd > 0){
10020                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10021                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10022             }
10023
10024             if(this.labelsm > 0){
10025                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10026                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10027             }
10028
10029             if(this.labelxs > 0){
10030                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10031                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10032             }
10033             
10034         } else if ( this.fieldLabel.length) {
10035             cfg.cn = [
10036
10037                {
10038                    tag: 'label',
10039                    //cls : 'input-group-addon',
10040                    html : this.fieldLabel
10041
10042                },
10043
10044                inputblock
10045
10046            ];
10047
10048         } else {
10049
10050             cfg.cn = [
10051
10052                 inputblock
10053
10054             ];
10055                 
10056         }
10057         
10058         if (this.disabled) {
10059             input.disabled=true;
10060         }
10061         
10062         return cfg;
10063         
10064     },
10065     /**
10066      * return the real textarea element.
10067      */
10068     inputEl: function ()
10069     {
10070         return this.el.select('textarea.form-control',true).first();
10071     },
10072     
10073     /**
10074      * Clear any invalid styles/messages for this field
10075      */
10076     clearInvalid : function()
10077     {
10078         
10079         if(!this.el || this.preventMark){ // not rendered
10080             return;
10081         }
10082         
10083         var label = this.el.select('label', true).first();
10084         var icon = this.el.select('i.fa-star', true).first();
10085         
10086         if(label && icon){
10087             icon.remove();
10088         }
10089         
10090         this.el.removeClass(this.invalidClass);
10091         
10092         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10093             
10094             var feedback = this.el.select('.form-control-feedback', true).first();
10095             
10096             if(feedback){
10097                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10098             }
10099             
10100         }
10101         
10102         this.fireEvent('valid', this);
10103     },
10104     
10105      /**
10106      * Mark this field as valid
10107      */
10108     markValid : function()
10109     {
10110         if(!this.el  || this.preventMark){ // not rendered
10111             return;
10112         }
10113         
10114         this.el.removeClass([this.invalidClass, this.validClass]);
10115         
10116         var feedback = this.el.select('.form-control-feedback', true).first();
10117             
10118         if(feedback){
10119             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10120         }
10121
10122         if(this.disabled || this.allowBlank){
10123             return;
10124         }
10125         
10126         var label = this.el.select('label', true).first();
10127         var icon = this.el.select('i.fa-star', true).first();
10128         
10129         if(label && icon){
10130             icon.remove();
10131         }
10132         
10133         this.el.addClass(this.validClass);
10134         
10135         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10136             
10137             var feedback = this.el.select('.form-control-feedback', true).first();
10138             
10139             if(feedback){
10140                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10141                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10142             }
10143             
10144         }
10145         
10146         this.fireEvent('valid', this);
10147     },
10148     
10149      /**
10150      * Mark this field as invalid
10151      * @param {String} msg The validation message
10152      */
10153     markInvalid : function(msg)
10154     {
10155         if(!this.el  || this.preventMark){ // not rendered
10156             return;
10157         }
10158         
10159         this.el.removeClass([this.invalidClass, this.validClass]);
10160         
10161         var feedback = this.el.select('.form-control-feedback', true).first();
10162             
10163         if(feedback){
10164             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10165         }
10166
10167         if(this.disabled || this.allowBlank){
10168             return;
10169         }
10170         
10171         var label = this.el.select('label', true).first();
10172         var icon = this.el.select('i.fa-star', true).first();
10173         
10174         if(!this.getValue().length && label && !icon){
10175             this.el.createChild({
10176                 tag : 'i',
10177                 cls : 'text-danger fa fa-lg fa-star',
10178                 tooltip : 'This field is required',
10179                 style : 'margin-right:5px;'
10180             }, label, true);
10181         }
10182
10183         this.el.addClass(this.invalidClass);
10184         
10185         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10186             
10187             var feedback = this.el.select('.form-control-feedback', true).first();
10188             
10189             if(feedback){
10190                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10191                 
10192                 if(this.getValue().length || this.forceFeedback){
10193                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10194                 }
10195                 
10196             }
10197             
10198         }
10199         
10200         this.fireEvent('invalid', this, msg);
10201     }
10202 });
10203
10204  
10205 /*
10206  * - LGPL
10207  *
10208  * trigger field - base class for combo..
10209  * 
10210  */
10211  
10212 /**
10213  * @class Roo.bootstrap.TriggerField
10214  * @extends Roo.bootstrap.Input
10215  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10216  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10217  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10218  * for which you can provide a custom implementation.  For example:
10219  * <pre><code>
10220 var trigger = new Roo.bootstrap.TriggerField();
10221 trigger.onTriggerClick = myTriggerFn;
10222 trigger.applyTo('my-field');
10223 </code></pre>
10224  *
10225  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10226  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10227  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10228  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10229  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10230
10231  * @constructor
10232  * Create a new TriggerField.
10233  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10234  * to the base TextField)
10235  */
10236 Roo.bootstrap.TriggerField = function(config){
10237     this.mimicing = false;
10238     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10239 };
10240
10241 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10242     /**
10243      * @cfg {String} triggerClass A CSS class to apply to the trigger
10244      */
10245      /**
10246      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10247      */
10248     hideTrigger:false,
10249
10250     /**
10251      * @cfg {Boolean} removable (true|false) special filter default false
10252      */
10253     removable : false,
10254     
10255     /** @cfg {Boolean} grow @hide */
10256     /** @cfg {Number} growMin @hide */
10257     /** @cfg {Number} growMax @hide */
10258
10259     /**
10260      * @hide 
10261      * @method
10262      */
10263     autoSize: Roo.emptyFn,
10264     // private
10265     monitorTab : true,
10266     // private
10267     deferHeight : true,
10268
10269     
10270     actionMode : 'wrap',
10271     
10272     caret : false,
10273     
10274     
10275     getAutoCreate : function(){
10276        
10277         var align = this.labelAlign || this.parentLabelAlign();
10278         
10279         var id = Roo.id();
10280         
10281         var cfg = {
10282             cls: 'form-group' //input-group
10283         };
10284         
10285         
10286         var input =  {
10287             tag: 'input',
10288             id : id,
10289             type : this.inputType,
10290             cls : 'form-control',
10291             autocomplete: 'new-password',
10292             placeholder : this.placeholder || '' 
10293             
10294         };
10295         if (this.name) {
10296             input.name = this.name;
10297         }
10298         if (this.size) {
10299             input.cls += ' input-' + this.size;
10300         }
10301         
10302         if (this.disabled) {
10303             input.disabled=true;
10304         }
10305         
10306         var inputblock = input;
10307         
10308         if(this.hasFeedback && !this.allowBlank){
10309             
10310             var feedback = {
10311                 tag: 'span',
10312                 cls: 'glyphicon form-control-feedback'
10313             };
10314             
10315             if(this.removable && !this.editable && !this.tickable){
10316                 inputblock = {
10317                     cls : 'has-feedback',
10318                     cn :  [
10319                         inputblock,
10320                         {
10321                             tag: 'button',
10322                             html : 'x',
10323                             cls : 'roo-combo-removable-btn close'
10324                         },
10325                         feedback
10326                     ] 
10327                 };
10328             } else {
10329                 inputblock = {
10330                     cls : 'has-feedback',
10331                     cn :  [
10332                         inputblock,
10333                         feedback
10334                     ] 
10335                 };
10336             }
10337
10338         } else {
10339             if(this.removable && !this.editable && !this.tickable){
10340                 inputblock = {
10341                     cls : 'roo-removable',
10342                     cn :  [
10343                         inputblock,
10344                         {
10345                             tag: 'button',
10346                             html : 'x',
10347                             cls : 'roo-combo-removable-btn close'
10348                         }
10349                     ] 
10350                 };
10351             }
10352         }
10353         
10354         if (this.before || this.after) {
10355             
10356             inputblock = {
10357                 cls : 'input-group',
10358                 cn :  [] 
10359             };
10360             if (this.before) {
10361                 inputblock.cn.push({
10362                     tag :'span',
10363                     cls : 'input-group-addon input-group-prepend input-group-text',
10364                     html : this.before
10365                 });
10366             }
10367             
10368             inputblock.cn.push(input);
10369             
10370             if(this.hasFeedback && !this.allowBlank){
10371                 inputblock.cls += ' has-feedback';
10372                 inputblock.cn.push(feedback);
10373             }
10374             
10375             if (this.after) {
10376                 inputblock.cn.push({
10377                     tag :'span',
10378                     cls : 'input-group-addon input-group-append input-group-text',
10379                     html : this.after
10380                 });
10381             }
10382             
10383         };
10384         
10385         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                 
13356                 box
13357 //                {
13358 //                    tag: 'ul',
13359 //                    cls: 'typeahead typeahead-long dropdown-menu',
13360 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13361 //                }
13362             ]
13363         };
13364         
13365         if(this.hasFeedback && !this.allowBlank){
13366             
13367             var feedback = {
13368                 tag: 'span',
13369                 cls: 'glyphicon form-control-feedback'
13370             };
13371
13372             combobox.cn.push(feedback);
13373         }
13374         
13375         var indicator = {
13376             tag : 'i',
13377             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13378             tooltip : 'This field is required'
13379         };
13380         if (Roo.bootstrap.version == 4) {
13381             indicator = {
13382                 tag : 'i',
13383                 style : 'display:none'
13384             };
13385         }
13386         if (align ==='left' && this.fieldLabel.length) {
13387             
13388             cfg.cls += ' roo-form-group-label-left row';
13389             
13390             cfg.cn = [
13391                 indicator,
13392                 {
13393                     tag: 'label',
13394                     'for' :  id,
13395                     cls : 'control-label col-form-label',
13396                     html : this.fieldLabel
13397
13398                 },
13399                 {
13400                     cls : "", 
13401                     cn: [
13402                         combobox
13403                     ]
13404                 }
13405
13406             ];
13407             
13408             var labelCfg = cfg.cn[1];
13409             var contentCfg = cfg.cn[2];
13410             
13411
13412             if(this.indicatorpos == 'right'){
13413                 
13414                 cfg.cn = [
13415                     {
13416                         tag: 'label',
13417                         'for' :  id,
13418                         cls : 'control-label col-form-label',
13419                         cn : [
13420                             {
13421                                 tag : 'span',
13422                                 html : this.fieldLabel
13423                             },
13424                             indicator
13425                         ]
13426                     },
13427                     {
13428                         cls : "",
13429                         cn: [
13430                             combobox
13431                         ]
13432                     }
13433
13434                 ];
13435                 
13436                 
13437                 
13438                 labelCfg = cfg.cn[0];
13439                 contentCfg = cfg.cn[1];
13440             
13441             }
13442             
13443             if(this.labelWidth > 12){
13444                 labelCfg.style = "width: " + this.labelWidth + 'px';
13445             }
13446             
13447             if(this.labelWidth < 13 && this.labelmd == 0){
13448                 this.labelmd = this.labelWidth;
13449             }
13450             
13451             if(this.labellg > 0){
13452                 labelCfg.cls += ' col-lg-' + this.labellg;
13453                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13454             }
13455             
13456             if(this.labelmd > 0){
13457                 labelCfg.cls += ' col-md-' + this.labelmd;
13458                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13459             }
13460             
13461             if(this.labelsm > 0){
13462                 labelCfg.cls += ' col-sm-' + this.labelsm;
13463                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13464             }
13465             
13466             if(this.labelxs > 0){
13467                 labelCfg.cls += ' col-xs-' + this.labelxs;
13468                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13469             }
13470                 
13471                 
13472         } else if ( this.fieldLabel.length) {
13473 //                Roo.log(" label");
13474                  cfg.cn = [
13475                    indicator,
13476                     {
13477                         tag: 'label',
13478                         //cls : 'input-group-addon',
13479                         html : this.fieldLabel
13480                     },
13481                     combobox
13482                 ];
13483                 
13484                 if(this.indicatorpos == 'right'){
13485                     cfg.cn = [
13486                         {
13487                             tag: 'label',
13488                             //cls : 'input-group-addon',
13489                             html : this.fieldLabel
13490                         },
13491                         indicator,
13492                         combobox
13493                     ];
13494                     
13495                 }
13496
13497         } else {
13498             
13499 //                Roo.log(" no label && no align");
13500                 cfg = combobox
13501                      
13502                 
13503         }
13504          
13505         var settings=this;
13506         ['xs','sm','md','lg'].map(function(size){
13507             if (settings[size]) {
13508                 cfg.cls += ' col-' + size + '-' + settings[size];
13509             }
13510         });
13511         
13512         return cfg;
13513         
13514     },
13515     
13516     _initEventsCalled : false,
13517     
13518     // private
13519     initEvents: function()
13520     {   
13521         if (this._initEventsCalled) { // as we call render... prevent looping...
13522             return;
13523         }
13524         this._initEventsCalled = true;
13525         
13526         if (!this.store) {
13527             throw "can not find store for combo";
13528         }
13529         
13530         this.indicator = this.indicatorEl();
13531         
13532         this.store = Roo.factory(this.store, Roo.data);
13533         this.store.parent = this;
13534         
13535         // if we are building from html. then this element is so complex, that we can not really
13536         // use the rendered HTML.
13537         // so we have to trash and replace the previous code.
13538         if (Roo.XComponent.build_from_html) {
13539             // remove this element....
13540             var e = this.el.dom, k=0;
13541             while (e ) { e = e.previousSibling;  ++k;}
13542
13543             this.el.remove();
13544             
13545             this.el=false;
13546             this.rendered = false;
13547             
13548             this.render(this.parent().getChildContainer(true), k);
13549         }
13550         
13551         if(Roo.isIOS && this.useNativeIOS){
13552             this.initIOSView();
13553             return;
13554         }
13555         
13556         /*
13557          * Touch Devices
13558          */
13559         
13560         if(Roo.isTouch && this.mobileTouchView){
13561             this.initTouchView();
13562             return;
13563         }
13564         
13565         if(this.tickable){
13566             this.initTickableEvents();
13567             return;
13568         }
13569         
13570         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13571         
13572         if(this.hiddenName){
13573             
13574             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13575             
13576             this.hiddenField.dom.value =
13577                 this.hiddenValue !== undefined ? this.hiddenValue :
13578                 this.value !== undefined ? this.value : '';
13579
13580             // prevent input submission
13581             this.el.dom.removeAttribute('name');
13582             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13583              
13584              
13585         }
13586         //if(Roo.isGecko){
13587         //    this.el.dom.setAttribute('autocomplete', 'off');
13588         //}
13589         
13590         var cls = 'x-combo-list';
13591         
13592         //this.list = new Roo.Layer({
13593         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13594         //});
13595         
13596         var _this = this;
13597         
13598         (function(){
13599             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13600             _this.list.setWidth(lw);
13601         }).defer(100);
13602         
13603         this.list.on('mouseover', this.onViewOver, this);
13604         this.list.on('mousemove', this.onViewMove, this);
13605         this.list.on('scroll', this.onViewScroll, this);
13606         
13607         /*
13608         this.list.swallowEvent('mousewheel');
13609         this.assetHeight = 0;
13610
13611         if(this.title){
13612             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13613             this.assetHeight += this.header.getHeight();
13614         }
13615
13616         this.innerList = this.list.createChild({cls:cls+'-inner'});
13617         this.innerList.on('mouseover', this.onViewOver, this);
13618         this.innerList.on('mousemove', this.onViewMove, this);
13619         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13620         
13621         if(this.allowBlank && !this.pageSize && !this.disableClear){
13622             this.footer = this.list.createChild({cls:cls+'-ft'});
13623             this.pageTb = new Roo.Toolbar(this.footer);
13624            
13625         }
13626         if(this.pageSize){
13627             this.footer = this.list.createChild({cls:cls+'-ft'});
13628             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13629                     {pageSize: this.pageSize});
13630             
13631         }
13632         
13633         if (this.pageTb && this.allowBlank && !this.disableClear) {
13634             var _this = this;
13635             this.pageTb.add(new Roo.Toolbar.Fill(), {
13636                 cls: 'x-btn-icon x-btn-clear',
13637                 text: '&#160;',
13638                 handler: function()
13639                 {
13640                     _this.collapse();
13641                     _this.clearValue();
13642                     _this.onSelect(false, -1);
13643                 }
13644             });
13645         }
13646         if (this.footer) {
13647             this.assetHeight += this.footer.getHeight();
13648         }
13649         */
13650             
13651         if(!this.tpl){
13652             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13653         }
13654
13655         this.view = new Roo.View(this.list, this.tpl, {
13656             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13657         });
13658         //this.view.wrapEl.setDisplayed(false);
13659         this.view.on('click', this.onViewClick, this);
13660         
13661         
13662         this.store.on('beforeload', this.onBeforeLoad, this);
13663         this.store.on('load', this.onLoad, this);
13664         this.store.on('loadexception', this.onLoadException, this);
13665         /*
13666         if(this.resizable){
13667             this.resizer = new Roo.Resizable(this.list,  {
13668                pinned:true, handles:'se'
13669             });
13670             this.resizer.on('resize', function(r, w, h){
13671                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13672                 this.listWidth = w;
13673                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13674                 this.restrictHeight();
13675             }, this);
13676             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13677         }
13678         */
13679         if(!this.editable){
13680             this.editable = true;
13681             this.setEditable(false);
13682         }
13683         
13684         /*
13685         
13686         if (typeof(this.events.add.listeners) != 'undefined') {
13687             
13688             this.addicon = this.wrap.createChild(
13689                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13690        
13691             this.addicon.on('click', function(e) {
13692                 this.fireEvent('add', this);
13693             }, this);
13694         }
13695         if (typeof(this.events.edit.listeners) != 'undefined') {
13696             
13697             this.editicon = this.wrap.createChild(
13698                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13699             if (this.addicon) {
13700                 this.editicon.setStyle('margin-left', '40px');
13701             }
13702             this.editicon.on('click', function(e) {
13703                 
13704                 // we fire even  if inothing is selected..
13705                 this.fireEvent('edit', this, this.lastData );
13706                 
13707             }, this);
13708         }
13709         */
13710         
13711         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13712             "up" : function(e){
13713                 this.inKeyMode = true;
13714                 this.selectPrev();
13715             },
13716
13717             "down" : function(e){
13718                 if(!this.isExpanded()){
13719                     this.onTriggerClick();
13720                 }else{
13721                     this.inKeyMode = true;
13722                     this.selectNext();
13723                 }
13724             },
13725
13726             "enter" : function(e){
13727 //                this.onViewClick();
13728                 //return true;
13729                 this.collapse();
13730                 
13731                 if(this.fireEvent("specialkey", this, e)){
13732                     this.onViewClick(false);
13733                 }
13734                 
13735                 return true;
13736             },
13737
13738             "esc" : function(e){
13739                 this.collapse();
13740             },
13741
13742             "tab" : function(e){
13743                 this.collapse();
13744                 
13745                 if(this.fireEvent("specialkey", this, e)){
13746                     this.onViewClick(false);
13747                 }
13748                 
13749                 return true;
13750             },
13751
13752             scope : this,
13753
13754             doRelay : function(foo, bar, hname){
13755                 if(hname == 'down' || this.scope.isExpanded()){
13756                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13757                 }
13758                 return true;
13759             },
13760
13761             forceKeyDown: true
13762         });
13763         
13764         
13765         this.queryDelay = Math.max(this.queryDelay || 10,
13766                 this.mode == 'local' ? 10 : 250);
13767         
13768         
13769         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13770         
13771         if(this.typeAhead){
13772             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13773         }
13774         if(this.editable !== false){
13775             this.inputEl().on("keyup", this.onKeyUp, this);
13776         }
13777         if(this.forceSelection){
13778             this.inputEl().on('blur', this.doForce, this);
13779         }
13780         
13781         if(this.multiple){
13782             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13783             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13784         }
13785     },
13786     
13787     initTickableEvents: function()
13788     {   
13789         this.createList();
13790         
13791         if(this.hiddenName){
13792             
13793             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13794             
13795             this.hiddenField.dom.value =
13796                 this.hiddenValue !== undefined ? this.hiddenValue :
13797                 this.value !== undefined ? this.value : '';
13798
13799             // prevent input submission
13800             this.el.dom.removeAttribute('name');
13801             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13802              
13803              
13804         }
13805         
13806 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13807         
13808         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13809         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13810         if(this.triggerList){
13811             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13812         }
13813          
13814         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13815         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13816         
13817         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13818         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13819         
13820         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13821         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13822         
13823         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13824         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13825         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13826         
13827         this.okBtn.hide();
13828         this.cancelBtn.hide();
13829         
13830         var _this = this;
13831         
13832         (function(){
13833             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13834             _this.list.setWidth(lw);
13835         }).defer(100);
13836         
13837         this.list.on('mouseover', this.onViewOver, this);
13838         this.list.on('mousemove', this.onViewMove, this);
13839         
13840         this.list.on('scroll', this.onViewScroll, this);
13841         
13842         if(!this.tpl){
13843             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13844                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13845         }
13846
13847         this.view = new Roo.View(this.list, this.tpl, {
13848             singleSelect:true,
13849             tickable:true,
13850             parent:this,
13851             store: this.store,
13852             selectedClass: this.selectedClass
13853         });
13854         
13855         //this.view.wrapEl.setDisplayed(false);
13856         this.view.on('click', this.onViewClick, this);
13857         
13858         
13859         
13860         this.store.on('beforeload', this.onBeforeLoad, this);
13861         this.store.on('load', this.onLoad, this);
13862         this.store.on('loadexception', this.onLoadException, this);
13863         
13864         if(this.editable){
13865             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13866                 "up" : function(e){
13867                     this.inKeyMode = true;
13868                     this.selectPrev();
13869                 },
13870
13871                 "down" : function(e){
13872                     this.inKeyMode = true;
13873                     this.selectNext();
13874                 },
13875
13876                 "enter" : function(e){
13877                     if(this.fireEvent("specialkey", this, e)){
13878                         this.onViewClick(false);
13879                     }
13880                     
13881                     return true;
13882                 },
13883
13884                 "esc" : function(e){
13885                     this.onTickableFooterButtonClick(e, false, false);
13886                 },
13887
13888                 "tab" : function(e){
13889                     this.fireEvent("specialkey", this, e);
13890                     
13891                     this.onTickableFooterButtonClick(e, false, false);
13892                     
13893                     return true;
13894                 },
13895
13896                 scope : this,
13897
13898                 doRelay : function(e, fn, key){
13899                     if(this.scope.isExpanded()){
13900                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13901                     }
13902                     return true;
13903                 },
13904
13905                 forceKeyDown: true
13906             });
13907         }
13908         
13909         this.queryDelay = Math.max(this.queryDelay || 10,
13910                 this.mode == 'local' ? 10 : 250);
13911         
13912         
13913         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13914         
13915         if(this.typeAhead){
13916             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13917         }
13918         
13919         if(this.editable !== false){
13920             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13921         }
13922         
13923         this.indicator = this.indicatorEl();
13924         
13925         if(this.indicator){
13926             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13927             this.indicator.hide();
13928         }
13929         
13930     },
13931
13932     onDestroy : function(){
13933         if(this.view){
13934             this.view.setStore(null);
13935             this.view.el.removeAllListeners();
13936             this.view.el.remove();
13937             this.view.purgeListeners();
13938         }
13939         if(this.list){
13940             this.list.dom.innerHTML  = '';
13941         }
13942         
13943         if(this.store){
13944             this.store.un('beforeload', this.onBeforeLoad, this);
13945             this.store.un('load', this.onLoad, this);
13946             this.store.un('loadexception', this.onLoadException, this);
13947         }
13948         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13949     },
13950
13951     // private
13952     fireKey : function(e){
13953         if(e.isNavKeyPress() && !this.list.isVisible()){
13954             this.fireEvent("specialkey", this, e);
13955         }
13956     },
13957
13958     // private
13959     onResize: function(w, h){
13960 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13961 //        
13962 //        if(typeof w != 'number'){
13963 //            // we do not handle it!?!?
13964 //            return;
13965 //        }
13966 //        var tw = this.trigger.getWidth();
13967 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13968 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13969 //        var x = w - tw;
13970 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13971 //            
13972 //        //this.trigger.setStyle('left', x+'px');
13973 //        
13974 //        if(this.list && this.listWidth === undefined){
13975 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13976 //            this.list.setWidth(lw);
13977 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13978 //        }
13979         
13980     
13981         
13982     },
13983
13984     /**
13985      * Allow or prevent the user from directly editing the field text.  If false is passed,
13986      * the user will only be able to select from the items defined in the dropdown list.  This method
13987      * is the runtime equivalent of setting the 'editable' config option at config time.
13988      * @param {Boolean} value True to allow the user to directly edit the field text
13989      */
13990     setEditable : function(value){
13991         if(value == this.editable){
13992             return;
13993         }
13994         this.editable = value;
13995         if(!value){
13996             this.inputEl().dom.setAttribute('readOnly', true);
13997             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13998             this.inputEl().addClass('x-combo-noedit');
13999         }else{
14000             this.inputEl().dom.setAttribute('readOnly', false);
14001             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14002             this.inputEl().removeClass('x-combo-noedit');
14003         }
14004     },
14005
14006     // private
14007     
14008     onBeforeLoad : function(combo,opts){
14009         if(!this.hasFocus){
14010             return;
14011         }
14012          if (!opts.add) {
14013             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14014          }
14015         this.restrictHeight();
14016         this.selectedIndex = -1;
14017     },
14018
14019     // private
14020     onLoad : function(){
14021         
14022         this.hasQuery = false;
14023         
14024         if(!this.hasFocus){
14025             return;
14026         }
14027         
14028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14029             this.loading.hide();
14030         }
14031         
14032         if(this.store.getCount() > 0){
14033             
14034             this.expand();
14035             this.restrictHeight();
14036             if(this.lastQuery == this.allQuery){
14037                 if(this.editable && !this.tickable){
14038                     this.inputEl().dom.select();
14039                 }
14040                 
14041                 if(
14042                     !this.selectByValue(this.value, true) &&
14043                     this.autoFocus && 
14044                     (
14045                         !this.store.lastOptions ||
14046                         typeof(this.store.lastOptions.add) == 'undefined' || 
14047                         this.store.lastOptions.add != true
14048                     )
14049                 ){
14050                     this.select(0, true);
14051                 }
14052             }else{
14053                 if(this.autoFocus){
14054                     this.selectNext();
14055                 }
14056                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14057                     this.taTask.delay(this.typeAheadDelay);
14058                 }
14059             }
14060         }else{
14061             this.onEmptyResults();
14062         }
14063         
14064         //this.el.focus();
14065     },
14066     // private
14067     onLoadException : function()
14068     {
14069         this.hasQuery = false;
14070         
14071         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14072             this.loading.hide();
14073         }
14074         
14075         if(this.tickable && this.editable){
14076             return;
14077         }
14078         
14079         this.collapse();
14080         // only causes errors at present
14081         //Roo.log(this.store.reader.jsonData);
14082         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14083             // fixme
14084             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14085         //}
14086         
14087         
14088     },
14089     // private
14090     onTypeAhead : function(){
14091         if(this.store.getCount() > 0){
14092             var r = this.store.getAt(0);
14093             var newValue = r.data[this.displayField];
14094             var len = newValue.length;
14095             var selStart = this.getRawValue().length;
14096             
14097             if(selStart != len){
14098                 this.setRawValue(newValue);
14099                 this.selectText(selStart, newValue.length);
14100             }
14101         }
14102     },
14103
14104     // private
14105     onSelect : function(record, index){
14106         
14107         if(this.fireEvent('beforeselect', this, record, index) !== false){
14108         
14109             this.setFromData(index > -1 ? record.data : false);
14110             
14111             this.collapse();
14112             this.fireEvent('select', this, record, index);
14113         }
14114     },
14115
14116     /**
14117      * Returns the currently selected field value or empty string if no value is set.
14118      * @return {String} value The selected value
14119      */
14120     getValue : function()
14121     {
14122         if(Roo.isIOS && this.useNativeIOS){
14123             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14124         }
14125         
14126         if(this.multiple){
14127             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14128         }
14129         
14130         if(this.valueField){
14131             return typeof this.value != 'undefined' ? this.value : '';
14132         }else{
14133             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14134         }
14135     },
14136     
14137     getRawValue : function()
14138     {
14139         if(Roo.isIOS && this.useNativeIOS){
14140             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14141         }
14142         
14143         var v = this.inputEl().getValue();
14144         
14145         return v;
14146     },
14147
14148     /**
14149      * Clears any text/value currently set in the field
14150      */
14151     clearValue : function(){
14152         
14153         if(this.hiddenField){
14154             this.hiddenField.dom.value = '';
14155         }
14156         this.value = '';
14157         this.setRawValue('');
14158         this.lastSelectionText = '';
14159         this.lastData = false;
14160         
14161         var close = this.closeTriggerEl();
14162         
14163         if(close){
14164             close.hide();
14165         }
14166         
14167         this.validate();
14168         
14169     },
14170
14171     /**
14172      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14173      * will be displayed in the field.  If the value does not match the data value of an existing item,
14174      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14175      * Otherwise the field will be blank (although the value will still be set).
14176      * @param {String} value The value to match
14177      */
14178     setValue : function(v)
14179     {
14180         if(Roo.isIOS && this.useNativeIOS){
14181             this.setIOSValue(v);
14182             return;
14183         }
14184         
14185         if(this.multiple){
14186             this.syncValue();
14187             return;
14188         }
14189         
14190         var text = v;
14191         if(this.valueField){
14192             var r = this.findRecord(this.valueField, v);
14193             if(r){
14194                 text = r.data[this.displayField];
14195             }else if(this.valueNotFoundText !== undefined){
14196                 text = this.valueNotFoundText;
14197             }
14198         }
14199         this.lastSelectionText = text;
14200         if(this.hiddenField){
14201             this.hiddenField.dom.value = v;
14202         }
14203         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14204         this.value = v;
14205         
14206         var close = this.closeTriggerEl();
14207         
14208         if(close){
14209             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14210         }
14211         
14212         this.validate();
14213     },
14214     /**
14215      * @property {Object} the last set data for the element
14216      */
14217     
14218     lastData : false,
14219     /**
14220      * Sets the value of the field based on a object which is related to the record format for the store.
14221      * @param {Object} value the value to set as. or false on reset?
14222      */
14223     setFromData : function(o){
14224         
14225         if(this.multiple){
14226             this.addItem(o);
14227             return;
14228         }
14229             
14230         var dv = ''; // display value
14231         var vv = ''; // value value..
14232         this.lastData = o;
14233         if (this.displayField) {
14234             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14235         } else {
14236             // this is an error condition!!!
14237             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14238         }
14239         
14240         if(this.valueField){
14241             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14242         }
14243         
14244         var close = this.closeTriggerEl();
14245         
14246         if(close){
14247             if(dv.length || vv * 1 > 0){
14248                 close.show() ;
14249                 this.blockFocus=true;
14250             } else {
14251                 close.hide();
14252             }             
14253         }
14254         
14255         if(this.hiddenField){
14256             this.hiddenField.dom.value = vv;
14257             
14258             this.lastSelectionText = dv;
14259             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14260             this.value = vv;
14261             return;
14262         }
14263         // no hidden field.. - we store the value in 'value', but still display
14264         // display field!!!!
14265         this.lastSelectionText = dv;
14266         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14267         this.value = vv;
14268         
14269         
14270         
14271     },
14272     // private
14273     reset : function(){
14274         // overridden so that last data is reset..
14275         
14276         if(this.multiple){
14277             this.clearItem();
14278             return;
14279         }
14280         
14281         this.setValue(this.originalValue);
14282         //this.clearInvalid();
14283         this.lastData = false;
14284         if (this.view) {
14285             this.view.clearSelections();
14286         }
14287         
14288         this.validate();
14289     },
14290     // private
14291     findRecord : function(prop, value){
14292         var record;
14293         if(this.store.getCount() > 0){
14294             this.store.each(function(r){
14295                 if(r.data[prop] == value){
14296                     record = r;
14297                     return false;
14298                 }
14299                 return true;
14300             });
14301         }
14302         return record;
14303     },
14304     
14305     getName: function()
14306     {
14307         // returns hidden if it's set..
14308         if (!this.rendered) {return ''};
14309         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14310         
14311     },
14312     // private
14313     onViewMove : function(e, t){
14314         this.inKeyMode = false;
14315     },
14316
14317     // private
14318     onViewOver : function(e, t){
14319         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14320             return;
14321         }
14322         var item = this.view.findItemFromChild(t);
14323         
14324         if(item){
14325             var index = this.view.indexOf(item);
14326             this.select(index, false);
14327         }
14328     },
14329
14330     // private
14331     onViewClick : function(view, doFocus, el, e)
14332     {
14333         var index = this.view.getSelectedIndexes()[0];
14334         
14335         var r = this.store.getAt(index);
14336         
14337         if(this.tickable){
14338             
14339             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14340                 return;
14341             }
14342             
14343             var rm = false;
14344             var _this = this;
14345             
14346             Roo.each(this.tickItems, function(v,k){
14347                 
14348                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14349                     Roo.log(v);
14350                     _this.tickItems.splice(k, 1);
14351                     
14352                     if(typeof(e) == 'undefined' && view == false){
14353                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14354                     }
14355                     
14356                     rm = true;
14357                     return;
14358                 }
14359             });
14360             
14361             if(rm){
14362                 return;
14363             }
14364             
14365             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14366                 this.tickItems.push(r.data);
14367             }
14368             
14369             if(typeof(e) == 'undefined' && view == false){
14370                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14371             }
14372                     
14373             return;
14374         }
14375         
14376         if(r){
14377             this.onSelect(r, index);
14378         }
14379         if(doFocus !== false && !this.blockFocus){
14380             this.inputEl().focus();
14381         }
14382     },
14383
14384     // private
14385     restrictHeight : function(){
14386         //this.innerList.dom.style.height = '';
14387         //var inner = this.innerList.dom;
14388         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14389         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14390         //this.list.beginUpdate();
14391         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14392         this.list.alignTo(this.inputEl(), this.listAlign);
14393         this.list.alignTo(this.inputEl(), this.listAlign);
14394         //this.list.endUpdate();
14395     },
14396
14397     // private
14398     onEmptyResults : function(){
14399         
14400         if(this.tickable && this.editable){
14401             this.hasFocus = false;
14402             this.restrictHeight();
14403             return;
14404         }
14405         
14406         this.collapse();
14407     },
14408
14409     /**
14410      * Returns true if the dropdown list is expanded, else false.
14411      */
14412     isExpanded : function(){
14413         return this.list.isVisible();
14414     },
14415
14416     /**
14417      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14418      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14419      * @param {String} value The data value of the item to select
14420      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14421      * selected item if it is not currently in view (defaults to true)
14422      * @return {Boolean} True if the value matched an item in the list, else false
14423      */
14424     selectByValue : function(v, scrollIntoView){
14425         if(v !== undefined && v !== null){
14426             var r = this.findRecord(this.valueField || this.displayField, v);
14427             if(r){
14428                 this.select(this.store.indexOf(r), scrollIntoView);
14429                 return true;
14430             }
14431         }
14432         return false;
14433     },
14434
14435     /**
14436      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14437      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14438      * @param {Number} index The zero-based index of the list item to select
14439      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14440      * selected item if it is not currently in view (defaults to true)
14441      */
14442     select : function(index, scrollIntoView){
14443         this.selectedIndex = index;
14444         this.view.select(index);
14445         if(scrollIntoView !== false){
14446             var el = this.view.getNode(index);
14447             /*
14448              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14449              */
14450             if(el){
14451                 this.list.scrollChildIntoView(el, false);
14452             }
14453         }
14454     },
14455
14456     // private
14457     selectNext : function(){
14458         var ct = this.store.getCount();
14459         if(ct > 0){
14460             if(this.selectedIndex == -1){
14461                 this.select(0);
14462             }else if(this.selectedIndex < ct-1){
14463                 this.select(this.selectedIndex+1);
14464             }
14465         }
14466     },
14467
14468     // private
14469     selectPrev : function(){
14470         var ct = this.store.getCount();
14471         if(ct > 0){
14472             if(this.selectedIndex == -1){
14473                 this.select(0);
14474             }else if(this.selectedIndex != 0){
14475                 this.select(this.selectedIndex-1);
14476             }
14477         }
14478     },
14479
14480     // private
14481     onKeyUp : function(e){
14482         if(this.editable !== false && !e.isSpecialKey()){
14483             this.lastKey = e.getKey();
14484             this.dqTask.delay(this.queryDelay);
14485         }
14486     },
14487
14488     // private
14489     validateBlur : function(){
14490         return !this.list || !this.list.isVisible();   
14491     },
14492
14493     // private
14494     initQuery : function(){
14495         
14496         var v = this.getRawValue();
14497         
14498         if(this.tickable && this.editable){
14499             v = this.tickableInputEl().getValue();
14500         }
14501         
14502         this.doQuery(v);
14503     },
14504
14505     // private
14506     doForce : function(){
14507         if(this.inputEl().dom.value.length > 0){
14508             this.inputEl().dom.value =
14509                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14510              
14511         }
14512     },
14513
14514     /**
14515      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14516      * query allowing the query action to be canceled if needed.
14517      * @param {String} query The SQL query to execute
14518      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14519      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14520      * saved in the current store (defaults to false)
14521      */
14522     doQuery : function(q, forceAll){
14523         
14524         if(q === undefined || q === null){
14525             q = '';
14526         }
14527         var qe = {
14528             query: q,
14529             forceAll: forceAll,
14530             combo: this,
14531             cancel:false
14532         };
14533         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14534             return false;
14535         }
14536         q = qe.query;
14537         
14538         forceAll = qe.forceAll;
14539         if(forceAll === true || (q.length >= this.minChars)){
14540             
14541             this.hasQuery = true;
14542             
14543             if(this.lastQuery != q || this.alwaysQuery){
14544                 this.lastQuery = q;
14545                 if(this.mode == 'local'){
14546                     this.selectedIndex = -1;
14547                     if(forceAll){
14548                         this.store.clearFilter();
14549                     }else{
14550                         
14551                         if(this.specialFilter){
14552                             this.fireEvent('specialfilter', this);
14553                             this.onLoad();
14554                             return;
14555                         }
14556                         
14557                         this.store.filter(this.displayField, q);
14558                     }
14559                     
14560                     this.store.fireEvent("datachanged", this.store);
14561                     
14562                     this.onLoad();
14563                     
14564                     
14565                 }else{
14566                     
14567                     this.store.baseParams[this.queryParam] = q;
14568                     
14569                     var options = {params : this.getParams(q)};
14570                     
14571                     if(this.loadNext){
14572                         options.add = true;
14573                         options.params.start = this.page * this.pageSize;
14574                     }
14575                     
14576                     this.store.load(options);
14577                     
14578                     /*
14579                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14580                      *  we should expand the list on onLoad
14581                      *  so command out it
14582                      */
14583 //                    this.expand();
14584                 }
14585             }else{
14586                 this.selectedIndex = -1;
14587                 this.onLoad();   
14588             }
14589         }
14590         
14591         this.loadNext = false;
14592     },
14593     
14594     // private
14595     getParams : function(q){
14596         var p = {};
14597         //p[this.queryParam] = q;
14598         
14599         if(this.pageSize){
14600             p.start = 0;
14601             p.limit = this.pageSize;
14602         }
14603         return p;
14604     },
14605
14606     /**
14607      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14608      */
14609     collapse : function(){
14610         if(!this.isExpanded()){
14611             return;
14612         }
14613         
14614         this.list.hide();
14615         
14616         this.hasFocus = false;
14617         
14618         if(this.tickable){
14619             this.okBtn.hide();
14620             this.cancelBtn.hide();
14621             this.trigger.show();
14622             
14623             if(this.editable){
14624                 this.tickableInputEl().dom.value = '';
14625                 this.tickableInputEl().blur();
14626             }
14627             
14628         }
14629         
14630         Roo.get(document).un('mousedown', this.collapseIf, this);
14631         Roo.get(document).un('mousewheel', this.collapseIf, this);
14632         if (!this.editable) {
14633             Roo.get(document).un('keydown', this.listKeyPress, this);
14634         }
14635         this.fireEvent('collapse', this);
14636         
14637         this.validate();
14638     },
14639
14640     // private
14641     collapseIf : function(e){
14642         var in_combo  = e.within(this.el);
14643         var in_list =  e.within(this.list);
14644         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14645         
14646         if (in_combo || in_list || is_list) {
14647             //e.stopPropagation();
14648             return;
14649         }
14650         
14651         if(this.tickable){
14652             this.onTickableFooterButtonClick(e, false, false);
14653         }
14654
14655         this.collapse();
14656         
14657     },
14658
14659     /**
14660      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14661      */
14662     expand : function(){
14663        
14664         if(this.isExpanded() || !this.hasFocus){
14665             return;
14666         }
14667         
14668         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14669         this.list.setWidth(lw);
14670         
14671         Roo.log('expand');
14672         
14673         this.list.show();
14674         
14675         this.restrictHeight();
14676         
14677         if(this.tickable){
14678             
14679             this.tickItems = Roo.apply([], this.item);
14680             
14681             this.okBtn.show();
14682             this.cancelBtn.show();
14683             this.trigger.hide();
14684             
14685             if(this.editable){
14686                 this.tickableInputEl().focus();
14687             }
14688             
14689         }
14690         
14691         Roo.get(document).on('mousedown', this.collapseIf, this);
14692         Roo.get(document).on('mousewheel', this.collapseIf, this);
14693         if (!this.editable) {
14694             Roo.get(document).on('keydown', this.listKeyPress, this);
14695         }
14696         
14697         this.fireEvent('expand', this);
14698     },
14699
14700     // private
14701     // Implements the default empty TriggerField.onTriggerClick function
14702     onTriggerClick : function(e)
14703     {
14704         Roo.log('trigger click');
14705         
14706         if(this.disabled || !this.triggerList){
14707             return;
14708         }
14709         
14710         this.page = 0;
14711         this.loadNext = false;
14712         
14713         if(this.isExpanded()){
14714             this.collapse();
14715             if (!this.blockFocus) {
14716                 this.inputEl().focus();
14717             }
14718             
14719         }else {
14720             this.hasFocus = true;
14721             if(this.triggerAction == 'all') {
14722                 this.doQuery(this.allQuery, true);
14723             } else {
14724                 this.doQuery(this.getRawValue());
14725             }
14726             if (!this.blockFocus) {
14727                 this.inputEl().focus();
14728             }
14729         }
14730     },
14731     
14732     onTickableTriggerClick : function(e)
14733     {
14734         if(this.disabled){
14735             return;
14736         }
14737         
14738         this.page = 0;
14739         this.loadNext = false;
14740         this.hasFocus = true;
14741         
14742         if(this.triggerAction == 'all') {
14743             this.doQuery(this.allQuery, true);
14744         } else {
14745             this.doQuery(this.getRawValue());
14746         }
14747     },
14748     
14749     onSearchFieldClick : function(e)
14750     {
14751         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14752             this.onTickableFooterButtonClick(e, false, false);
14753             return;
14754         }
14755         
14756         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14757             return;
14758         }
14759         
14760         this.page = 0;
14761         this.loadNext = false;
14762         this.hasFocus = true;
14763         
14764         if(this.triggerAction == 'all') {
14765             this.doQuery(this.allQuery, true);
14766         } else {
14767             this.doQuery(this.getRawValue());
14768         }
14769     },
14770     
14771     listKeyPress : function(e)
14772     {
14773         //Roo.log('listkeypress');
14774         // scroll to first matching element based on key pres..
14775         if (e.isSpecialKey()) {
14776             return false;
14777         }
14778         var k = String.fromCharCode(e.getKey()).toUpperCase();
14779         //Roo.log(k);
14780         var match  = false;
14781         var csel = this.view.getSelectedNodes();
14782         var cselitem = false;
14783         if (csel.length) {
14784             var ix = this.view.indexOf(csel[0]);
14785             cselitem  = this.store.getAt(ix);
14786             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14787                 cselitem = false;
14788             }
14789             
14790         }
14791         
14792         this.store.each(function(v) { 
14793             if (cselitem) {
14794                 // start at existing selection.
14795                 if (cselitem.id == v.id) {
14796                     cselitem = false;
14797                 }
14798                 return true;
14799             }
14800                 
14801             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14802                 match = this.store.indexOf(v);
14803                 return false;
14804             }
14805             return true;
14806         }, this);
14807         
14808         if (match === false) {
14809             return true; // no more action?
14810         }
14811         // scroll to?
14812         this.view.select(match);
14813         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14814         sn.scrollIntoView(sn.dom.parentNode, false);
14815     },
14816     
14817     onViewScroll : function(e, t){
14818         
14819         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){
14820             return;
14821         }
14822         
14823         this.hasQuery = true;
14824         
14825         this.loading = this.list.select('.loading', true).first();
14826         
14827         if(this.loading === null){
14828             this.list.createChild({
14829                 tag: 'div',
14830                 cls: 'loading roo-select2-more-results roo-select2-active',
14831                 html: 'Loading more results...'
14832             });
14833             
14834             this.loading = this.list.select('.loading', true).first();
14835             
14836             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14837             
14838             this.loading.hide();
14839         }
14840         
14841         this.loading.show();
14842         
14843         var _combo = this;
14844         
14845         this.page++;
14846         this.loadNext = true;
14847         
14848         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14849         
14850         return;
14851     },
14852     
14853     addItem : function(o)
14854     {   
14855         var dv = ''; // display value
14856         
14857         if (this.displayField) {
14858             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14859         } else {
14860             // this is an error condition!!!
14861             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14862         }
14863         
14864         if(!dv.length){
14865             return;
14866         }
14867         
14868         var choice = this.choices.createChild({
14869             tag: 'li',
14870             cls: 'roo-select2-search-choice',
14871             cn: [
14872                 {
14873                     tag: 'div',
14874                     html: dv
14875                 },
14876                 {
14877                     tag: 'a',
14878                     href: '#',
14879                     cls: 'roo-select2-search-choice-close fa fa-times',
14880                     tabindex: '-1'
14881                 }
14882             ]
14883             
14884         }, this.searchField);
14885         
14886         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14887         
14888         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14889         
14890         this.item.push(o);
14891         
14892         this.lastData = o;
14893         
14894         this.syncValue();
14895         
14896         this.inputEl().dom.value = '';
14897         
14898         this.validate();
14899     },
14900     
14901     onRemoveItem : function(e, _self, o)
14902     {
14903         e.preventDefault();
14904         
14905         this.lastItem = Roo.apply([], this.item);
14906         
14907         var index = this.item.indexOf(o.data) * 1;
14908         
14909         if( index < 0){
14910             Roo.log('not this item?!');
14911             return;
14912         }
14913         
14914         this.item.splice(index, 1);
14915         o.item.remove();
14916         
14917         this.syncValue();
14918         
14919         this.fireEvent('remove', this, e);
14920         
14921         this.validate();
14922         
14923     },
14924     
14925     syncValue : function()
14926     {
14927         if(!this.item.length){
14928             this.clearValue();
14929             return;
14930         }
14931             
14932         var value = [];
14933         var _this = this;
14934         Roo.each(this.item, function(i){
14935             if(_this.valueField){
14936                 value.push(i[_this.valueField]);
14937                 return;
14938             }
14939
14940             value.push(i);
14941         });
14942
14943         this.value = value.join(',');
14944
14945         if(this.hiddenField){
14946             this.hiddenField.dom.value = this.value;
14947         }
14948         
14949         this.store.fireEvent("datachanged", this.store);
14950         
14951         this.validate();
14952     },
14953     
14954     clearItem : function()
14955     {
14956         if(!this.multiple){
14957             return;
14958         }
14959         
14960         this.item = [];
14961         
14962         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14963            c.remove();
14964         });
14965         
14966         this.syncValue();
14967         
14968         this.validate();
14969         
14970         if(this.tickable && !Roo.isTouch){
14971             this.view.refresh();
14972         }
14973     },
14974     
14975     inputEl: function ()
14976     {
14977         if(Roo.isIOS && this.useNativeIOS){
14978             return this.el.select('select.roo-ios-select', true).first();
14979         }
14980         
14981         if(Roo.isTouch && this.mobileTouchView){
14982             return this.el.select('input.form-control',true).first();
14983         }
14984         
14985         if(this.tickable){
14986             return this.searchField;
14987         }
14988         
14989         return this.el.select('input.form-control',true).first();
14990     },
14991     
14992     onTickableFooterButtonClick : function(e, btn, el)
14993     {
14994         e.preventDefault();
14995         
14996         this.lastItem = Roo.apply([], this.item);
14997         
14998         if(btn && btn.name == 'cancel'){
14999             this.tickItems = Roo.apply([], this.item);
15000             this.collapse();
15001             return;
15002         }
15003         
15004         this.clearItem();
15005         
15006         var _this = this;
15007         
15008         Roo.each(this.tickItems, function(o){
15009             _this.addItem(o);
15010         });
15011         
15012         this.collapse();
15013         
15014     },
15015     
15016     validate : function()
15017     {
15018         if(this.getVisibilityEl().hasClass('hidden')){
15019             return true;
15020         }
15021         
15022         var v = this.getRawValue();
15023         
15024         if(this.multiple){
15025             v = this.getValue();
15026         }
15027         
15028         if(this.disabled || this.allowBlank || v.length){
15029             this.markValid();
15030             return true;
15031         }
15032         
15033         this.markInvalid();
15034         return false;
15035     },
15036     
15037     tickableInputEl : function()
15038     {
15039         if(!this.tickable || !this.editable){
15040             return this.inputEl();
15041         }
15042         
15043         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15044     },
15045     
15046     
15047     getAutoCreateTouchView : function()
15048     {
15049         var id = Roo.id();
15050         
15051         var cfg = {
15052             cls: 'form-group' //input-group
15053         };
15054         
15055         var input =  {
15056             tag: 'input',
15057             id : id,
15058             type : this.inputType,
15059             cls : 'form-control x-combo-noedit',
15060             autocomplete: 'new-password',
15061             placeholder : this.placeholder || '',
15062             readonly : true
15063         };
15064         
15065         if (this.name) {
15066             input.name = this.name;
15067         }
15068         
15069         if (this.size) {
15070             input.cls += ' input-' + this.size;
15071         }
15072         
15073         if (this.disabled) {
15074             input.disabled = true;
15075         }
15076         
15077         var inputblock = {
15078             cls : '',
15079             cn : [
15080                 input
15081             ]
15082         };
15083         
15084         if(this.before){
15085             inputblock.cls += ' input-group';
15086             
15087             inputblock.cn.unshift({
15088                 tag :'span',
15089                 cls : 'input-group-addon input-group-prepend input-group-text',
15090                 html : this.before
15091             });
15092         }
15093         
15094         if(this.removable && !this.multiple){
15095             inputblock.cls += ' roo-removable';
15096             
15097             inputblock.cn.push({
15098                 tag: 'button',
15099                 html : 'x',
15100                 cls : 'roo-combo-removable-btn close'
15101             });
15102         }
15103
15104         if(this.hasFeedback && !this.allowBlank){
15105             
15106             inputblock.cls += ' has-feedback';
15107             
15108             inputblock.cn.push({
15109                 tag: 'span',
15110                 cls: 'glyphicon form-control-feedback'
15111             });
15112             
15113         }
15114         
15115         if (this.after) {
15116             
15117             inputblock.cls += (this.before) ? '' : ' input-group';
15118             
15119             inputblock.cn.push({
15120                 tag :'span',
15121                 cls : 'input-group-addon input-group-append input-group-text',
15122                 html : this.after
15123             });
15124         }
15125
15126         
15127         var ibwrap = inputblock;
15128         
15129         if(this.multiple){
15130             ibwrap = {
15131                 tag: 'ul',
15132                 cls: 'roo-select2-choices',
15133                 cn:[
15134                     {
15135                         tag: 'li',
15136                         cls: 'roo-select2-search-field',
15137                         cn: [
15138
15139                             inputblock
15140                         ]
15141                     }
15142                 ]
15143             };
15144         
15145             
15146         }
15147         
15148         var combobox = {
15149             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15150            cn: [
15151                 {
15152                     tag: 'input',
15153                     type : 'hidden',
15154                     cls: 'form-hidden-field'
15155                 },
15156                 ibwrap
15157             ]
15158         };
15159         
15160         if(!this.multiple && this.showToggleBtn){
15161             
15162             var caret = {
15163                         tag: 'span',
15164                         cls: 'caret'
15165             };
15166             
15167             if (this.caret != false) {
15168                 caret = {
15169                      tag: 'i',
15170                      cls: 'fa fa-' + this.caret
15171                 };
15172                 
15173             }
15174             
15175             combobox.cn.push({
15176                 tag :'span',
15177                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15178                 cn : [
15179                     caret,
15180                     {
15181                         tag: 'span',
15182                         cls: 'combobox-clear',
15183                         cn  : [
15184                             {
15185                                 tag : 'i',
15186                                 cls: 'icon-remove'
15187                             }
15188                         ]
15189                     }
15190                 ]
15191
15192             })
15193         }
15194         
15195         if(this.multiple){
15196             combobox.cls += ' roo-select2-container-multi';
15197         }
15198         
15199         var align = this.labelAlign || this.parentLabelAlign();
15200         
15201         if (align ==='left' && this.fieldLabel.length) {
15202
15203             cfg.cn = [
15204                 {
15205                    tag : 'i',
15206                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15207                    tooltip : 'This field is required'
15208                 },
15209                 {
15210                     tag: 'label',
15211                     cls : 'control-label col-form-label',
15212                     html : this.fieldLabel
15213
15214                 },
15215                 {
15216                     cls : '', 
15217                     cn: [
15218                         combobox
15219                     ]
15220                 }
15221             ];
15222             
15223             var labelCfg = cfg.cn[1];
15224             var contentCfg = cfg.cn[2];
15225             
15226
15227             if(this.indicatorpos == 'right'){
15228                 cfg.cn = [
15229                     {
15230                         tag: 'label',
15231                         'for' :  id,
15232                         cls : 'control-label col-form-label',
15233                         cn : [
15234                             {
15235                                 tag : 'span',
15236                                 html : this.fieldLabel
15237                             },
15238                             {
15239                                 tag : 'i',
15240                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15241                                 tooltip : 'This field is required'
15242                             }
15243                         ]
15244                     },
15245                     {
15246                         cls : "",
15247                         cn: [
15248                             combobox
15249                         ]
15250                     }
15251
15252                 ];
15253                 
15254                 labelCfg = cfg.cn[0];
15255                 contentCfg = cfg.cn[1];
15256             }
15257             
15258            
15259             
15260             if(this.labelWidth > 12){
15261                 labelCfg.style = "width: " + this.labelWidth + 'px';
15262             }
15263             
15264             if(this.labelWidth < 13 && this.labelmd == 0){
15265                 this.labelmd = this.labelWidth;
15266             }
15267             
15268             if(this.labellg > 0){
15269                 labelCfg.cls += ' col-lg-' + this.labellg;
15270                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15271             }
15272             
15273             if(this.labelmd > 0){
15274                 labelCfg.cls += ' col-md-' + this.labelmd;
15275                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15276             }
15277             
15278             if(this.labelsm > 0){
15279                 labelCfg.cls += ' col-sm-' + this.labelsm;
15280                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15281             }
15282             
15283             if(this.labelxs > 0){
15284                 labelCfg.cls += ' col-xs-' + this.labelxs;
15285                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15286             }
15287                 
15288                 
15289         } else if ( this.fieldLabel.length) {
15290             cfg.cn = [
15291                 {
15292                    tag : 'i',
15293                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15294                    tooltip : 'This field is required'
15295                 },
15296                 {
15297                     tag: 'label',
15298                     cls : 'control-label',
15299                     html : this.fieldLabel
15300
15301                 },
15302                 {
15303                     cls : '', 
15304                     cn: [
15305                         combobox
15306                     ]
15307                 }
15308             ];
15309             
15310             if(this.indicatorpos == 'right'){
15311                 cfg.cn = [
15312                     {
15313                         tag: 'label',
15314                         cls : 'control-label',
15315                         html : this.fieldLabel,
15316                         cn : [
15317                             {
15318                                tag : 'i',
15319                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15320                                tooltip : 'This field is required'
15321                             }
15322                         ]
15323                     },
15324                     {
15325                         cls : '', 
15326                         cn: [
15327                             combobox
15328                         ]
15329                     }
15330                 ];
15331             }
15332         } else {
15333             cfg.cn = combobox;    
15334         }
15335         
15336         
15337         var settings = this;
15338         
15339         ['xs','sm','md','lg'].map(function(size){
15340             if (settings[size]) {
15341                 cfg.cls += ' col-' + size + '-' + settings[size];
15342             }
15343         });
15344         
15345         return cfg;
15346     },
15347     
15348     initTouchView : function()
15349     {
15350         this.renderTouchView();
15351         
15352         this.touchViewEl.on('scroll', function(){
15353             this.el.dom.scrollTop = 0;
15354         }, this);
15355         
15356         this.originalValue = this.getValue();
15357         
15358         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15359         
15360         this.inputEl().on("click", this.showTouchView, this);
15361         if (this.triggerEl) {
15362             this.triggerEl.on("click", this.showTouchView, this);
15363         }
15364         
15365         
15366         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15367         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15368         
15369         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15370         
15371         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15372         this.store.on('load', this.onTouchViewLoad, this);
15373         this.store.on('loadexception', this.onTouchViewLoadException, this);
15374         
15375         if(this.hiddenName){
15376             
15377             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15378             
15379             this.hiddenField.dom.value =
15380                 this.hiddenValue !== undefined ? this.hiddenValue :
15381                 this.value !== undefined ? this.value : '';
15382         
15383             this.el.dom.removeAttribute('name');
15384             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15385         }
15386         
15387         if(this.multiple){
15388             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15389             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15390         }
15391         
15392         if(this.removable && !this.multiple){
15393             var close = this.closeTriggerEl();
15394             if(close){
15395                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15396                 close.on('click', this.removeBtnClick, this, close);
15397             }
15398         }
15399         /*
15400          * fix the bug in Safari iOS8
15401          */
15402         this.inputEl().on("focus", function(e){
15403             document.activeElement.blur();
15404         }, this);
15405         
15406         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15407         
15408         return;
15409         
15410         
15411     },
15412     
15413     renderTouchView : function()
15414     {
15415         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15416         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15417         
15418         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15419         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15420         
15421         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15422         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15423         this.touchViewBodyEl.setStyle('overflow', 'auto');
15424         
15425         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15426         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15427         
15428         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15429         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15430         
15431     },
15432     
15433     showTouchView : function()
15434     {
15435         if(this.disabled){
15436             return;
15437         }
15438         
15439         this.touchViewHeaderEl.hide();
15440
15441         if(this.modalTitle.length){
15442             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15443             this.touchViewHeaderEl.show();
15444         }
15445
15446         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15447         this.touchViewEl.show();
15448
15449         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15450         
15451         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15452         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15453
15454         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15455
15456         if(this.modalTitle.length){
15457             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15458         }
15459         
15460         this.touchViewBodyEl.setHeight(bodyHeight);
15461
15462         if(this.animate){
15463             var _this = this;
15464             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15465         }else{
15466             this.touchViewEl.addClass('in');
15467         }
15468         
15469         if(this._touchViewMask){
15470             Roo.get(document.body).addClass("x-body-masked");
15471             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15472             this._touchViewMask.setStyle('z-index', 10000);
15473             this._touchViewMask.addClass('show');
15474         }
15475         
15476         this.doTouchViewQuery();
15477         
15478     },
15479     
15480     hideTouchView : function()
15481     {
15482         this.touchViewEl.removeClass('in');
15483
15484         if(this.animate){
15485             var _this = this;
15486             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15487         }else{
15488             this.touchViewEl.setStyle('display', 'none');
15489         }
15490         
15491         if(this._touchViewMask){
15492             this._touchViewMask.removeClass('show');
15493             Roo.get(document.body).removeClass("x-body-masked");
15494         }
15495     },
15496     
15497     setTouchViewValue : function()
15498     {
15499         if(this.multiple){
15500             this.clearItem();
15501         
15502             var _this = this;
15503
15504             Roo.each(this.tickItems, function(o){
15505                 this.addItem(o);
15506             }, this);
15507         }
15508         
15509         this.hideTouchView();
15510     },
15511     
15512     doTouchViewQuery : function()
15513     {
15514         var qe = {
15515             query: '',
15516             forceAll: true,
15517             combo: this,
15518             cancel:false
15519         };
15520         
15521         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15522             return false;
15523         }
15524         
15525         if(!this.alwaysQuery || this.mode == 'local'){
15526             this.onTouchViewLoad();
15527             return;
15528         }
15529         
15530         this.store.load();
15531     },
15532     
15533     onTouchViewBeforeLoad : function(combo,opts)
15534     {
15535         return;
15536     },
15537
15538     // private
15539     onTouchViewLoad : function()
15540     {
15541         if(this.store.getCount() < 1){
15542             this.onTouchViewEmptyResults();
15543             return;
15544         }
15545         
15546         this.clearTouchView();
15547         
15548         var rawValue = this.getRawValue();
15549         
15550         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15551         
15552         this.tickItems = [];
15553         
15554         this.store.data.each(function(d, rowIndex){
15555             var row = this.touchViewListGroup.createChild(template);
15556             
15557             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15558                 row.addClass(d.data.cls);
15559             }
15560             
15561             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15562                 var cfg = {
15563                     data : d.data,
15564                     html : d.data[this.displayField]
15565                 };
15566                 
15567                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15568                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15569                 }
15570             }
15571             row.removeClass('selected');
15572             if(!this.multiple && this.valueField &&
15573                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15574             {
15575                 // radio buttons..
15576                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15577                 row.addClass('selected');
15578             }
15579             
15580             if(this.multiple && this.valueField &&
15581                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15582             {
15583                 
15584                 // checkboxes...
15585                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15586                 this.tickItems.push(d.data);
15587             }
15588             
15589             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15590             
15591         }, this);
15592         
15593         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15594         
15595         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15596
15597         if(this.modalTitle.length){
15598             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15599         }
15600
15601         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15602         
15603         if(this.mobile_restrict_height && listHeight < bodyHeight){
15604             this.touchViewBodyEl.setHeight(listHeight);
15605         }
15606         
15607         var _this = this;
15608         
15609         if(firstChecked && listHeight > bodyHeight){
15610             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15611         }
15612         
15613     },
15614     
15615     onTouchViewLoadException : function()
15616     {
15617         this.hideTouchView();
15618     },
15619     
15620     onTouchViewEmptyResults : function()
15621     {
15622         this.clearTouchView();
15623         
15624         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15625         
15626         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15627         
15628     },
15629     
15630     clearTouchView : function()
15631     {
15632         this.touchViewListGroup.dom.innerHTML = '';
15633     },
15634     
15635     onTouchViewClick : function(e, el, o)
15636     {
15637         e.preventDefault();
15638         
15639         var row = o.row;
15640         var rowIndex = o.rowIndex;
15641         
15642         var r = this.store.getAt(rowIndex);
15643         
15644         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15645             
15646             if(!this.multiple){
15647                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15648                     c.dom.removeAttribute('checked');
15649                 }, this);
15650
15651                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15652
15653                 this.setFromData(r.data);
15654
15655                 var close = this.closeTriggerEl();
15656
15657                 if(close){
15658                     close.show();
15659                 }
15660
15661                 this.hideTouchView();
15662
15663                 this.fireEvent('select', this, r, rowIndex);
15664
15665                 return;
15666             }
15667
15668             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15669                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15670                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15671                 return;
15672             }
15673
15674             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15675             this.addItem(r.data);
15676             this.tickItems.push(r.data);
15677         }
15678     },
15679     
15680     getAutoCreateNativeIOS : function()
15681     {
15682         var cfg = {
15683             cls: 'form-group' //input-group,
15684         };
15685         
15686         var combobox =  {
15687             tag: 'select',
15688             cls : 'roo-ios-select'
15689         };
15690         
15691         if (this.name) {
15692             combobox.name = this.name;
15693         }
15694         
15695         if (this.disabled) {
15696             combobox.disabled = true;
15697         }
15698         
15699         var settings = this;
15700         
15701         ['xs','sm','md','lg'].map(function(size){
15702             if (settings[size]) {
15703                 cfg.cls += ' col-' + size + '-' + settings[size];
15704             }
15705         });
15706         
15707         cfg.cn = combobox;
15708         
15709         return cfg;
15710         
15711     },
15712     
15713     initIOSView : function()
15714     {
15715         this.store.on('load', this.onIOSViewLoad, this);
15716         
15717         return;
15718     },
15719     
15720     onIOSViewLoad : function()
15721     {
15722         if(this.store.getCount() < 1){
15723             return;
15724         }
15725         
15726         this.clearIOSView();
15727         
15728         if(this.allowBlank) {
15729             
15730             var default_text = '-- SELECT --';
15731             
15732             if(this.placeholder.length){
15733                 default_text = this.placeholder;
15734             }
15735             
15736             if(this.emptyTitle.length){
15737                 default_text += ' - ' + this.emptyTitle + ' -';
15738             }
15739             
15740             var opt = this.inputEl().createChild({
15741                 tag: 'option',
15742                 value : 0,
15743                 html : default_text
15744             });
15745             
15746             var o = {};
15747             o[this.valueField] = 0;
15748             o[this.displayField] = default_text;
15749             
15750             this.ios_options.push({
15751                 data : o,
15752                 el : opt
15753             });
15754             
15755         }
15756         
15757         this.store.data.each(function(d, rowIndex){
15758             
15759             var html = '';
15760             
15761             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15762                 html = d.data[this.displayField];
15763             }
15764             
15765             var value = '';
15766             
15767             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15768                 value = d.data[this.valueField];
15769             }
15770             
15771             var option = {
15772                 tag: 'option',
15773                 value : value,
15774                 html : html
15775             };
15776             
15777             if(this.value == d.data[this.valueField]){
15778                 option['selected'] = true;
15779             }
15780             
15781             var opt = this.inputEl().createChild(option);
15782             
15783             this.ios_options.push({
15784                 data : d.data,
15785                 el : opt
15786             });
15787             
15788         }, this);
15789         
15790         this.inputEl().on('change', function(){
15791            this.fireEvent('select', this);
15792         }, this);
15793         
15794     },
15795     
15796     clearIOSView: function()
15797     {
15798         this.inputEl().dom.innerHTML = '';
15799         
15800         this.ios_options = [];
15801     },
15802     
15803     setIOSValue: function(v)
15804     {
15805         this.value = v;
15806         
15807         if(!this.ios_options){
15808             return;
15809         }
15810         
15811         Roo.each(this.ios_options, function(opts){
15812            
15813            opts.el.dom.removeAttribute('selected');
15814            
15815            if(opts.data[this.valueField] != v){
15816                return;
15817            }
15818            
15819            opts.el.dom.setAttribute('selected', true);
15820            
15821         }, this);
15822     }
15823
15824     /** 
15825     * @cfg {Boolean} grow 
15826     * @hide 
15827     */
15828     /** 
15829     * @cfg {Number} growMin 
15830     * @hide 
15831     */
15832     /** 
15833     * @cfg {Number} growMax 
15834     * @hide 
15835     */
15836     /**
15837      * @hide
15838      * @method autoSize
15839      */
15840 });
15841
15842 Roo.apply(Roo.bootstrap.ComboBox,  {
15843     
15844     header : {
15845         tag: 'div',
15846         cls: 'modal-header',
15847         cn: [
15848             {
15849                 tag: 'h4',
15850                 cls: 'modal-title'
15851             }
15852         ]
15853     },
15854     
15855     body : {
15856         tag: 'div',
15857         cls: 'modal-body',
15858         cn: [
15859             {
15860                 tag: 'ul',
15861                 cls: 'list-group'
15862             }
15863         ]
15864     },
15865     
15866     listItemRadio : {
15867         tag: 'li',
15868         cls: 'list-group-item',
15869         cn: [
15870             {
15871                 tag: 'span',
15872                 cls: 'roo-combobox-list-group-item-value'
15873             },
15874             {
15875                 tag: 'div',
15876                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15877                 cn: [
15878                     {
15879                         tag: 'input',
15880                         type: 'radio'
15881                     },
15882                     {
15883                         tag: 'label'
15884                     }
15885                 ]
15886             }
15887         ]
15888     },
15889     
15890     listItemCheckbox : {
15891         tag: 'li',
15892         cls: 'list-group-item',
15893         cn: [
15894             {
15895                 tag: 'span',
15896                 cls: 'roo-combobox-list-group-item-value'
15897             },
15898             {
15899                 tag: 'div',
15900                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15901                 cn: [
15902                     {
15903                         tag: 'input',
15904                         type: 'checkbox'
15905                     },
15906                     {
15907                         tag: 'label'
15908                     }
15909                 ]
15910             }
15911         ]
15912     },
15913     
15914     emptyResult : {
15915         tag: 'div',
15916         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15917     },
15918     
15919     footer : {
15920         tag: 'div',
15921         cls: 'modal-footer',
15922         cn: [
15923             {
15924                 tag: 'div',
15925                 cls: 'row',
15926                 cn: [
15927                     {
15928                         tag: 'div',
15929                         cls: 'col-xs-6 text-left',
15930                         cn: {
15931                             tag: 'button',
15932                             cls: 'btn btn-danger roo-touch-view-cancel',
15933                             html: 'Cancel'
15934                         }
15935                     },
15936                     {
15937                         tag: 'div',
15938                         cls: 'col-xs-6 text-right',
15939                         cn: {
15940                             tag: 'button',
15941                             cls: 'btn btn-success roo-touch-view-ok',
15942                             html: 'OK'
15943                         }
15944                     }
15945                 ]
15946             }
15947         ]
15948         
15949     }
15950 });
15951
15952 Roo.apply(Roo.bootstrap.ComboBox,  {
15953     
15954     touchViewTemplate : {
15955         tag: 'div',
15956         cls: 'modal fade roo-combobox-touch-view',
15957         cn: [
15958             {
15959                 tag: 'div',
15960                 cls: 'modal-dialog',
15961                 style : 'position:fixed', // we have to fix position....
15962                 cn: [
15963                     {
15964                         tag: 'div',
15965                         cls: 'modal-content',
15966                         cn: [
15967                             Roo.bootstrap.ComboBox.header,
15968                             Roo.bootstrap.ComboBox.body,
15969                             Roo.bootstrap.ComboBox.footer
15970                         ]
15971                     }
15972                 ]
15973             }
15974         ]
15975     }
15976 });/*
15977  * Based on:
15978  * Ext JS Library 1.1.1
15979  * Copyright(c) 2006-2007, Ext JS, LLC.
15980  *
15981  * Originally Released Under LGPL - original licence link has changed is not relivant.
15982  *
15983  * Fork - LGPL
15984  * <script type="text/javascript">
15985  */
15986
15987 /**
15988  * @class Roo.View
15989  * @extends Roo.util.Observable
15990  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15991  * This class also supports single and multi selection modes. <br>
15992  * Create a data model bound view:
15993  <pre><code>
15994  var store = new Roo.data.Store(...);
15995
15996  var view = new Roo.View({
15997     el : "my-element",
15998     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15999  
16000     singleSelect: true,
16001     selectedClass: "ydataview-selected",
16002     store: store
16003  });
16004
16005  // listen for node click?
16006  view.on("click", function(vw, index, node, e){
16007  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16008  });
16009
16010  // load XML data
16011  dataModel.load("foobar.xml");
16012  </code></pre>
16013  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16014  * <br><br>
16015  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16016  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16017  * 
16018  * Note: old style constructor is still suported (container, template, config)
16019  * 
16020  * @constructor
16021  * Create a new View
16022  * @param {Object} config The config object
16023  * 
16024  */
16025 Roo.View = function(config, depreciated_tpl, depreciated_config){
16026     
16027     this.parent = false;
16028     
16029     if (typeof(depreciated_tpl) == 'undefined') {
16030         // new way.. - universal constructor.
16031         Roo.apply(this, config);
16032         this.el  = Roo.get(this.el);
16033     } else {
16034         // old format..
16035         this.el  = Roo.get(config);
16036         this.tpl = depreciated_tpl;
16037         Roo.apply(this, depreciated_config);
16038     }
16039     this.wrapEl  = this.el.wrap().wrap();
16040     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16041     
16042     
16043     if(typeof(this.tpl) == "string"){
16044         this.tpl = new Roo.Template(this.tpl);
16045     } else {
16046         // support xtype ctors..
16047         this.tpl = new Roo.factory(this.tpl, Roo);
16048     }
16049     
16050     
16051     this.tpl.compile();
16052     
16053     /** @private */
16054     this.addEvents({
16055         /**
16056          * @event beforeclick
16057          * Fires before a click is processed. Returns false to cancel the default action.
16058          * @param {Roo.View} this
16059          * @param {Number} index The index of the target node
16060          * @param {HTMLElement} node The target node
16061          * @param {Roo.EventObject} e The raw event object
16062          */
16063             "beforeclick" : true,
16064         /**
16065          * @event click
16066          * Fires when a template node is clicked.
16067          * @param {Roo.View} this
16068          * @param {Number} index The index of the target node
16069          * @param {HTMLElement} node The target node
16070          * @param {Roo.EventObject} e The raw event object
16071          */
16072             "click" : true,
16073         /**
16074          * @event dblclick
16075          * Fires when a template node is double clicked.
16076          * @param {Roo.View} this
16077          * @param {Number} index The index of the target node
16078          * @param {HTMLElement} node The target node
16079          * @param {Roo.EventObject} e The raw event object
16080          */
16081             "dblclick" : true,
16082         /**
16083          * @event contextmenu
16084          * Fires when a template node is right clicked.
16085          * @param {Roo.View} this
16086          * @param {Number} index The index of the target node
16087          * @param {HTMLElement} node The target node
16088          * @param {Roo.EventObject} e The raw event object
16089          */
16090             "contextmenu" : true,
16091         /**
16092          * @event selectionchange
16093          * Fires when the selected nodes change.
16094          * @param {Roo.View} this
16095          * @param {Array} selections Array of the selected nodes
16096          */
16097             "selectionchange" : true,
16098     
16099         /**
16100          * @event beforeselect
16101          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16102          * @param {Roo.View} this
16103          * @param {HTMLElement} node The node to be selected
16104          * @param {Array} selections Array of currently selected nodes
16105          */
16106             "beforeselect" : true,
16107         /**
16108          * @event preparedata
16109          * Fires on every row to render, to allow you to change the data.
16110          * @param {Roo.View} this
16111          * @param {Object} data to be rendered (change this)
16112          */
16113           "preparedata" : true
16114           
16115           
16116         });
16117
16118
16119
16120     this.el.on({
16121         "click": this.onClick,
16122         "dblclick": this.onDblClick,
16123         "contextmenu": this.onContextMenu,
16124         scope:this
16125     });
16126
16127     this.selections = [];
16128     this.nodes = [];
16129     this.cmp = new Roo.CompositeElementLite([]);
16130     if(this.store){
16131         this.store = Roo.factory(this.store, Roo.data);
16132         this.setStore(this.store, true);
16133     }
16134     
16135     if ( this.footer && this.footer.xtype) {
16136            
16137          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16138         
16139         this.footer.dataSource = this.store;
16140         this.footer.container = fctr;
16141         this.footer = Roo.factory(this.footer, Roo);
16142         fctr.insertFirst(this.el);
16143         
16144         // this is a bit insane - as the paging toolbar seems to detach the el..
16145 //        dom.parentNode.parentNode.parentNode
16146          // they get detached?
16147     }
16148     
16149     
16150     Roo.View.superclass.constructor.call(this);
16151     
16152     
16153 };
16154
16155 Roo.extend(Roo.View, Roo.util.Observable, {
16156     
16157      /**
16158      * @cfg {Roo.data.Store} store Data store to load data from.
16159      */
16160     store : false,
16161     
16162     /**
16163      * @cfg {String|Roo.Element} el The container element.
16164      */
16165     el : '',
16166     
16167     /**
16168      * @cfg {String|Roo.Template} tpl The template used by this View 
16169      */
16170     tpl : false,
16171     /**
16172      * @cfg {String} dataName the named area of the template to use as the data area
16173      *                          Works with domtemplates roo-name="name"
16174      */
16175     dataName: false,
16176     /**
16177      * @cfg {String} selectedClass The css class to add to selected nodes
16178      */
16179     selectedClass : "x-view-selected",
16180      /**
16181      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16182      */
16183     emptyText : "",
16184     
16185     /**
16186      * @cfg {String} text to display on mask (default Loading)
16187      */
16188     mask : false,
16189     /**
16190      * @cfg {Boolean} multiSelect Allow multiple selection
16191      */
16192     multiSelect : false,
16193     /**
16194      * @cfg {Boolean} singleSelect Allow single selection
16195      */
16196     singleSelect:  false,
16197     
16198     /**
16199      * @cfg {Boolean} toggleSelect - selecting 
16200      */
16201     toggleSelect : false,
16202     
16203     /**
16204      * @cfg {Boolean} tickable - selecting 
16205      */
16206     tickable : false,
16207     
16208     /**
16209      * Returns the element this view is bound to.
16210      * @return {Roo.Element}
16211      */
16212     getEl : function(){
16213         return this.wrapEl;
16214     },
16215     
16216     
16217
16218     /**
16219      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16220      */
16221     refresh : function(){
16222         //Roo.log('refresh');
16223         var t = this.tpl;
16224         
16225         // if we are using something like 'domtemplate', then
16226         // the what gets used is:
16227         // t.applySubtemplate(NAME, data, wrapping data..)
16228         // the outer template then get' applied with
16229         //     the store 'extra data'
16230         // and the body get's added to the
16231         //      roo-name="data" node?
16232         //      <span class='roo-tpl-{name}'></span> ?????
16233         
16234         
16235         
16236         this.clearSelections();
16237         this.el.update("");
16238         var html = [];
16239         var records = this.store.getRange();
16240         if(records.length < 1) {
16241             
16242             // is this valid??  = should it render a template??
16243             
16244             this.el.update(this.emptyText);
16245             return;
16246         }
16247         var el = this.el;
16248         if (this.dataName) {
16249             this.el.update(t.apply(this.store.meta)); //????
16250             el = this.el.child('.roo-tpl-' + this.dataName);
16251         }
16252         
16253         for(var i = 0, len = records.length; i < len; i++){
16254             var data = this.prepareData(records[i].data, i, records[i]);
16255             this.fireEvent("preparedata", this, data, i, records[i]);
16256             
16257             var d = Roo.apply({}, data);
16258             
16259             if(this.tickable){
16260                 Roo.apply(d, {'roo-id' : Roo.id()});
16261                 
16262                 var _this = this;
16263             
16264                 Roo.each(this.parent.item, function(item){
16265                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16266                         return;
16267                     }
16268                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16269                 });
16270             }
16271             
16272             html[html.length] = Roo.util.Format.trim(
16273                 this.dataName ?
16274                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16275                     t.apply(d)
16276             );
16277         }
16278         
16279         
16280         
16281         el.update(html.join(""));
16282         this.nodes = el.dom.childNodes;
16283         this.updateIndexes(0);
16284     },
16285     
16286
16287     /**
16288      * Function to override to reformat the data that is sent to
16289      * the template for each node.
16290      * DEPRICATED - use the preparedata event handler.
16291      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16292      * a JSON object for an UpdateManager bound view).
16293      */
16294     prepareData : function(data, index, record)
16295     {
16296         this.fireEvent("preparedata", this, data, index, record);
16297         return data;
16298     },
16299
16300     onUpdate : function(ds, record){
16301         // Roo.log('on update');   
16302         this.clearSelections();
16303         var index = this.store.indexOf(record);
16304         var n = this.nodes[index];
16305         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16306         n.parentNode.removeChild(n);
16307         this.updateIndexes(index, index);
16308     },
16309
16310     
16311     
16312 // --------- FIXME     
16313     onAdd : function(ds, records, index)
16314     {
16315         //Roo.log(['on Add', ds, records, index] );        
16316         this.clearSelections();
16317         if(this.nodes.length == 0){
16318             this.refresh();
16319             return;
16320         }
16321         var n = this.nodes[index];
16322         for(var i = 0, len = records.length; i < len; i++){
16323             var d = this.prepareData(records[i].data, i, records[i]);
16324             if(n){
16325                 this.tpl.insertBefore(n, d);
16326             }else{
16327                 
16328                 this.tpl.append(this.el, d);
16329             }
16330         }
16331         this.updateIndexes(index);
16332     },
16333
16334     onRemove : function(ds, record, index){
16335        // Roo.log('onRemove');
16336         this.clearSelections();
16337         var el = this.dataName  ?
16338             this.el.child('.roo-tpl-' + this.dataName) :
16339             this.el; 
16340         
16341         el.dom.removeChild(this.nodes[index]);
16342         this.updateIndexes(index);
16343     },
16344
16345     /**
16346      * Refresh an individual node.
16347      * @param {Number} index
16348      */
16349     refreshNode : function(index){
16350         this.onUpdate(this.store, this.store.getAt(index));
16351     },
16352
16353     updateIndexes : function(startIndex, endIndex){
16354         var ns = this.nodes;
16355         startIndex = startIndex || 0;
16356         endIndex = endIndex || ns.length - 1;
16357         for(var i = startIndex; i <= endIndex; i++){
16358             ns[i].nodeIndex = i;
16359         }
16360     },
16361
16362     /**
16363      * Changes the data store this view uses and refresh the view.
16364      * @param {Store} store
16365      */
16366     setStore : function(store, initial){
16367         if(!initial && this.store){
16368             this.store.un("datachanged", this.refresh);
16369             this.store.un("add", this.onAdd);
16370             this.store.un("remove", this.onRemove);
16371             this.store.un("update", this.onUpdate);
16372             this.store.un("clear", this.refresh);
16373             this.store.un("beforeload", this.onBeforeLoad);
16374             this.store.un("load", this.onLoad);
16375             this.store.un("loadexception", this.onLoad);
16376         }
16377         if(store){
16378           
16379             store.on("datachanged", this.refresh, this);
16380             store.on("add", this.onAdd, this);
16381             store.on("remove", this.onRemove, this);
16382             store.on("update", this.onUpdate, this);
16383             store.on("clear", this.refresh, this);
16384             store.on("beforeload", this.onBeforeLoad, this);
16385             store.on("load", this.onLoad, this);
16386             store.on("loadexception", this.onLoad, this);
16387         }
16388         
16389         if(store){
16390             this.refresh();
16391         }
16392     },
16393     /**
16394      * onbeforeLoad - masks the loading area.
16395      *
16396      */
16397     onBeforeLoad : function(store,opts)
16398     {
16399          //Roo.log('onBeforeLoad');   
16400         if (!opts.add) {
16401             this.el.update("");
16402         }
16403         this.el.mask(this.mask ? this.mask : "Loading" ); 
16404     },
16405     onLoad : function ()
16406     {
16407         this.el.unmask();
16408     },
16409     
16410
16411     /**
16412      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16413      * @param {HTMLElement} node
16414      * @return {HTMLElement} The template node
16415      */
16416     findItemFromChild : function(node){
16417         var el = this.dataName  ?
16418             this.el.child('.roo-tpl-' + this.dataName,true) :
16419             this.el.dom; 
16420         
16421         if(!node || node.parentNode == el){
16422                     return node;
16423             }
16424             var p = node.parentNode;
16425             while(p && p != el){
16426             if(p.parentNode == el){
16427                 return p;
16428             }
16429             p = p.parentNode;
16430         }
16431             return null;
16432     },
16433
16434     /** @ignore */
16435     onClick : function(e){
16436         var item = this.findItemFromChild(e.getTarget());
16437         if(item){
16438             var index = this.indexOf(item);
16439             if(this.onItemClick(item, index, e) !== false){
16440                 this.fireEvent("click", this, index, item, e);
16441             }
16442         }else{
16443             this.clearSelections();
16444         }
16445     },
16446
16447     /** @ignore */
16448     onContextMenu : function(e){
16449         var item = this.findItemFromChild(e.getTarget());
16450         if(item){
16451             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16452         }
16453     },
16454
16455     /** @ignore */
16456     onDblClick : function(e){
16457         var item = this.findItemFromChild(e.getTarget());
16458         if(item){
16459             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16460         }
16461     },
16462
16463     onItemClick : function(item, index, e)
16464     {
16465         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16466             return false;
16467         }
16468         if (this.toggleSelect) {
16469             var m = this.isSelected(item) ? 'unselect' : 'select';
16470             //Roo.log(m);
16471             var _t = this;
16472             _t[m](item, true, false);
16473             return true;
16474         }
16475         if(this.multiSelect || this.singleSelect){
16476             if(this.multiSelect && e.shiftKey && this.lastSelection){
16477                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16478             }else{
16479                 this.select(item, this.multiSelect && e.ctrlKey);
16480                 this.lastSelection = item;
16481             }
16482             
16483             if(!this.tickable){
16484                 e.preventDefault();
16485             }
16486             
16487         }
16488         return true;
16489     },
16490
16491     /**
16492      * Get the number of selected nodes.
16493      * @return {Number}
16494      */
16495     getSelectionCount : function(){
16496         return this.selections.length;
16497     },
16498
16499     /**
16500      * Get the currently selected nodes.
16501      * @return {Array} An array of HTMLElements
16502      */
16503     getSelectedNodes : function(){
16504         return this.selections;
16505     },
16506
16507     /**
16508      * Get the indexes of the selected nodes.
16509      * @return {Array}
16510      */
16511     getSelectedIndexes : function(){
16512         var indexes = [], s = this.selections;
16513         for(var i = 0, len = s.length; i < len; i++){
16514             indexes.push(s[i].nodeIndex);
16515         }
16516         return indexes;
16517     },
16518
16519     /**
16520      * Clear all selections
16521      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16522      */
16523     clearSelections : function(suppressEvent){
16524         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16525             this.cmp.elements = this.selections;
16526             this.cmp.removeClass(this.selectedClass);
16527             this.selections = [];
16528             if(!suppressEvent){
16529                 this.fireEvent("selectionchange", this, this.selections);
16530             }
16531         }
16532     },
16533
16534     /**
16535      * Returns true if the passed node is selected
16536      * @param {HTMLElement/Number} node The node or node index
16537      * @return {Boolean}
16538      */
16539     isSelected : function(node){
16540         var s = this.selections;
16541         if(s.length < 1){
16542             return false;
16543         }
16544         node = this.getNode(node);
16545         return s.indexOf(node) !== -1;
16546     },
16547
16548     /**
16549      * Selects nodes.
16550      * @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
16551      * @param {Boolean} keepExisting (optional) true to keep existing selections
16552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16553      */
16554     select : function(nodeInfo, keepExisting, suppressEvent){
16555         if(nodeInfo instanceof Array){
16556             if(!keepExisting){
16557                 this.clearSelections(true);
16558             }
16559             for(var i = 0, len = nodeInfo.length; i < len; i++){
16560                 this.select(nodeInfo[i], true, true);
16561             }
16562             return;
16563         } 
16564         var node = this.getNode(nodeInfo);
16565         if(!node || this.isSelected(node)){
16566             return; // already selected.
16567         }
16568         if(!keepExisting){
16569             this.clearSelections(true);
16570         }
16571         
16572         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16573             Roo.fly(node).addClass(this.selectedClass);
16574             this.selections.push(node);
16575             if(!suppressEvent){
16576                 this.fireEvent("selectionchange", this, this.selections);
16577             }
16578         }
16579         
16580         
16581     },
16582       /**
16583      * Unselects nodes.
16584      * @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
16585      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16586      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16587      */
16588     unselect : function(nodeInfo, keepExisting, suppressEvent)
16589     {
16590         if(nodeInfo instanceof Array){
16591             Roo.each(this.selections, function(s) {
16592                 this.unselect(s, nodeInfo);
16593             }, this);
16594             return;
16595         }
16596         var node = this.getNode(nodeInfo);
16597         if(!node || !this.isSelected(node)){
16598             //Roo.log("not selected");
16599             return; // not selected.
16600         }
16601         // fireevent???
16602         var ns = [];
16603         Roo.each(this.selections, function(s) {
16604             if (s == node ) {
16605                 Roo.fly(node).removeClass(this.selectedClass);
16606
16607                 return;
16608             }
16609             ns.push(s);
16610         },this);
16611         
16612         this.selections= ns;
16613         this.fireEvent("selectionchange", this, this.selections);
16614     },
16615
16616     /**
16617      * Gets a template node.
16618      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16619      * @return {HTMLElement} The node or null if it wasn't found
16620      */
16621     getNode : function(nodeInfo){
16622         if(typeof nodeInfo == "string"){
16623             return document.getElementById(nodeInfo);
16624         }else if(typeof nodeInfo == "number"){
16625             return this.nodes[nodeInfo];
16626         }
16627         return nodeInfo;
16628     },
16629
16630     /**
16631      * Gets a range template nodes.
16632      * @param {Number} startIndex
16633      * @param {Number} endIndex
16634      * @return {Array} An array of nodes
16635      */
16636     getNodes : function(start, end){
16637         var ns = this.nodes;
16638         start = start || 0;
16639         end = typeof end == "undefined" ? ns.length - 1 : end;
16640         var nodes = [];
16641         if(start <= end){
16642             for(var i = start; i <= end; i++){
16643                 nodes.push(ns[i]);
16644             }
16645         } else{
16646             for(var i = start; i >= end; i--){
16647                 nodes.push(ns[i]);
16648             }
16649         }
16650         return nodes;
16651     },
16652
16653     /**
16654      * Finds the index of the passed node
16655      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16656      * @return {Number} The index of the node or -1
16657      */
16658     indexOf : function(node){
16659         node = this.getNode(node);
16660         if(typeof node.nodeIndex == "number"){
16661             return node.nodeIndex;
16662         }
16663         var ns = this.nodes;
16664         for(var i = 0, len = ns.length; i < len; i++){
16665             if(ns[i] == node){
16666                 return i;
16667             }
16668         }
16669         return -1;
16670     }
16671 });
16672 /*
16673  * - LGPL
16674  *
16675  * based on jquery fullcalendar
16676  * 
16677  */
16678
16679 Roo.bootstrap = Roo.bootstrap || {};
16680 /**
16681  * @class Roo.bootstrap.Calendar
16682  * @extends Roo.bootstrap.Component
16683  * Bootstrap Calendar class
16684  * @cfg {Boolean} loadMask (true|false) default false
16685  * @cfg {Object} header generate the user specific header of the calendar, default false
16686
16687  * @constructor
16688  * Create a new Container
16689  * @param {Object} config The config object
16690  */
16691
16692
16693
16694 Roo.bootstrap.Calendar = function(config){
16695     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16696      this.addEvents({
16697         /**
16698              * @event select
16699              * Fires when a date is selected
16700              * @param {DatePicker} this
16701              * @param {Date} date The selected date
16702              */
16703         'select': true,
16704         /**
16705              * @event monthchange
16706              * Fires when the displayed month changes 
16707              * @param {DatePicker} this
16708              * @param {Date} date The selected month
16709              */
16710         'monthchange': true,
16711         /**
16712              * @event evententer
16713              * Fires when mouse over an event
16714              * @param {Calendar} this
16715              * @param {event} Event
16716              */
16717         'evententer': true,
16718         /**
16719              * @event eventleave
16720              * Fires when the mouse leaves an
16721              * @param {Calendar} this
16722              * @param {event}
16723              */
16724         'eventleave': true,
16725         /**
16726              * @event eventclick
16727              * Fires when the mouse click an
16728              * @param {Calendar} this
16729              * @param {event}
16730              */
16731         'eventclick': true
16732         
16733     });
16734
16735 };
16736
16737 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16738     
16739      /**
16740      * @cfg {Number} startDay
16741      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16742      */
16743     startDay : 0,
16744     
16745     loadMask : false,
16746     
16747     header : false,
16748       
16749     getAutoCreate : function(){
16750         
16751         
16752         var fc_button = function(name, corner, style, content ) {
16753             return Roo.apply({},{
16754                 tag : 'span',
16755                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16756                          (corner.length ?
16757                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16758                             ''
16759                         ),
16760                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16761                 unselectable: 'on'
16762             });
16763         };
16764         
16765         var header = {};
16766         
16767         if(!this.header){
16768             header = {
16769                 tag : 'table',
16770                 cls : 'fc-header',
16771                 style : 'width:100%',
16772                 cn : [
16773                     {
16774                         tag: 'tr',
16775                         cn : [
16776                             {
16777                                 tag : 'td',
16778                                 cls : 'fc-header-left',
16779                                 cn : [
16780                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16781                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16782                                     { tag: 'span', cls: 'fc-header-space' },
16783                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16784
16785
16786                                 ]
16787                             },
16788
16789                             {
16790                                 tag : 'td',
16791                                 cls : 'fc-header-center',
16792                                 cn : [
16793                                     {
16794                                         tag: 'span',
16795                                         cls: 'fc-header-title',
16796                                         cn : {
16797                                             tag: 'H2',
16798                                             html : 'month / year'
16799                                         }
16800                                     }
16801
16802                                 ]
16803                             },
16804                             {
16805                                 tag : 'td',
16806                                 cls : 'fc-header-right',
16807                                 cn : [
16808                               /*      fc_button('month', 'left', '', 'month' ),
16809                                     fc_button('week', '', '', 'week' ),
16810                                     fc_button('day', 'right', '', 'day' )
16811                                 */    
16812
16813                                 ]
16814                             }
16815
16816                         ]
16817                     }
16818                 ]
16819             };
16820         }
16821         
16822         header = this.header;
16823         
16824        
16825         var cal_heads = function() {
16826             var ret = [];
16827             // fixme - handle this.
16828             
16829             for (var i =0; i < Date.dayNames.length; i++) {
16830                 var d = Date.dayNames[i];
16831                 ret.push({
16832                     tag: 'th',
16833                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16834                     html : d.substring(0,3)
16835                 });
16836                 
16837             }
16838             ret[0].cls += ' fc-first';
16839             ret[6].cls += ' fc-last';
16840             return ret;
16841         };
16842         var cal_cell = function(n) {
16843             return  {
16844                 tag: 'td',
16845                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16846                 cn : [
16847                     {
16848                         cn : [
16849                             {
16850                                 cls: 'fc-day-number',
16851                                 html: 'D'
16852                             },
16853                             {
16854                                 cls: 'fc-day-content',
16855                              
16856                                 cn : [
16857                                      {
16858                                         style: 'position: relative;' // height: 17px;
16859                                     }
16860                                 ]
16861                             }
16862                             
16863                             
16864                         ]
16865                     }
16866                 ]
16867                 
16868             }
16869         };
16870         var cal_rows = function() {
16871             
16872             var ret = [];
16873             for (var r = 0; r < 6; r++) {
16874                 var row= {
16875                     tag : 'tr',
16876                     cls : 'fc-week',
16877                     cn : []
16878                 };
16879                 
16880                 for (var i =0; i < Date.dayNames.length; i++) {
16881                     var d = Date.dayNames[i];
16882                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16883
16884                 }
16885                 row.cn[0].cls+=' fc-first';
16886                 row.cn[0].cn[0].style = 'min-height:90px';
16887                 row.cn[6].cls+=' fc-last';
16888                 ret.push(row);
16889                 
16890             }
16891             ret[0].cls += ' fc-first';
16892             ret[4].cls += ' fc-prev-last';
16893             ret[5].cls += ' fc-last';
16894             return ret;
16895             
16896         };
16897         
16898         var cal_table = {
16899             tag: 'table',
16900             cls: 'fc-border-separate',
16901             style : 'width:100%',
16902             cellspacing  : 0,
16903             cn : [
16904                 { 
16905                     tag: 'thead',
16906                     cn : [
16907                         { 
16908                             tag: 'tr',
16909                             cls : 'fc-first fc-last',
16910                             cn : cal_heads()
16911                         }
16912                     ]
16913                 },
16914                 { 
16915                     tag: 'tbody',
16916                     cn : cal_rows()
16917                 }
16918                   
16919             ]
16920         };
16921          
16922          var cfg = {
16923             cls : 'fc fc-ltr',
16924             cn : [
16925                 header,
16926                 {
16927                     cls : 'fc-content',
16928                     style : "position: relative;",
16929                     cn : [
16930                         {
16931                             cls : 'fc-view fc-view-month fc-grid',
16932                             style : 'position: relative',
16933                             unselectable : 'on',
16934                             cn : [
16935                                 {
16936                                     cls : 'fc-event-container',
16937                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16938                                 },
16939                                 cal_table
16940                             ]
16941                         }
16942                     ]
16943     
16944                 }
16945            ] 
16946             
16947         };
16948         
16949          
16950         
16951         return cfg;
16952     },
16953     
16954     
16955     initEvents : function()
16956     {
16957         if(!this.store){
16958             throw "can not find store for calendar";
16959         }
16960         
16961         var mark = {
16962             tag: "div",
16963             cls:"x-dlg-mask",
16964             style: "text-align:center",
16965             cn: [
16966                 {
16967                     tag: "div",
16968                     style: "background-color:white;width:50%;margin:250 auto",
16969                     cn: [
16970                         {
16971                             tag: "img",
16972                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16973                         },
16974                         {
16975                             tag: "span",
16976                             html: "Loading"
16977                         }
16978                         
16979                     ]
16980                 }
16981             ]
16982         };
16983         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16984         
16985         var size = this.el.select('.fc-content', true).first().getSize();
16986         this.maskEl.setSize(size.width, size.height);
16987         this.maskEl.enableDisplayMode("block");
16988         if(!this.loadMask){
16989             this.maskEl.hide();
16990         }
16991         
16992         this.store = Roo.factory(this.store, Roo.data);
16993         this.store.on('load', this.onLoad, this);
16994         this.store.on('beforeload', this.onBeforeLoad, this);
16995         
16996         this.resize();
16997         
16998         this.cells = this.el.select('.fc-day',true);
16999         //Roo.log(this.cells);
17000         this.textNodes = this.el.query('.fc-day-number');
17001         this.cells.addClassOnOver('fc-state-hover');
17002         
17003         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17004         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17005         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17006         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17007         
17008         this.on('monthchange', this.onMonthChange, this);
17009         
17010         this.update(new Date().clearTime());
17011     },
17012     
17013     resize : function() {
17014         var sz  = this.el.getSize();
17015         
17016         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17017         this.el.select('.fc-day-content div',true).setHeight(34);
17018     },
17019     
17020     
17021     // private
17022     showPrevMonth : function(e){
17023         this.update(this.activeDate.add("mo", -1));
17024     },
17025     showToday : function(e){
17026         this.update(new Date().clearTime());
17027     },
17028     // private
17029     showNextMonth : function(e){
17030         this.update(this.activeDate.add("mo", 1));
17031     },
17032
17033     // private
17034     showPrevYear : function(){
17035         this.update(this.activeDate.add("y", -1));
17036     },
17037
17038     // private
17039     showNextYear : function(){
17040         this.update(this.activeDate.add("y", 1));
17041     },
17042
17043     
17044    // private
17045     update : function(date)
17046     {
17047         var vd = this.activeDate;
17048         this.activeDate = date;
17049 //        if(vd && this.el){
17050 //            var t = date.getTime();
17051 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17052 //                Roo.log('using add remove');
17053 //                
17054 //                this.fireEvent('monthchange', this, date);
17055 //                
17056 //                this.cells.removeClass("fc-state-highlight");
17057 //                this.cells.each(function(c){
17058 //                   if(c.dateValue == t){
17059 //                       c.addClass("fc-state-highlight");
17060 //                       setTimeout(function(){
17061 //                            try{c.dom.firstChild.focus();}catch(e){}
17062 //                       }, 50);
17063 //                       return false;
17064 //                   }
17065 //                   return true;
17066 //                });
17067 //                return;
17068 //            }
17069 //        }
17070         
17071         var days = date.getDaysInMonth();
17072         
17073         var firstOfMonth = date.getFirstDateOfMonth();
17074         var startingPos = firstOfMonth.getDay()-this.startDay;
17075         
17076         if(startingPos < this.startDay){
17077             startingPos += 7;
17078         }
17079         
17080         var pm = date.add(Date.MONTH, -1);
17081         var prevStart = pm.getDaysInMonth()-startingPos;
17082 //        
17083         this.cells = this.el.select('.fc-day',true);
17084         this.textNodes = this.el.query('.fc-day-number');
17085         this.cells.addClassOnOver('fc-state-hover');
17086         
17087         var cells = this.cells.elements;
17088         var textEls = this.textNodes;
17089         
17090         Roo.each(cells, function(cell){
17091             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17092         });
17093         
17094         days += startingPos;
17095
17096         // convert everything to numbers so it's fast
17097         var day = 86400000;
17098         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17099         //Roo.log(d);
17100         //Roo.log(pm);
17101         //Roo.log(prevStart);
17102         
17103         var today = new Date().clearTime().getTime();
17104         var sel = date.clearTime().getTime();
17105         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17106         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17107         var ddMatch = this.disabledDatesRE;
17108         var ddText = this.disabledDatesText;
17109         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17110         var ddaysText = this.disabledDaysText;
17111         var format = this.format;
17112         
17113         var setCellClass = function(cal, cell){
17114             cell.row = 0;
17115             cell.events = [];
17116             cell.more = [];
17117             //Roo.log('set Cell Class');
17118             cell.title = "";
17119             var t = d.getTime();
17120             
17121             //Roo.log(d);
17122             
17123             cell.dateValue = t;
17124             if(t == today){
17125                 cell.className += " fc-today";
17126                 cell.className += " fc-state-highlight";
17127                 cell.title = cal.todayText;
17128             }
17129             if(t == sel){
17130                 // disable highlight in other month..
17131                 //cell.className += " fc-state-highlight";
17132                 
17133             }
17134             // disabling
17135             if(t < min) {
17136                 cell.className = " fc-state-disabled";
17137                 cell.title = cal.minText;
17138                 return;
17139             }
17140             if(t > max) {
17141                 cell.className = " fc-state-disabled";
17142                 cell.title = cal.maxText;
17143                 return;
17144             }
17145             if(ddays){
17146                 if(ddays.indexOf(d.getDay()) != -1){
17147                     cell.title = ddaysText;
17148                     cell.className = " fc-state-disabled";
17149                 }
17150             }
17151             if(ddMatch && format){
17152                 var fvalue = d.dateFormat(format);
17153                 if(ddMatch.test(fvalue)){
17154                     cell.title = ddText.replace("%0", fvalue);
17155                     cell.className = " fc-state-disabled";
17156                 }
17157             }
17158             
17159             if (!cell.initialClassName) {
17160                 cell.initialClassName = cell.dom.className;
17161             }
17162             
17163             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17164         };
17165
17166         var i = 0;
17167         
17168         for(; i < startingPos; i++) {
17169             textEls[i].innerHTML = (++prevStart);
17170             d.setDate(d.getDate()+1);
17171             
17172             cells[i].className = "fc-past fc-other-month";
17173             setCellClass(this, cells[i]);
17174         }
17175         
17176         var intDay = 0;
17177         
17178         for(; i < days; i++){
17179             intDay = i - startingPos + 1;
17180             textEls[i].innerHTML = (intDay);
17181             d.setDate(d.getDate()+1);
17182             
17183             cells[i].className = ''; // "x-date-active";
17184             setCellClass(this, cells[i]);
17185         }
17186         var extraDays = 0;
17187         
17188         for(; i < 42; i++) {
17189             textEls[i].innerHTML = (++extraDays);
17190             d.setDate(d.getDate()+1);
17191             
17192             cells[i].className = "fc-future fc-other-month";
17193             setCellClass(this, cells[i]);
17194         }
17195         
17196         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17197         
17198         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17199         
17200         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17201         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17202         
17203         if(totalRows != 6){
17204             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17205             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17206         }
17207         
17208         this.fireEvent('monthchange', this, date);
17209         
17210         
17211         /*
17212         if(!this.internalRender){
17213             var main = this.el.dom.firstChild;
17214             var w = main.offsetWidth;
17215             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17216             Roo.fly(main).setWidth(w);
17217             this.internalRender = true;
17218             // opera does not respect the auto grow header center column
17219             // then, after it gets a width opera refuses to recalculate
17220             // without a second pass
17221             if(Roo.isOpera && !this.secondPass){
17222                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17223                 this.secondPass = true;
17224                 this.update.defer(10, this, [date]);
17225             }
17226         }
17227         */
17228         
17229     },
17230     
17231     findCell : function(dt) {
17232         dt = dt.clearTime().getTime();
17233         var ret = false;
17234         this.cells.each(function(c){
17235             //Roo.log("check " +c.dateValue + '?=' + dt);
17236             if(c.dateValue == dt){
17237                 ret = c;
17238                 return false;
17239             }
17240             return true;
17241         });
17242         
17243         return ret;
17244     },
17245     
17246     findCells : function(ev) {
17247         var s = ev.start.clone().clearTime().getTime();
17248        // Roo.log(s);
17249         var e= ev.end.clone().clearTime().getTime();
17250        // Roo.log(e);
17251         var ret = [];
17252         this.cells.each(function(c){
17253              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17254             
17255             if(c.dateValue > e){
17256                 return ;
17257             }
17258             if(c.dateValue < s){
17259                 return ;
17260             }
17261             ret.push(c);
17262         });
17263         
17264         return ret;    
17265     },
17266     
17267 //    findBestRow: function(cells)
17268 //    {
17269 //        var ret = 0;
17270 //        
17271 //        for (var i =0 ; i < cells.length;i++) {
17272 //            ret  = Math.max(cells[i].rows || 0,ret);
17273 //        }
17274 //        return ret;
17275 //        
17276 //    },
17277     
17278     
17279     addItem : function(ev)
17280     {
17281         // look for vertical location slot in
17282         var cells = this.findCells(ev);
17283         
17284 //        ev.row = this.findBestRow(cells);
17285         
17286         // work out the location.
17287         
17288         var crow = false;
17289         var rows = [];
17290         for(var i =0; i < cells.length; i++) {
17291             
17292             cells[i].row = cells[0].row;
17293             
17294             if(i == 0){
17295                 cells[i].row = cells[i].row + 1;
17296             }
17297             
17298             if (!crow) {
17299                 crow = {
17300                     start : cells[i],
17301                     end :  cells[i]
17302                 };
17303                 continue;
17304             }
17305             if (crow.start.getY() == cells[i].getY()) {
17306                 // on same row.
17307                 crow.end = cells[i];
17308                 continue;
17309             }
17310             // different row.
17311             rows.push(crow);
17312             crow = {
17313                 start: cells[i],
17314                 end : cells[i]
17315             };
17316             
17317         }
17318         
17319         rows.push(crow);
17320         ev.els = [];
17321         ev.rows = rows;
17322         ev.cells = cells;
17323         
17324         cells[0].events.push(ev);
17325         
17326         this.calevents.push(ev);
17327     },
17328     
17329     clearEvents: function() {
17330         
17331         if(!this.calevents){
17332             return;
17333         }
17334         
17335         Roo.each(this.cells.elements, function(c){
17336             c.row = 0;
17337             c.events = [];
17338             c.more = [];
17339         });
17340         
17341         Roo.each(this.calevents, function(e) {
17342             Roo.each(e.els, function(el) {
17343                 el.un('mouseenter' ,this.onEventEnter, this);
17344                 el.un('mouseleave' ,this.onEventLeave, this);
17345                 el.remove();
17346             },this);
17347         },this);
17348         
17349         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17350             e.remove();
17351         });
17352         
17353     },
17354     
17355     renderEvents: function()
17356     {   
17357         var _this = this;
17358         
17359         this.cells.each(function(c) {
17360             
17361             if(c.row < 5){
17362                 return;
17363             }
17364             
17365             var ev = c.events;
17366             
17367             var r = 4;
17368             if(c.row != c.events.length){
17369                 r = 4 - (4 - (c.row - c.events.length));
17370             }
17371             
17372             c.events = ev.slice(0, r);
17373             c.more = ev.slice(r);
17374             
17375             if(c.more.length && c.more.length == 1){
17376                 c.events.push(c.more.pop());
17377             }
17378             
17379             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17380             
17381         });
17382             
17383         this.cells.each(function(c) {
17384             
17385             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17386             
17387             
17388             for (var e = 0; e < c.events.length; e++){
17389                 var ev = c.events[e];
17390                 var rows = ev.rows;
17391                 
17392                 for(var i = 0; i < rows.length; i++) {
17393                 
17394                     // how many rows should it span..
17395
17396                     var  cfg = {
17397                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17398                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17399
17400                         unselectable : "on",
17401                         cn : [
17402                             {
17403                                 cls: 'fc-event-inner',
17404                                 cn : [
17405     //                                {
17406     //                                  tag:'span',
17407     //                                  cls: 'fc-event-time',
17408     //                                  html : cells.length > 1 ? '' : ev.time
17409     //                                },
17410                                     {
17411                                       tag:'span',
17412                                       cls: 'fc-event-title',
17413                                       html : String.format('{0}', ev.title)
17414                                     }
17415
17416
17417                                 ]
17418                             },
17419                             {
17420                                 cls: 'ui-resizable-handle ui-resizable-e',
17421                                 html : '&nbsp;&nbsp;&nbsp'
17422                             }
17423
17424                         ]
17425                     };
17426
17427                     if (i == 0) {
17428                         cfg.cls += ' fc-event-start';
17429                     }
17430                     if ((i+1) == rows.length) {
17431                         cfg.cls += ' fc-event-end';
17432                     }
17433
17434                     var ctr = _this.el.select('.fc-event-container',true).first();
17435                     var cg = ctr.createChild(cfg);
17436
17437                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17438                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17439
17440                     var r = (c.more.length) ? 1 : 0;
17441                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17442                     cg.setWidth(ebox.right - sbox.x -2);
17443
17444                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17445                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17446                     cg.on('click', _this.onEventClick, _this, ev);
17447
17448                     ev.els.push(cg);
17449                     
17450                 }
17451                 
17452             }
17453             
17454             
17455             if(c.more.length){
17456                 var  cfg = {
17457                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17458                     style : 'position: absolute',
17459                     unselectable : "on",
17460                     cn : [
17461                         {
17462                             cls: 'fc-event-inner',
17463                             cn : [
17464                                 {
17465                                   tag:'span',
17466                                   cls: 'fc-event-title',
17467                                   html : 'More'
17468                                 }
17469
17470
17471                             ]
17472                         },
17473                         {
17474                             cls: 'ui-resizable-handle ui-resizable-e',
17475                             html : '&nbsp;&nbsp;&nbsp'
17476                         }
17477
17478                     ]
17479                 };
17480
17481                 var ctr = _this.el.select('.fc-event-container',true).first();
17482                 var cg = ctr.createChild(cfg);
17483
17484                 var sbox = c.select('.fc-day-content',true).first().getBox();
17485                 var ebox = c.select('.fc-day-content',true).first().getBox();
17486                 //Roo.log(cg);
17487                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17488                 cg.setWidth(ebox.right - sbox.x -2);
17489
17490                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17491                 
17492             }
17493             
17494         });
17495         
17496         
17497         
17498     },
17499     
17500     onEventEnter: function (e, el,event,d) {
17501         this.fireEvent('evententer', this, el, event);
17502     },
17503     
17504     onEventLeave: function (e, el,event,d) {
17505         this.fireEvent('eventleave', this, el, event);
17506     },
17507     
17508     onEventClick: function (e, el,event,d) {
17509         this.fireEvent('eventclick', this, el, event);
17510     },
17511     
17512     onMonthChange: function () {
17513         this.store.load();
17514     },
17515     
17516     onMoreEventClick: function(e, el, more)
17517     {
17518         var _this = this;
17519         
17520         this.calpopover.placement = 'right';
17521         this.calpopover.setTitle('More');
17522         
17523         this.calpopover.setContent('');
17524         
17525         var ctr = this.calpopover.el.select('.popover-content', true).first();
17526         
17527         Roo.each(more, function(m){
17528             var cfg = {
17529                 cls : 'fc-event-hori fc-event-draggable',
17530                 html : m.title
17531             };
17532             var cg = ctr.createChild(cfg);
17533             
17534             cg.on('click', _this.onEventClick, _this, m);
17535         });
17536         
17537         this.calpopover.show(el);
17538         
17539         
17540     },
17541     
17542     onLoad: function () 
17543     {   
17544         this.calevents = [];
17545         var cal = this;
17546         
17547         if(this.store.getCount() > 0){
17548             this.store.data.each(function(d){
17549                cal.addItem({
17550                     id : d.data.id,
17551                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17552                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17553                     time : d.data.start_time,
17554                     title : d.data.title,
17555                     description : d.data.description,
17556                     venue : d.data.venue
17557                 });
17558             });
17559         }
17560         
17561         this.renderEvents();
17562         
17563         if(this.calevents.length && this.loadMask){
17564             this.maskEl.hide();
17565         }
17566     },
17567     
17568     onBeforeLoad: function()
17569     {
17570         this.clearEvents();
17571         if(this.loadMask){
17572             this.maskEl.show();
17573         }
17574     }
17575 });
17576
17577  
17578  /*
17579  * - LGPL
17580  *
17581  * element
17582  * 
17583  */
17584
17585 /**
17586  * @class Roo.bootstrap.Popover
17587  * @extends Roo.bootstrap.Component
17588  * Bootstrap Popover class
17589  * @cfg {String} html contents of the popover   (or false to use children..)
17590  * @cfg {String} title of popover (or false to hide)
17591  * @cfg {String} placement how it is placed
17592  * @cfg {String} trigger click || hover (or false to trigger manually)
17593  * @cfg {String} over what (parent or false to trigger manually.)
17594  * @cfg {Number} delay - delay before showing
17595  
17596  * @constructor
17597  * Create a new Popover
17598  * @param {Object} config The config object
17599  */
17600
17601 Roo.bootstrap.Popover = function(config){
17602     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17603     
17604     this.addEvents({
17605         // raw events
17606          /**
17607          * @event show
17608          * After the popover show
17609          * 
17610          * @param {Roo.bootstrap.Popover} this
17611          */
17612         "show" : true,
17613         /**
17614          * @event hide
17615          * After the popover hide
17616          * 
17617          * @param {Roo.bootstrap.Popover} this
17618          */
17619         "hide" : true
17620     });
17621 };
17622
17623 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17624     
17625     title: 'Fill in a title',
17626     html: false,
17627     
17628     placement : 'right',
17629     trigger : 'hover', // hover
17630     
17631     delay : 0,
17632     
17633     over: 'parent',
17634     
17635     can_build_overlaid : false,
17636     
17637     getChildContainer : function()
17638     {
17639         return this.el.select('.popover-content',true).first();
17640     },
17641     
17642     getAutoCreate : function(){
17643          
17644         var cfg = {
17645            cls : 'popover roo-dynamic',
17646            style: 'display:block',
17647            cn : [
17648                 {
17649                     cls : 'arrow'
17650                 },
17651                 {
17652                     cls : 'popover-inner',
17653                     cn : [
17654                         {
17655                             tag: 'h3',
17656                             cls: 'popover-title popover-header',
17657                             html : this.title
17658                         },
17659                         {
17660                             cls : 'popover-content popover-body',
17661                             html : this.html
17662                         }
17663                     ]
17664                     
17665                 }
17666            ]
17667         };
17668         
17669         return cfg;
17670     },
17671     setTitle: function(str)
17672     {
17673         this.title = str;
17674         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17675     },
17676     setContent: function(str)
17677     {
17678         this.html = str;
17679         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17680     },
17681     // as it get's added to the bottom of the page.
17682     onRender : function(ct, position)
17683     {
17684         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17685         if(!this.el){
17686             var cfg = Roo.apply({},  this.getAutoCreate());
17687             cfg.id = Roo.id();
17688             
17689             if (this.cls) {
17690                 cfg.cls += ' ' + this.cls;
17691             }
17692             if (this.style) {
17693                 cfg.style = this.style;
17694             }
17695             //Roo.log("adding to ");
17696             this.el = Roo.get(document.body).createChild(cfg, position);
17697 //            Roo.log(this.el);
17698         }
17699         this.initEvents();
17700     },
17701     
17702     initEvents : function()
17703     {
17704         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17705         this.el.enableDisplayMode('block');
17706         this.el.hide();
17707         if (this.over === false) {
17708             return; 
17709         }
17710         if (this.triggers === false) {
17711             return;
17712         }
17713         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17714         var triggers = this.trigger ? this.trigger.split(' ') : [];
17715         Roo.each(triggers, function(trigger) {
17716         
17717             if (trigger == 'click') {
17718                 on_el.on('click', this.toggle, this);
17719             } else if (trigger != 'manual') {
17720                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17721                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17722       
17723                 on_el.on(eventIn  ,this.enter, this);
17724                 on_el.on(eventOut, this.leave, this);
17725             }
17726         }, this);
17727         
17728     },
17729     
17730     
17731     // private
17732     timeout : null,
17733     hoverState : null,
17734     
17735     toggle : function () {
17736         this.hoverState == 'in' ? this.leave() : this.enter();
17737     },
17738     
17739     enter : function () {
17740         
17741         clearTimeout(this.timeout);
17742     
17743         this.hoverState = 'in';
17744     
17745         if (!this.delay || !this.delay.show) {
17746             this.show();
17747             return;
17748         }
17749         var _t = this;
17750         this.timeout = setTimeout(function () {
17751             if (_t.hoverState == 'in') {
17752                 _t.show();
17753             }
17754         }, this.delay.show)
17755     },
17756     
17757     leave : function() {
17758         clearTimeout(this.timeout);
17759     
17760         this.hoverState = 'out';
17761     
17762         if (!this.delay || !this.delay.hide) {
17763             this.hide();
17764             return;
17765         }
17766         var _t = this;
17767         this.timeout = setTimeout(function () {
17768             if (_t.hoverState == 'out') {
17769                 _t.hide();
17770             }
17771         }, this.delay.hide)
17772     },
17773     
17774     show : function (on_el)
17775     {
17776         if (!on_el) {
17777             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17778         }
17779         
17780         // set content.
17781         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17782         if (this.html !== false) {
17783             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17784         }
17785         this.el.removeClass([
17786             'fade','top','bottom', 'left', 'right','in',
17787             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17788         ]);
17789         if (!this.title.length) {
17790             this.el.select('.popover-title',true).hide();
17791         }
17792         
17793         var placement = typeof this.placement == 'function' ?
17794             this.placement.call(this, this.el, on_el) :
17795             this.placement;
17796             
17797         var autoToken = /\s?auto?\s?/i;
17798         var autoPlace = autoToken.test(placement);
17799         if (autoPlace) {
17800             placement = placement.replace(autoToken, '') || 'top';
17801         }
17802         
17803         //this.el.detach()
17804         //this.el.setXY([0,0]);
17805         this.el.show();
17806         this.el.dom.style.display='block';
17807         this.el.addClass(placement);
17808         
17809         //this.el.appendTo(on_el);
17810         
17811         var p = this.getPosition();
17812         var box = this.el.getBox();
17813         
17814         if (autoPlace) {
17815             // fixme..
17816         }
17817         var align = Roo.bootstrap.Popover.alignment[placement];
17818         
17819 //        Roo.log(align);
17820         this.el.alignTo(on_el, align[0],align[1]);
17821         //var arrow = this.el.select('.arrow',true).first();
17822         //arrow.set(align[2], 
17823         
17824         this.el.addClass('in');
17825         
17826         
17827         if (this.el.hasClass('fade')) {
17828             // fade it?
17829         }
17830         
17831         this.hoverState = 'in';
17832         
17833         this.fireEvent('show', this);
17834         
17835     },
17836     hide : function()
17837     {
17838         this.el.setXY([0,0]);
17839         this.el.removeClass('in');
17840         this.el.hide();
17841         this.hoverState = null;
17842         
17843         this.fireEvent('hide', this);
17844     }
17845     
17846 });
17847
17848 Roo.bootstrap.Popover.alignment = {
17849     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17850     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17851     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17852     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17853 };
17854
17855  /*
17856  * - LGPL
17857  *
17858  * Progress
17859  * 
17860  */
17861
17862 /**
17863  * @class Roo.bootstrap.Progress
17864  * @extends Roo.bootstrap.Component
17865  * Bootstrap Progress class
17866  * @cfg {Boolean} striped striped of the progress bar
17867  * @cfg {Boolean} active animated of the progress bar
17868  * 
17869  * 
17870  * @constructor
17871  * Create a new Progress
17872  * @param {Object} config The config object
17873  */
17874
17875 Roo.bootstrap.Progress = function(config){
17876     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17877 };
17878
17879 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17880     
17881     striped : false,
17882     active: false,
17883     
17884     getAutoCreate : function(){
17885         var cfg = {
17886             tag: 'div',
17887             cls: 'progress'
17888         };
17889         
17890         
17891         if(this.striped){
17892             cfg.cls += ' progress-striped';
17893         }
17894       
17895         if(this.active){
17896             cfg.cls += ' active';
17897         }
17898         
17899         
17900         return cfg;
17901     }
17902    
17903 });
17904
17905  
17906
17907  /*
17908  * - LGPL
17909  *
17910  * ProgressBar
17911  * 
17912  */
17913
17914 /**
17915  * @class Roo.bootstrap.ProgressBar
17916  * @extends Roo.bootstrap.Component
17917  * Bootstrap ProgressBar class
17918  * @cfg {Number} aria_valuenow aria-value now
17919  * @cfg {Number} aria_valuemin aria-value min
17920  * @cfg {Number} aria_valuemax aria-value max
17921  * @cfg {String} label label for the progress bar
17922  * @cfg {String} panel (success | info | warning | danger )
17923  * @cfg {String} role role of the progress bar
17924  * @cfg {String} sr_only text
17925  * 
17926  * 
17927  * @constructor
17928  * Create a new ProgressBar
17929  * @param {Object} config The config object
17930  */
17931
17932 Roo.bootstrap.ProgressBar = function(config){
17933     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17934 };
17935
17936 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17937     
17938     aria_valuenow : 0,
17939     aria_valuemin : 0,
17940     aria_valuemax : 100,
17941     label : false,
17942     panel : false,
17943     role : false,
17944     sr_only: false,
17945     
17946     getAutoCreate : function()
17947     {
17948         
17949         var cfg = {
17950             tag: 'div',
17951             cls: 'progress-bar',
17952             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17953         };
17954         
17955         if(this.sr_only){
17956             cfg.cn = {
17957                 tag: 'span',
17958                 cls: 'sr-only',
17959                 html: this.sr_only
17960             }
17961         }
17962         
17963         if(this.role){
17964             cfg.role = this.role;
17965         }
17966         
17967         if(this.aria_valuenow){
17968             cfg['aria-valuenow'] = this.aria_valuenow;
17969         }
17970         
17971         if(this.aria_valuemin){
17972             cfg['aria-valuemin'] = this.aria_valuemin;
17973         }
17974         
17975         if(this.aria_valuemax){
17976             cfg['aria-valuemax'] = this.aria_valuemax;
17977         }
17978         
17979         if(this.label && !this.sr_only){
17980             cfg.html = this.label;
17981         }
17982         
17983         if(this.panel){
17984             cfg.cls += ' progress-bar-' + this.panel;
17985         }
17986         
17987         return cfg;
17988     },
17989     
17990     update : function(aria_valuenow)
17991     {
17992         this.aria_valuenow = aria_valuenow;
17993         
17994         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17995     }
17996    
17997 });
17998
17999  
18000
18001  /*
18002  * - LGPL
18003  *
18004  * column
18005  * 
18006  */
18007
18008 /**
18009  * @class Roo.bootstrap.TabGroup
18010  * @extends Roo.bootstrap.Column
18011  * Bootstrap Column class
18012  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18013  * @cfg {Boolean} carousel true to make the group behave like a carousel
18014  * @cfg {Boolean} bullets show bullets for the panels
18015  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18016  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18017  * @cfg {Boolean} showarrow (true|false) show arrow default true
18018  * 
18019  * @constructor
18020  * Create a new TabGroup
18021  * @param {Object} config The config object
18022  */
18023
18024 Roo.bootstrap.TabGroup = function(config){
18025     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18026     if (!this.navId) {
18027         this.navId = Roo.id();
18028     }
18029     this.tabs = [];
18030     Roo.bootstrap.TabGroup.register(this);
18031     
18032 };
18033
18034 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18035     
18036     carousel : false,
18037     transition : false,
18038     bullets : 0,
18039     timer : 0,
18040     autoslide : false,
18041     slideFn : false,
18042     slideOnTouch : false,
18043     showarrow : true,
18044     
18045     getAutoCreate : function()
18046     {
18047         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18048         
18049         cfg.cls += ' tab-content';
18050         
18051         if (this.carousel) {
18052             cfg.cls += ' carousel slide';
18053             
18054             cfg.cn = [{
18055                cls : 'carousel-inner',
18056                cn : []
18057             }];
18058         
18059             if(this.bullets  && !Roo.isTouch){
18060                 
18061                 var bullets = {
18062                     cls : 'carousel-bullets',
18063                     cn : []
18064                 };
18065                
18066                 if(this.bullets_cls){
18067                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18068                 }
18069                 
18070                 bullets.cn.push({
18071                     cls : 'clear'
18072                 });
18073                 
18074                 cfg.cn[0].cn.push(bullets);
18075             }
18076             
18077             if(this.showarrow){
18078                 cfg.cn[0].cn.push({
18079                     tag : 'div',
18080                     class : 'carousel-arrow',
18081                     cn : [
18082                         {
18083                             tag : 'div',
18084                             class : 'carousel-prev',
18085                             cn : [
18086                                 {
18087                                     tag : 'i',
18088                                     class : 'fa fa-chevron-left'
18089                                 }
18090                             ]
18091                         },
18092                         {
18093                             tag : 'div',
18094                             class : 'carousel-next',
18095                             cn : [
18096                                 {
18097                                     tag : 'i',
18098                                     class : 'fa fa-chevron-right'
18099                                 }
18100                             ]
18101                         }
18102                     ]
18103                 });
18104             }
18105             
18106         }
18107         
18108         return cfg;
18109     },
18110     
18111     initEvents:  function()
18112     {
18113 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18114 //            this.el.on("touchstart", this.onTouchStart, this);
18115 //        }
18116         
18117         if(this.autoslide){
18118             var _this = this;
18119             
18120             this.slideFn = window.setInterval(function() {
18121                 _this.showPanelNext();
18122             }, this.timer);
18123         }
18124         
18125         if(this.showarrow){
18126             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18127             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18128         }
18129         
18130         
18131     },
18132     
18133 //    onTouchStart : function(e, el, o)
18134 //    {
18135 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18136 //            return;
18137 //        }
18138 //        
18139 //        this.showPanelNext();
18140 //    },
18141     
18142     
18143     getChildContainer : function()
18144     {
18145         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18146     },
18147     
18148     /**
18149     * register a Navigation item
18150     * @param {Roo.bootstrap.NavItem} the navitem to add
18151     */
18152     register : function(item)
18153     {
18154         this.tabs.push( item);
18155         item.navId = this.navId; // not really needed..
18156         this.addBullet();
18157     
18158     },
18159     
18160     getActivePanel : function()
18161     {
18162         var r = false;
18163         Roo.each(this.tabs, function(t) {
18164             if (t.active) {
18165                 r = t;
18166                 return false;
18167             }
18168             return null;
18169         });
18170         return r;
18171         
18172     },
18173     getPanelByName : function(n)
18174     {
18175         var r = false;
18176         Roo.each(this.tabs, function(t) {
18177             if (t.tabId == n) {
18178                 r = t;
18179                 return false;
18180             }
18181             return null;
18182         });
18183         return r;
18184     },
18185     indexOfPanel : function(p)
18186     {
18187         var r = false;
18188         Roo.each(this.tabs, function(t,i) {
18189             if (t.tabId == p.tabId) {
18190                 r = i;
18191                 return false;
18192             }
18193             return null;
18194         });
18195         return r;
18196     },
18197     /**
18198      * show a specific panel
18199      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18200      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18201      */
18202     showPanel : function (pan)
18203     {
18204         if(this.transition || typeof(pan) == 'undefined'){
18205             Roo.log("waiting for the transitionend");
18206             return;
18207         }
18208         
18209         if (typeof(pan) == 'number') {
18210             pan = this.tabs[pan];
18211         }
18212         
18213         if (typeof(pan) == 'string') {
18214             pan = this.getPanelByName(pan);
18215         }
18216         
18217         var cur = this.getActivePanel();
18218         
18219         if(!pan || !cur){
18220             Roo.log('pan or acitve pan is undefined');
18221             return false;
18222         }
18223         
18224         if (pan.tabId == this.getActivePanel().tabId) {
18225             return true;
18226         }
18227         
18228         if (false === cur.fireEvent('beforedeactivate')) {
18229             return false;
18230         }
18231         
18232         if(this.bullets > 0 && !Roo.isTouch){
18233             this.setActiveBullet(this.indexOfPanel(pan));
18234         }
18235         
18236         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18237             
18238             this.transition = true;
18239             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18240             var lr = dir == 'next' ? 'left' : 'right';
18241             pan.el.addClass(dir); // or prev
18242             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18243             cur.el.addClass(lr); // or right
18244             pan.el.addClass(lr);
18245             
18246             var _this = this;
18247             cur.el.on('transitionend', function() {
18248                 Roo.log("trans end?");
18249                 
18250                 pan.el.removeClass([lr,dir]);
18251                 pan.setActive(true);
18252                 
18253                 cur.el.removeClass([lr]);
18254                 cur.setActive(false);
18255                 
18256                 _this.transition = false;
18257                 
18258             }, this, { single:  true } );
18259             
18260             return true;
18261         }
18262         
18263         cur.setActive(false);
18264         pan.setActive(true);
18265         
18266         return true;
18267         
18268     },
18269     showPanelNext : function()
18270     {
18271         var i = this.indexOfPanel(this.getActivePanel());
18272         
18273         if (i >= this.tabs.length - 1 && !this.autoslide) {
18274             return;
18275         }
18276         
18277         if (i >= this.tabs.length - 1 && this.autoslide) {
18278             i = -1;
18279         }
18280         
18281         this.showPanel(this.tabs[i+1]);
18282     },
18283     
18284     showPanelPrev : function()
18285     {
18286         var i = this.indexOfPanel(this.getActivePanel());
18287         
18288         if (i  < 1 && !this.autoslide) {
18289             return;
18290         }
18291         
18292         if (i < 1 && this.autoslide) {
18293             i = this.tabs.length;
18294         }
18295         
18296         this.showPanel(this.tabs[i-1]);
18297     },
18298     
18299     
18300     addBullet: function()
18301     {
18302         if(!this.bullets || Roo.isTouch){
18303             return;
18304         }
18305         var ctr = this.el.select('.carousel-bullets',true).first();
18306         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18307         var bullet = ctr.createChild({
18308             cls : 'bullet bullet-' + i
18309         },ctr.dom.lastChild);
18310         
18311         
18312         var _this = this;
18313         
18314         bullet.on('click', (function(e, el, o, ii, t){
18315
18316             e.preventDefault();
18317
18318             this.showPanel(ii);
18319
18320             if(this.autoslide && this.slideFn){
18321                 clearInterval(this.slideFn);
18322                 this.slideFn = window.setInterval(function() {
18323                     _this.showPanelNext();
18324                 }, this.timer);
18325             }
18326
18327         }).createDelegate(this, [i, bullet], true));
18328                 
18329         
18330     },
18331      
18332     setActiveBullet : function(i)
18333     {
18334         if(Roo.isTouch){
18335             return;
18336         }
18337         
18338         Roo.each(this.el.select('.bullet', true).elements, function(el){
18339             el.removeClass('selected');
18340         });
18341
18342         var bullet = this.el.select('.bullet-' + i, true).first();
18343         
18344         if(!bullet){
18345             return;
18346         }
18347         
18348         bullet.addClass('selected');
18349     }
18350     
18351     
18352   
18353 });
18354
18355  
18356
18357  
18358  
18359 Roo.apply(Roo.bootstrap.TabGroup, {
18360     
18361     groups: {},
18362      /**
18363     * register a Navigation Group
18364     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18365     */
18366     register : function(navgrp)
18367     {
18368         this.groups[navgrp.navId] = navgrp;
18369         
18370     },
18371     /**
18372     * fetch a Navigation Group based on the navigation ID
18373     * if one does not exist , it will get created.
18374     * @param {string} the navgroup to add
18375     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18376     */
18377     get: function(navId) {
18378         if (typeof(this.groups[navId]) == 'undefined') {
18379             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18380         }
18381         return this.groups[navId] ;
18382     }
18383     
18384     
18385     
18386 });
18387
18388  /*
18389  * - LGPL
18390  *
18391  * TabPanel
18392  * 
18393  */
18394
18395 /**
18396  * @class Roo.bootstrap.TabPanel
18397  * @extends Roo.bootstrap.Component
18398  * Bootstrap TabPanel class
18399  * @cfg {Boolean} active panel active
18400  * @cfg {String} html panel content
18401  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18402  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18403  * @cfg {String} href click to link..
18404  * 
18405  * 
18406  * @constructor
18407  * Create a new TabPanel
18408  * @param {Object} config The config object
18409  */
18410
18411 Roo.bootstrap.TabPanel = function(config){
18412     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18413     this.addEvents({
18414         /**
18415              * @event changed
18416              * Fires when the active status changes
18417              * @param {Roo.bootstrap.TabPanel} this
18418              * @param {Boolean} state the new state
18419             
18420          */
18421         'changed': true,
18422         /**
18423              * @event beforedeactivate
18424              * Fires before a tab is de-activated - can be used to do validation on a form.
18425              * @param {Roo.bootstrap.TabPanel} this
18426              * @return {Boolean} false if there is an error
18427             
18428          */
18429         'beforedeactivate': true
18430      });
18431     
18432     this.tabId = this.tabId || Roo.id();
18433   
18434 };
18435
18436 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18437     
18438     active: false,
18439     html: false,
18440     tabId: false,
18441     navId : false,
18442     href : '',
18443     
18444     getAutoCreate : function(){
18445         var cfg = {
18446             tag: 'div',
18447             // item is needed for carousel - not sure if it has any effect otherwise
18448             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18449             html: this.html || ''
18450         };
18451         
18452         if(this.active){
18453             cfg.cls += ' active';
18454         }
18455         
18456         if(this.tabId){
18457             cfg.tabId = this.tabId;
18458         }
18459         
18460         
18461         return cfg;
18462     },
18463     
18464     initEvents:  function()
18465     {
18466         var p = this.parent();
18467         
18468         this.navId = this.navId || p.navId;
18469         
18470         if (typeof(this.navId) != 'undefined') {
18471             // not really needed.. but just in case.. parent should be a NavGroup.
18472             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18473             
18474             tg.register(this);
18475             
18476             var i = tg.tabs.length - 1;
18477             
18478             if(this.active && tg.bullets > 0 && i < tg.bullets){
18479                 tg.setActiveBullet(i);
18480             }
18481         }
18482         
18483         this.el.on('click', this.onClick, this);
18484         
18485         if(Roo.isTouch){
18486             this.el.on("touchstart", this.onTouchStart, this);
18487             this.el.on("touchmove", this.onTouchMove, this);
18488             this.el.on("touchend", this.onTouchEnd, this);
18489         }
18490         
18491     },
18492     
18493     onRender : function(ct, position)
18494     {
18495         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18496     },
18497     
18498     setActive : function(state)
18499     {
18500         Roo.log("panel - set active " + this.tabId + "=" + state);
18501         
18502         this.active = state;
18503         if (!state) {
18504             this.el.removeClass('active');
18505             
18506         } else  if (!this.el.hasClass('active')) {
18507             this.el.addClass('active');
18508         }
18509         
18510         this.fireEvent('changed', this, state);
18511     },
18512     
18513     onClick : function(e)
18514     {
18515         e.preventDefault();
18516         
18517         if(!this.href.length){
18518             return;
18519         }
18520         
18521         window.location.href = this.href;
18522     },
18523     
18524     startX : 0,
18525     startY : 0,
18526     endX : 0,
18527     endY : 0,
18528     swiping : false,
18529     
18530     onTouchStart : function(e)
18531     {
18532         this.swiping = false;
18533         
18534         this.startX = e.browserEvent.touches[0].clientX;
18535         this.startY = e.browserEvent.touches[0].clientY;
18536     },
18537     
18538     onTouchMove : function(e)
18539     {
18540         this.swiping = true;
18541         
18542         this.endX = e.browserEvent.touches[0].clientX;
18543         this.endY = e.browserEvent.touches[0].clientY;
18544     },
18545     
18546     onTouchEnd : function(e)
18547     {
18548         if(!this.swiping){
18549             this.onClick(e);
18550             return;
18551         }
18552         
18553         var tabGroup = this.parent();
18554         
18555         if(this.endX > this.startX){ // swiping right
18556             tabGroup.showPanelPrev();
18557             return;
18558         }
18559         
18560         if(this.startX > this.endX){ // swiping left
18561             tabGroup.showPanelNext();
18562             return;
18563         }
18564     }
18565     
18566     
18567 });
18568  
18569
18570  
18571
18572  /*
18573  * - LGPL
18574  *
18575  * DateField
18576  * 
18577  */
18578
18579 /**
18580  * @class Roo.bootstrap.DateField
18581  * @extends Roo.bootstrap.Input
18582  * Bootstrap DateField class
18583  * @cfg {Number} weekStart default 0
18584  * @cfg {String} viewMode default empty, (months|years)
18585  * @cfg {String} minViewMode default empty, (months|years)
18586  * @cfg {Number} startDate default -Infinity
18587  * @cfg {Number} endDate default Infinity
18588  * @cfg {Boolean} todayHighlight default false
18589  * @cfg {Boolean} todayBtn default false
18590  * @cfg {Boolean} calendarWeeks default false
18591  * @cfg {Object} daysOfWeekDisabled default empty
18592  * @cfg {Boolean} singleMode default false (true | false)
18593  * 
18594  * @cfg {Boolean} keyboardNavigation default true
18595  * @cfg {String} language default en
18596  * 
18597  * @constructor
18598  * Create a new DateField
18599  * @param {Object} config The config object
18600  */
18601
18602 Roo.bootstrap.DateField = function(config){
18603     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18604      this.addEvents({
18605             /**
18606              * @event show
18607              * Fires when this field show.
18608              * @param {Roo.bootstrap.DateField} this
18609              * @param {Mixed} date The date value
18610              */
18611             show : true,
18612             /**
18613              * @event show
18614              * Fires when this field hide.
18615              * @param {Roo.bootstrap.DateField} this
18616              * @param {Mixed} date The date value
18617              */
18618             hide : true,
18619             /**
18620              * @event select
18621              * Fires when select a date.
18622              * @param {Roo.bootstrap.DateField} this
18623              * @param {Mixed} date The date value
18624              */
18625             select : true,
18626             /**
18627              * @event beforeselect
18628              * Fires when before select a date.
18629              * @param {Roo.bootstrap.DateField} this
18630              * @param {Mixed} date The date value
18631              */
18632             beforeselect : true
18633         });
18634 };
18635
18636 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18637     
18638     /**
18639      * @cfg {String} format
18640      * The default date format string which can be overriden for localization support.  The format must be
18641      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18642      */
18643     format : "m/d/y",
18644     /**
18645      * @cfg {String} altFormats
18646      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18647      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18648      */
18649     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18650     
18651     weekStart : 0,
18652     
18653     viewMode : '',
18654     
18655     minViewMode : '',
18656     
18657     todayHighlight : false,
18658     
18659     todayBtn: false,
18660     
18661     language: 'en',
18662     
18663     keyboardNavigation: true,
18664     
18665     calendarWeeks: false,
18666     
18667     startDate: -Infinity,
18668     
18669     endDate: Infinity,
18670     
18671     daysOfWeekDisabled: [],
18672     
18673     _events: [],
18674     
18675     singleMode : false,
18676     
18677     UTCDate: function()
18678     {
18679         return new Date(Date.UTC.apply(Date, arguments));
18680     },
18681     
18682     UTCToday: function()
18683     {
18684         var today = new Date();
18685         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18686     },
18687     
18688     getDate: function() {
18689             var d = this.getUTCDate();
18690             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18691     },
18692     
18693     getUTCDate: function() {
18694             return this.date;
18695     },
18696     
18697     setDate: function(d) {
18698             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18699     },
18700     
18701     setUTCDate: function(d) {
18702             this.date = d;
18703             this.setValue(this.formatDate(this.date));
18704     },
18705         
18706     onRender: function(ct, position)
18707     {
18708         
18709         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18710         
18711         this.language = this.language || 'en';
18712         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18713         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18714         
18715         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18716         this.format = this.format || 'm/d/y';
18717         this.isInline = false;
18718         this.isInput = true;
18719         this.component = this.el.select('.add-on', true).first() || false;
18720         this.component = (this.component && this.component.length === 0) ? false : this.component;
18721         this.hasInput = this.component && this.inputEl().length;
18722         
18723         if (typeof(this.minViewMode === 'string')) {
18724             switch (this.minViewMode) {
18725                 case 'months':
18726                     this.minViewMode = 1;
18727                     break;
18728                 case 'years':
18729                     this.minViewMode = 2;
18730                     break;
18731                 default:
18732                     this.minViewMode = 0;
18733                     break;
18734             }
18735         }
18736         
18737         if (typeof(this.viewMode === 'string')) {
18738             switch (this.viewMode) {
18739                 case 'months':
18740                     this.viewMode = 1;
18741                     break;
18742                 case 'years':
18743                     this.viewMode = 2;
18744                     break;
18745                 default:
18746                     this.viewMode = 0;
18747                     break;
18748             }
18749         }
18750                 
18751         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18752         
18753 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18754         
18755         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18756         
18757         this.picker().on('mousedown', this.onMousedown, this);
18758         this.picker().on('click', this.onClick, this);
18759         
18760         this.picker().addClass('datepicker-dropdown');
18761         
18762         this.startViewMode = this.viewMode;
18763         
18764         if(this.singleMode){
18765             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18766                 v.setVisibilityMode(Roo.Element.DISPLAY);
18767                 v.hide();
18768             });
18769             
18770             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18771                 v.setStyle('width', '189px');
18772             });
18773         }
18774         
18775         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18776             if(!this.calendarWeeks){
18777                 v.remove();
18778                 return;
18779             }
18780             
18781             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18782             v.attr('colspan', function(i, val){
18783                 return parseInt(val) + 1;
18784             });
18785         });
18786                         
18787         
18788         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18789         
18790         this.setStartDate(this.startDate);
18791         this.setEndDate(this.endDate);
18792         
18793         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18794         
18795         this.fillDow();
18796         this.fillMonths();
18797         this.update();
18798         this.showMode();
18799         
18800         if(this.isInline) {
18801             this.showPopup();
18802         }
18803     },
18804     
18805     picker : function()
18806     {
18807         return this.pickerEl;
18808 //        return this.el.select('.datepicker', true).first();
18809     },
18810     
18811     fillDow: function()
18812     {
18813         var dowCnt = this.weekStart;
18814         
18815         var dow = {
18816             tag: 'tr',
18817             cn: [
18818                 
18819             ]
18820         };
18821         
18822         if(this.calendarWeeks){
18823             dow.cn.push({
18824                 tag: 'th',
18825                 cls: 'cw',
18826                 html: '&nbsp;'
18827             })
18828         }
18829         
18830         while (dowCnt < this.weekStart + 7) {
18831             dow.cn.push({
18832                 tag: 'th',
18833                 cls: 'dow',
18834                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18835             });
18836         }
18837         
18838         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18839     },
18840     
18841     fillMonths: function()
18842     {    
18843         var i = 0;
18844         var months = this.picker().select('>.datepicker-months td', true).first();
18845         
18846         months.dom.innerHTML = '';
18847         
18848         while (i < 12) {
18849             var month = {
18850                 tag: 'span',
18851                 cls: 'month',
18852                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18853             };
18854             
18855             months.createChild(month);
18856         }
18857         
18858     },
18859     
18860     update: function()
18861     {
18862         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;
18863         
18864         if (this.date < this.startDate) {
18865             this.viewDate = new Date(this.startDate);
18866         } else if (this.date > this.endDate) {
18867             this.viewDate = new Date(this.endDate);
18868         } else {
18869             this.viewDate = new Date(this.date);
18870         }
18871         
18872         this.fill();
18873     },
18874     
18875     fill: function() 
18876     {
18877         var d = new Date(this.viewDate),
18878                 year = d.getUTCFullYear(),
18879                 month = d.getUTCMonth(),
18880                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18881                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18882                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18883                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18884                 currentDate = this.date && this.date.valueOf(),
18885                 today = this.UTCToday();
18886         
18887         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18888         
18889 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18890         
18891 //        this.picker.select('>tfoot th.today').
18892 //                                              .text(dates[this.language].today)
18893 //                                              .toggle(this.todayBtn !== false);
18894     
18895         this.updateNavArrows();
18896         this.fillMonths();
18897                                                 
18898         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18899         
18900         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18901          
18902         prevMonth.setUTCDate(day);
18903         
18904         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18905         
18906         var nextMonth = new Date(prevMonth);
18907         
18908         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18909         
18910         nextMonth = nextMonth.valueOf();
18911         
18912         var fillMonths = false;
18913         
18914         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18915         
18916         while(prevMonth.valueOf() <= nextMonth) {
18917             var clsName = '';
18918             
18919             if (prevMonth.getUTCDay() === this.weekStart) {
18920                 if(fillMonths){
18921                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18922                 }
18923                     
18924                 fillMonths = {
18925                     tag: 'tr',
18926                     cn: []
18927                 };
18928                 
18929                 if(this.calendarWeeks){
18930                     // ISO 8601: First week contains first thursday.
18931                     // ISO also states week starts on Monday, but we can be more abstract here.
18932                     var
18933                     // Start of current week: based on weekstart/current date
18934                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18935                     // Thursday of this week
18936                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18937                     // First Thursday of year, year from thursday
18938                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18939                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18940                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18941                     
18942                     fillMonths.cn.push({
18943                         tag: 'td',
18944                         cls: 'cw',
18945                         html: calWeek
18946                     });
18947                 }
18948             }
18949             
18950             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18951                 clsName += ' old';
18952             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18953                 clsName += ' new';
18954             }
18955             if (this.todayHighlight &&
18956                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18957                 prevMonth.getUTCMonth() == today.getMonth() &&
18958                 prevMonth.getUTCDate() == today.getDate()) {
18959                 clsName += ' today';
18960             }
18961             
18962             if (currentDate && prevMonth.valueOf() === currentDate) {
18963                 clsName += ' active';
18964             }
18965             
18966             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18967                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18968                     clsName += ' disabled';
18969             }
18970             
18971             fillMonths.cn.push({
18972                 tag: 'td',
18973                 cls: 'day ' + clsName,
18974                 html: prevMonth.getDate()
18975             });
18976             
18977             prevMonth.setDate(prevMonth.getDate()+1);
18978         }
18979           
18980         var currentYear = this.date && this.date.getUTCFullYear();
18981         var currentMonth = this.date && this.date.getUTCMonth();
18982         
18983         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18984         
18985         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18986             v.removeClass('active');
18987             
18988             if(currentYear === year && k === currentMonth){
18989                 v.addClass('active');
18990             }
18991             
18992             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18993                 v.addClass('disabled');
18994             }
18995             
18996         });
18997         
18998         
18999         year = parseInt(year/10, 10) * 10;
19000         
19001         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19002         
19003         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19004         
19005         year -= 1;
19006         for (var i = -1; i < 11; i++) {
19007             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19008                 tag: 'span',
19009                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19010                 html: year
19011             });
19012             
19013             year += 1;
19014         }
19015     },
19016     
19017     showMode: function(dir) 
19018     {
19019         if (dir) {
19020             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19021         }
19022         
19023         Roo.each(this.picker().select('>div',true).elements, function(v){
19024             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19025             v.hide();
19026         });
19027         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19028     },
19029     
19030     place: function()
19031     {
19032         if(this.isInline) {
19033             return;
19034         }
19035         
19036         this.picker().removeClass(['bottom', 'top']);
19037         
19038         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19039             /*
19040              * place to the top of element!
19041              *
19042              */
19043             
19044             this.picker().addClass('top');
19045             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19046             
19047             return;
19048         }
19049         
19050         this.picker().addClass('bottom');
19051         
19052         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19053     },
19054     
19055     parseDate : function(value)
19056     {
19057         if(!value || value instanceof Date){
19058             return value;
19059         }
19060         var v = Date.parseDate(value, this.format);
19061         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19062             v = Date.parseDate(value, 'Y-m-d');
19063         }
19064         if(!v && this.altFormats){
19065             if(!this.altFormatsArray){
19066                 this.altFormatsArray = this.altFormats.split("|");
19067             }
19068             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19069                 v = Date.parseDate(value, this.altFormatsArray[i]);
19070             }
19071         }
19072         return v;
19073     },
19074     
19075     formatDate : function(date, fmt)
19076     {   
19077         return (!date || !(date instanceof Date)) ?
19078         date : date.dateFormat(fmt || this.format);
19079     },
19080     
19081     onFocus : function()
19082     {
19083         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19084         this.showPopup();
19085     },
19086     
19087     onBlur : function()
19088     {
19089         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19090         
19091         var d = this.inputEl().getValue();
19092         
19093         this.setValue(d);
19094                 
19095         this.hidePopup();
19096     },
19097     
19098     showPopup : function()
19099     {
19100         this.picker().show();
19101         this.update();
19102         this.place();
19103         
19104         this.fireEvent('showpopup', this, this.date);
19105     },
19106     
19107     hidePopup : function()
19108     {
19109         if(this.isInline) {
19110             return;
19111         }
19112         this.picker().hide();
19113         this.viewMode = this.startViewMode;
19114         this.showMode();
19115         
19116         this.fireEvent('hidepopup', this, this.date);
19117         
19118     },
19119     
19120     onMousedown: function(e)
19121     {
19122         e.stopPropagation();
19123         e.preventDefault();
19124     },
19125     
19126     keyup: function(e)
19127     {
19128         Roo.bootstrap.DateField.superclass.keyup.call(this);
19129         this.update();
19130     },
19131
19132     setValue: function(v)
19133     {
19134         if(this.fireEvent('beforeselect', this, v) !== false){
19135             var d = new Date(this.parseDate(v) ).clearTime();
19136         
19137             if(isNaN(d.getTime())){
19138                 this.date = this.viewDate = '';
19139                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19140                 return;
19141             }
19142
19143             v = this.formatDate(d);
19144
19145             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19146
19147             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19148
19149             this.update();
19150
19151             this.fireEvent('select', this, this.date);
19152         }
19153     },
19154     
19155     getValue: function()
19156     {
19157         return this.formatDate(this.date);
19158     },
19159     
19160     fireKey: function(e)
19161     {
19162         if (!this.picker().isVisible()){
19163             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19164                 this.showPopup();
19165             }
19166             return;
19167         }
19168         
19169         var dateChanged = false,
19170         dir, day, month,
19171         newDate, newViewDate;
19172         
19173         switch(e.keyCode){
19174             case 27: // escape
19175                 this.hidePopup();
19176                 e.preventDefault();
19177                 break;
19178             case 37: // left
19179             case 39: // right
19180                 if (!this.keyboardNavigation) {
19181                     break;
19182                 }
19183                 dir = e.keyCode == 37 ? -1 : 1;
19184                 
19185                 if (e.ctrlKey){
19186                     newDate = this.moveYear(this.date, dir);
19187                     newViewDate = this.moveYear(this.viewDate, dir);
19188                 } else if (e.shiftKey){
19189                     newDate = this.moveMonth(this.date, dir);
19190                     newViewDate = this.moveMonth(this.viewDate, dir);
19191                 } else {
19192                     newDate = new Date(this.date);
19193                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19194                     newViewDate = new Date(this.viewDate);
19195                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19196                 }
19197                 if (this.dateWithinRange(newDate)){
19198                     this.date = newDate;
19199                     this.viewDate = newViewDate;
19200                     this.setValue(this.formatDate(this.date));
19201 //                    this.update();
19202                     e.preventDefault();
19203                     dateChanged = true;
19204                 }
19205                 break;
19206             case 38: // up
19207             case 40: // down
19208                 if (!this.keyboardNavigation) {
19209                     break;
19210                 }
19211                 dir = e.keyCode == 38 ? -1 : 1;
19212                 if (e.ctrlKey){
19213                     newDate = this.moveYear(this.date, dir);
19214                     newViewDate = this.moveYear(this.viewDate, dir);
19215                 } else if (e.shiftKey){
19216                     newDate = this.moveMonth(this.date, dir);
19217                     newViewDate = this.moveMonth(this.viewDate, dir);
19218                 } else {
19219                     newDate = new Date(this.date);
19220                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19221                     newViewDate = new Date(this.viewDate);
19222                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19223                 }
19224                 if (this.dateWithinRange(newDate)){
19225                     this.date = newDate;
19226                     this.viewDate = newViewDate;
19227                     this.setValue(this.formatDate(this.date));
19228 //                    this.update();
19229                     e.preventDefault();
19230                     dateChanged = true;
19231                 }
19232                 break;
19233             case 13: // enter
19234                 this.setValue(this.formatDate(this.date));
19235                 this.hidePopup();
19236                 e.preventDefault();
19237                 break;
19238             case 9: // tab
19239                 this.setValue(this.formatDate(this.date));
19240                 this.hidePopup();
19241                 break;
19242             case 16: // shift
19243             case 17: // ctrl
19244             case 18: // alt
19245                 break;
19246             default :
19247                 this.hidePopup();
19248                 
19249         }
19250     },
19251     
19252     
19253     onClick: function(e) 
19254     {
19255         e.stopPropagation();
19256         e.preventDefault();
19257         
19258         var target = e.getTarget();
19259         
19260         if(target.nodeName.toLowerCase() === 'i'){
19261             target = Roo.get(target).dom.parentNode;
19262         }
19263         
19264         var nodeName = target.nodeName;
19265         var className = target.className;
19266         var html = target.innerHTML;
19267         //Roo.log(nodeName);
19268         
19269         switch(nodeName.toLowerCase()) {
19270             case 'th':
19271                 switch(className) {
19272                     case 'switch':
19273                         this.showMode(1);
19274                         break;
19275                     case 'prev':
19276                     case 'next':
19277                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19278                         switch(this.viewMode){
19279                                 case 0:
19280                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19281                                         break;
19282                                 case 1:
19283                                 case 2:
19284                                         this.viewDate = this.moveYear(this.viewDate, dir);
19285                                         break;
19286                         }
19287                         this.fill();
19288                         break;
19289                     case 'today':
19290                         var date = new Date();
19291                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19292 //                        this.fill()
19293                         this.setValue(this.formatDate(this.date));
19294                         
19295                         this.hidePopup();
19296                         break;
19297                 }
19298                 break;
19299             case 'span':
19300                 if (className.indexOf('disabled') < 0) {
19301                     this.viewDate.setUTCDate(1);
19302                     if (className.indexOf('month') > -1) {
19303                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19304                     } else {
19305                         var year = parseInt(html, 10) || 0;
19306                         this.viewDate.setUTCFullYear(year);
19307                         
19308                     }
19309                     
19310                     if(this.singleMode){
19311                         this.setValue(this.formatDate(this.viewDate));
19312                         this.hidePopup();
19313                         return;
19314                     }
19315                     
19316                     this.showMode(-1);
19317                     this.fill();
19318                 }
19319                 break;
19320                 
19321             case 'td':
19322                 //Roo.log(className);
19323                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19324                     var day = parseInt(html, 10) || 1;
19325                     var year = this.viewDate.getUTCFullYear(),
19326                         month = this.viewDate.getUTCMonth();
19327
19328                     if (className.indexOf('old') > -1) {
19329                         if(month === 0 ){
19330                             month = 11;
19331                             year -= 1;
19332                         }else{
19333                             month -= 1;
19334                         }
19335                     } else if (className.indexOf('new') > -1) {
19336                         if (month == 11) {
19337                             month = 0;
19338                             year += 1;
19339                         } else {
19340                             month += 1;
19341                         }
19342                     }
19343                     //Roo.log([year,month,day]);
19344                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19345                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19346 //                    this.fill();
19347                     //Roo.log(this.formatDate(this.date));
19348                     this.setValue(this.formatDate(this.date));
19349                     this.hidePopup();
19350                 }
19351                 break;
19352         }
19353     },
19354     
19355     setStartDate: function(startDate)
19356     {
19357         this.startDate = startDate || -Infinity;
19358         if (this.startDate !== -Infinity) {
19359             this.startDate = this.parseDate(this.startDate);
19360         }
19361         this.update();
19362         this.updateNavArrows();
19363     },
19364
19365     setEndDate: function(endDate)
19366     {
19367         this.endDate = endDate || Infinity;
19368         if (this.endDate !== Infinity) {
19369             this.endDate = this.parseDate(this.endDate);
19370         }
19371         this.update();
19372         this.updateNavArrows();
19373     },
19374     
19375     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19376     {
19377         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19378         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19379             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19380         }
19381         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19382             return parseInt(d, 10);
19383         });
19384         this.update();
19385         this.updateNavArrows();
19386     },
19387     
19388     updateNavArrows: function() 
19389     {
19390         if(this.singleMode){
19391             return;
19392         }
19393         
19394         var d = new Date(this.viewDate),
19395         year = d.getUTCFullYear(),
19396         month = d.getUTCMonth();
19397         
19398         Roo.each(this.picker().select('.prev', true).elements, function(v){
19399             v.show();
19400             switch (this.viewMode) {
19401                 case 0:
19402
19403                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19404                         v.hide();
19405                     }
19406                     break;
19407                 case 1:
19408                 case 2:
19409                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19410                         v.hide();
19411                     }
19412                     break;
19413             }
19414         });
19415         
19416         Roo.each(this.picker().select('.next', true).elements, function(v){
19417             v.show();
19418             switch (this.viewMode) {
19419                 case 0:
19420
19421                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19422                         v.hide();
19423                     }
19424                     break;
19425                 case 1:
19426                 case 2:
19427                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19428                         v.hide();
19429                     }
19430                     break;
19431             }
19432         })
19433     },
19434     
19435     moveMonth: function(date, dir)
19436     {
19437         if (!dir) {
19438             return date;
19439         }
19440         var new_date = new Date(date.valueOf()),
19441         day = new_date.getUTCDate(),
19442         month = new_date.getUTCMonth(),
19443         mag = Math.abs(dir),
19444         new_month, test;
19445         dir = dir > 0 ? 1 : -1;
19446         if (mag == 1){
19447             test = dir == -1
19448             // If going back one month, make sure month is not current month
19449             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19450             ? function(){
19451                 return new_date.getUTCMonth() == month;
19452             }
19453             // If going forward one month, make sure month is as expected
19454             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19455             : function(){
19456                 return new_date.getUTCMonth() != new_month;
19457             };
19458             new_month = month + dir;
19459             new_date.setUTCMonth(new_month);
19460             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19461             if (new_month < 0 || new_month > 11) {
19462                 new_month = (new_month + 12) % 12;
19463             }
19464         } else {
19465             // For magnitudes >1, move one month at a time...
19466             for (var i=0; i<mag; i++) {
19467                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19468                 new_date = this.moveMonth(new_date, dir);
19469             }
19470             // ...then reset the day, keeping it in the new month
19471             new_month = new_date.getUTCMonth();
19472             new_date.setUTCDate(day);
19473             test = function(){
19474                 return new_month != new_date.getUTCMonth();
19475             };
19476         }
19477         // Common date-resetting loop -- if date is beyond end of month, make it
19478         // end of month
19479         while (test()){
19480             new_date.setUTCDate(--day);
19481             new_date.setUTCMonth(new_month);
19482         }
19483         return new_date;
19484     },
19485
19486     moveYear: function(date, dir)
19487     {
19488         return this.moveMonth(date, dir*12);
19489     },
19490
19491     dateWithinRange: function(date)
19492     {
19493         return date >= this.startDate && date <= this.endDate;
19494     },
19495
19496     
19497     remove: function() 
19498     {
19499         this.picker().remove();
19500     },
19501     
19502     validateValue : function(value)
19503     {
19504         if(this.getVisibilityEl().hasClass('hidden')){
19505             return true;
19506         }
19507         
19508         if(value.length < 1)  {
19509             if(this.allowBlank){
19510                 return true;
19511             }
19512             return false;
19513         }
19514         
19515         if(value.length < this.minLength){
19516             return false;
19517         }
19518         if(value.length > this.maxLength){
19519             return false;
19520         }
19521         if(this.vtype){
19522             var vt = Roo.form.VTypes;
19523             if(!vt[this.vtype](value, this)){
19524                 return false;
19525             }
19526         }
19527         if(typeof this.validator == "function"){
19528             var msg = this.validator(value);
19529             if(msg !== true){
19530                 return false;
19531             }
19532         }
19533         
19534         if(this.regex && !this.regex.test(value)){
19535             return false;
19536         }
19537         
19538         if(typeof(this.parseDate(value)) == 'undefined'){
19539             return false;
19540         }
19541         
19542         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19543             return false;
19544         }      
19545         
19546         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19547             return false;
19548         } 
19549         
19550         
19551         return true;
19552     },
19553     
19554     reset : function()
19555     {
19556         this.date = this.viewDate = '';
19557         
19558         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19559     }
19560    
19561 });
19562
19563 Roo.apply(Roo.bootstrap.DateField,  {
19564     
19565     head : {
19566         tag: 'thead',
19567         cn: [
19568         {
19569             tag: 'tr',
19570             cn: [
19571             {
19572                 tag: 'th',
19573                 cls: 'prev',
19574                 html: '<i class="fa fa-arrow-left"/>'
19575             },
19576             {
19577                 tag: 'th',
19578                 cls: 'switch',
19579                 colspan: '5'
19580             },
19581             {
19582                 tag: 'th',
19583                 cls: 'next',
19584                 html: '<i class="fa fa-arrow-right"/>'
19585             }
19586
19587             ]
19588         }
19589         ]
19590     },
19591     
19592     content : {
19593         tag: 'tbody',
19594         cn: [
19595         {
19596             tag: 'tr',
19597             cn: [
19598             {
19599                 tag: 'td',
19600                 colspan: '7'
19601             }
19602             ]
19603         }
19604         ]
19605     },
19606     
19607     footer : {
19608         tag: 'tfoot',
19609         cn: [
19610         {
19611             tag: 'tr',
19612             cn: [
19613             {
19614                 tag: 'th',
19615                 colspan: '7',
19616                 cls: 'today'
19617             }
19618                     
19619             ]
19620         }
19621         ]
19622     },
19623     
19624     dates:{
19625         en: {
19626             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19627             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19628             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19629             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19630             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19631             today: "Today"
19632         }
19633     },
19634     
19635     modes: [
19636     {
19637         clsName: 'days',
19638         navFnc: 'Month',
19639         navStep: 1
19640     },
19641     {
19642         clsName: 'months',
19643         navFnc: 'FullYear',
19644         navStep: 1
19645     },
19646     {
19647         clsName: 'years',
19648         navFnc: 'FullYear',
19649         navStep: 10
19650     }]
19651 });
19652
19653 Roo.apply(Roo.bootstrap.DateField,  {
19654   
19655     template : {
19656         tag: 'div',
19657         cls: 'datepicker dropdown-menu roo-dynamic',
19658         cn: [
19659         {
19660             tag: 'div',
19661             cls: 'datepicker-days',
19662             cn: [
19663             {
19664                 tag: 'table',
19665                 cls: 'table-condensed',
19666                 cn:[
19667                 Roo.bootstrap.DateField.head,
19668                 {
19669                     tag: 'tbody'
19670                 },
19671                 Roo.bootstrap.DateField.footer
19672                 ]
19673             }
19674             ]
19675         },
19676         {
19677             tag: 'div',
19678             cls: 'datepicker-months',
19679             cn: [
19680             {
19681                 tag: 'table',
19682                 cls: 'table-condensed',
19683                 cn:[
19684                 Roo.bootstrap.DateField.head,
19685                 Roo.bootstrap.DateField.content,
19686                 Roo.bootstrap.DateField.footer
19687                 ]
19688             }
19689             ]
19690         },
19691         {
19692             tag: 'div',
19693             cls: 'datepicker-years',
19694             cn: [
19695             {
19696                 tag: 'table',
19697                 cls: 'table-condensed',
19698                 cn:[
19699                 Roo.bootstrap.DateField.head,
19700                 Roo.bootstrap.DateField.content,
19701                 Roo.bootstrap.DateField.footer
19702                 ]
19703             }
19704             ]
19705         }
19706         ]
19707     }
19708 });
19709
19710  
19711
19712  /*
19713  * - LGPL
19714  *
19715  * TimeField
19716  * 
19717  */
19718
19719 /**
19720  * @class Roo.bootstrap.TimeField
19721  * @extends Roo.bootstrap.Input
19722  * Bootstrap DateField class
19723  * 
19724  * 
19725  * @constructor
19726  * Create a new TimeField
19727  * @param {Object} config The config object
19728  */
19729
19730 Roo.bootstrap.TimeField = function(config){
19731     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19732     this.addEvents({
19733             /**
19734              * @event show
19735              * Fires when this field show.
19736              * @param {Roo.bootstrap.DateField} thisthis
19737              * @param {Mixed} date The date value
19738              */
19739             show : true,
19740             /**
19741              * @event show
19742              * Fires when this field hide.
19743              * @param {Roo.bootstrap.DateField} this
19744              * @param {Mixed} date The date value
19745              */
19746             hide : true,
19747             /**
19748              * @event select
19749              * Fires when select a date.
19750              * @param {Roo.bootstrap.DateField} this
19751              * @param {Mixed} date The date value
19752              */
19753             select : true
19754         });
19755 };
19756
19757 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19758     
19759     /**
19760      * @cfg {String} format
19761      * The default time format string which can be overriden for localization support.  The format must be
19762      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19763      */
19764     format : "H:i",
19765        
19766     onRender: function(ct, position)
19767     {
19768         
19769         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19770                 
19771         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19772         
19773         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19774         
19775         this.pop = this.picker().select('>.datepicker-time',true).first();
19776         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19777         
19778         this.picker().on('mousedown', this.onMousedown, this);
19779         this.picker().on('click', this.onClick, this);
19780         
19781         this.picker().addClass('datepicker-dropdown');
19782     
19783         this.fillTime();
19784         this.update();
19785             
19786         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19787         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19788         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19789         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19790         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19791         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19792
19793     },
19794     
19795     fireKey: function(e){
19796         if (!this.picker().isVisible()){
19797             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19798                 this.show();
19799             }
19800             return;
19801         }
19802
19803         e.preventDefault();
19804         
19805         switch(e.keyCode){
19806             case 27: // escape
19807                 this.hide();
19808                 break;
19809             case 37: // left
19810             case 39: // right
19811                 this.onTogglePeriod();
19812                 break;
19813             case 38: // up
19814                 this.onIncrementMinutes();
19815                 break;
19816             case 40: // down
19817                 this.onDecrementMinutes();
19818                 break;
19819             case 13: // enter
19820             case 9: // tab
19821                 this.setTime();
19822                 break;
19823         }
19824     },
19825     
19826     onClick: function(e) {
19827         e.stopPropagation();
19828         e.preventDefault();
19829     },
19830     
19831     picker : function()
19832     {
19833         return this.el.select('.datepicker', true).first();
19834     },
19835     
19836     fillTime: function()
19837     {    
19838         var time = this.pop.select('tbody', true).first();
19839         
19840         time.dom.innerHTML = '';
19841         
19842         time.createChild({
19843             tag: 'tr',
19844             cn: [
19845                 {
19846                     tag: 'td',
19847                     cn: [
19848                         {
19849                             tag: 'a',
19850                             href: '#',
19851                             cls: 'btn',
19852                             cn: [
19853                                 {
19854                                     tag: 'span',
19855                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19856                                 }
19857                             ]
19858                         } 
19859                     ]
19860                 },
19861                 {
19862                     tag: 'td',
19863                     cls: 'separator'
19864                 },
19865                 {
19866                     tag: 'td',
19867                     cn: [
19868                         {
19869                             tag: 'a',
19870                             href: '#',
19871                             cls: 'btn',
19872                             cn: [
19873                                 {
19874                                     tag: 'span',
19875                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19876                                 }
19877                             ]
19878                         }
19879                     ]
19880                 },
19881                 {
19882                     tag: 'td',
19883                     cls: 'separator'
19884                 }
19885             ]
19886         });
19887         
19888         time.createChild({
19889             tag: 'tr',
19890             cn: [
19891                 {
19892                     tag: 'td',
19893                     cn: [
19894                         {
19895                             tag: 'span',
19896                             cls: 'timepicker-hour',
19897                             html: '00'
19898                         }  
19899                     ]
19900                 },
19901                 {
19902                     tag: 'td',
19903                     cls: 'separator',
19904                     html: ':'
19905                 },
19906                 {
19907                     tag: 'td',
19908                     cn: [
19909                         {
19910                             tag: 'span',
19911                             cls: 'timepicker-minute',
19912                             html: '00'
19913                         }  
19914                     ]
19915                 },
19916                 {
19917                     tag: 'td',
19918                     cls: 'separator'
19919                 },
19920                 {
19921                     tag: 'td',
19922                     cn: [
19923                         {
19924                             tag: 'button',
19925                             type: 'button',
19926                             cls: 'btn btn-primary period',
19927                             html: 'AM'
19928                             
19929                         }
19930                     ]
19931                 }
19932             ]
19933         });
19934         
19935         time.createChild({
19936             tag: 'tr',
19937             cn: [
19938                 {
19939                     tag: 'td',
19940                     cn: [
19941                         {
19942                             tag: 'a',
19943                             href: '#',
19944                             cls: 'btn',
19945                             cn: [
19946                                 {
19947                                     tag: 'span',
19948                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19949                                 }
19950                             ]
19951                         }
19952                     ]
19953                 },
19954                 {
19955                     tag: 'td',
19956                     cls: 'separator'
19957                 },
19958                 {
19959                     tag: 'td',
19960                     cn: [
19961                         {
19962                             tag: 'a',
19963                             href: '#',
19964                             cls: 'btn',
19965                             cn: [
19966                                 {
19967                                     tag: 'span',
19968                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19969                                 }
19970                             ]
19971                         }
19972                     ]
19973                 },
19974                 {
19975                     tag: 'td',
19976                     cls: 'separator'
19977                 }
19978             ]
19979         });
19980         
19981     },
19982     
19983     update: function()
19984     {
19985         
19986         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19987         
19988         this.fill();
19989     },
19990     
19991     fill: function() 
19992     {
19993         var hours = this.time.getHours();
19994         var minutes = this.time.getMinutes();
19995         var period = 'AM';
19996         
19997         if(hours > 11){
19998             period = 'PM';
19999         }
20000         
20001         if(hours == 0){
20002             hours = 12;
20003         }
20004         
20005         
20006         if(hours > 12){
20007             hours = hours - 12;
20008         }
20009         
20010         if(hours < 10){
20011             hours = '0' + hours;
20012         }
20013         
20014         if(minutes < 10){
20015             minutes = '0' + minutes;
20016         }
20017         
20018         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20019         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20020         this.pop.select('button', true).first().dom.innerHTML = period;
20021         
20022     },
20023     
20024     place: function()
20025     {   
20026         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20027         
20028         var cls = ['bottom'];
20029         
20030         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20031             cls.pop();
20032             cls.push('top');
20033         }
20034         
20035         cls.push('right');
20036         
20037         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20038             cls.pop();
20039             cls.push('left');
20040         }
20041         
20042         this.picker().addClass(cls.join('-'));
20043         
20044         var _this = this;
20045         
20046         Roo.each(cls, function(c){
20047             if(c == 'bottom'){
20048                 _this.picker().setTop(_this.inputEl().getHeight());
20049                 return;
20050             }
20051             if(c == 'top'){
20052                 _this.picker().setTop(0 - _this.picker().getHeight());
20053                 return;
20054             }
20055             
20056             if(c == 'left'){
20057                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20058                 return;
20059             }
20060             if(c == 'right'){
20061                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20062                 return;
20063             }
20064         });
20065         
20066     },
20067   
20068     onFocus : function()
20069     {
20070         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20071         this.show();
20072     },
20073     
20074     onBlur : function()
20075     {
20076         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20077         this.hide();
20078     },
20079     
20080     show : function()
20081     {
20082         this.picker().show();
20083         this.pop.show();
20084         this.update();
20085         this.place();
20086         
20087         this.fireEvent('show', this, this.date);
20088     },
20089     
20090     hide : function()
20091     {
20092         this.picker().hide();
20093         this.pop.hide();
20094         
20095         this.fireEvent('hide', this, this.date);
20096     },
20097     
20098     setTime : function()
20099     {
20100         this.hide();
20101         this.setValue(this.time.format(this.format));
20102         
20103         this.fireEvent('select', this, this.date);
20104         
20105         
20106     },
20107     
20108     onMousedown: function(e){
20109         e.stopPropagation();
20110         e.preventDefault();
20111     },
20112     
20113     onIncrementHours: function()
20114     {
20115         Roo.log('onIncrementHours');
20116         this.time = this.time.add(Date.HOUR, 1);
20117         this.update();
20118         
20119     },
20120     
20121     onDecrementHours: function()
20122     {
20123         Roo.log('onDecrementHours');
20124         this.time = this.time.add(Date.HOUR, -1);
20125         this.update();
20126     },
20127     
20128     onIncrementMinutes: function()
20129     {
20130         Roo.log('onIncrementMinutes');
20131         this.time = this.time.add(Date.MINUTE, 1);
20132         this.update();
20133     },
20134     
20135     onDecrementMinutes: function()
20136     {
20137         Roo.log('onDecrementMinutes');
20138         this.time = this.time.add(Date.MINUTE, -1);
20139         this.update();
20140     },
20141     
20142     onTogglePeriod: function()
20143     {
20144         Roo.log('onTogglePeriod');
20145         this.time = this.time.add(Date.HOUR, 12);
20146         this.update();
20147     }
20148     
20149    
20150 });
20151
20152 Roo.apply(Roo.bootstrap.TimeField,  {
20153     
20154     content : {
20155         tag: 'tbody',
20156         cn: [
20157             {
20158                 tag: 'tr',
20159                 cn: [
20160                 {
20161                     tag: 'td',
20162                     colspan: '7'
20163                 }
20164                 ]
20165             }
20166         ]
20167     },
20168     
20169     footer : {
20170         tag: 'tfoot',
20171         cn: [
20172             {
20173                 tag: 'tr',
20174                 cn: [
20175                 {
20176                     tag: 'th',
20177                     colspan: '7',
20178                     cls: '',
20179                     cn: [
20180                         {
20181                             tag: 'button',
20182                             cls: 'btn btn-info ok',
20183                             html: 'OK'
20184                         }
20185                     ]
20186                 }
20187
20188                 ]
20189             }
20190         ]
20191     }
20192 });
20193
20194 Roo.apply(Roo.bootstrap.TimeField,  {
20195   
20196     template : {
20197         tag: 'div',
20198         cls: 'datepicker dropdown-menu',
20199         cn: [
20200             {
20201                 tag: 'div',
20202                 cls: 'datepicker-time',
20203                 cn: [
20204                 {
20205                     tag: 'table',
20206                     cls: 'table-condensed',
20207                     cn:[
20208                     Roo.bootstrap.TimeField.content,
20209                     Roo.bootstrap.TimeField.footer
20210                     ]
20211                 }
20212                 ]
20213             }
20214         ]
20215     }
20216 });
20217
20218  
20219
20220  /*
20221  * - LGPL
20222  *
20223  * MonthField
20224  * 
20225  */
20226
20227 /**
20228  * @class Roo.bootstrap.MonthField
20229  * @extends Roo.bootstrap.Input
20230  * Bootstrap MonthField class
20231  * 
20232  * @cfg {String} language default en
20233  * 
20234  * @constructor
20235  * Create a new MonthField
20236  * @param {Object} config The config object
20237  */
20238
20239 Roo.bootstrap.MonthField = function(config){
20240     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20241     
20242     this.addEvents({
20243         /**
20244          * @event show
20245          * Fires when this field show.
20246          * @param {Roo.bootstrap.MonthField} this
20247          * @param {Mixed} date The date value
20248          */
20249         show : true,
20250         /**
20251          * @event show
20252          * Fires when this field hide.
20253          * @param {Roo.bootstrap.MonthField} this
20254          * @param {Mixed} date The date value
20255          */
20256         hide : true,
20257         /**
20258          * @event select
20259          * Fires when select a date.
20260          * @param {Roo.bootstrap.MonthField} this
20261          * @param {String} oldvalue The old value
20262          * @param {String} newvalue The new value
20263          */
20264         select : true
20265     });
20266 };
20267
20268 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20269     
20270     onRender: function(ct, position)
20271     {
20272         
20273         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20274         
20275         this.language = this.language || 'en';
20276         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20277         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20278         
20279         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20280         this.isInline = false;
20281         this.isInput = true;
20282         this.component = this.el.select('.add-on', true).first() || false;
20283         this.component = (this.component && this.component.length === 0) ? false : this.component;
20284         this.hasInput = this.component && this.inputEL().length;
20285         
20286         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20287         
20288         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20289         
20290         this.picker().on('mousedown', this.onMousedown, this);
20291         this.picker().on('click', this.onClick, this);
20292         
20293         this.picker().addClass('datepicker-dropdown');
20294         
20295         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20296             v.setStyle('width', '189px');
20297         });
20298         
20299         this.fillMonths();
20300         
20301         this.update();
20302         
20303         if(this.isInline) {
20304             this.show();
20305         }
20306         
20307     },
20308     
20309     setValue: function(v, suppressEvent)
20310     {   
20311         var o = this.getValue();
20312         
20313         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20314         
20315         this.update();
20316
20317         if(suppressEvent !== true){
20318             this.fireEvent('select', this, o, v);
20319         }
20320         
20321     },
20322     
20323     getValue: function()
20324     {
20325         return this.value;
20326     },
20327     
20328     onClick: function(e) 
20329     {
20330         e.stopPropagation();
20331         e.preventDefault();
20332         
20333         var target = e.getTarget();
20334         
20335         if(target.nodeName.toLowerCase() === 'i'){
20336             target = Roo.get(target).dom.parentNode;
20337         }
20338         
20339         var nodeName = target.nodeName;
20340         var className = target.className;
20341         var html = target.innerHTML;
20342         
20343         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20344             return;
20345         }
20346         
20347         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20348         
20349         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20350         
20351         this.hide();
20352                         
20353     },
20354     
20355     picker : function()
20356     {
20357         return this.pickerEl;
20358     },
20359     
20360     fillMonths: function()
20361     {    
20362         var i = 0;
20363         var months = this.picker().select('>.datepicker-months td', true).first();
20364         
20365         months.dom.innerHTML = '';
20366         
20367         while (i < 12) {
20368             var month = {
20369                 tag: 'span',
20370                 cls: 'month',
20371                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20372             };
20373             
20374             months.createChild(month);
20375         }
20376         
20377     },
20378     
20379     update: function()
20380     {
20381         var _this = this;
20382         
20383         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20384             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20385         }
20386         
20387         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20388             e.removeClass('active');
20389             
20390             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20391                 e.addClass('active');
20392             }
20393         })
20394     },
20395     
20396     place: function()
20397     {
20398         if(this.isInline) {
20399             return;
20400         }
20401         
20402         this.picker().removeClass(['bottom', 'top']);
20403         
20404         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20405             /*
20406              * place to the top of element!
20407              *
20408              */
20409             
20410             this.picker().addClass('top');
20411             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20412             
20413             return;
20414         }
20415         
20416         this.picker().addClass('bottom');
20417         
20418         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20419     },
20420     
20421     onFocus : function()
20422     {
20423         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20424         this.show();
20425     },
20426     
20427     onBlur : function()
20428     {
20429         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20430         
20431         var d = this.inputEl().getValue();
20432         
20433         this.setValue(d);
20434                 
20435         this.hide();
20436     },
20437     
20438     show : function()
20439     {
20440         this.picker().show();
20441         this.picker().select('>.datepicker-months', true).first().show();
20442         this.update();
20443         this.place();
20444         
20445         this.fireEvent('show', this, this.date);
20446     },
20447     
20448     hide : function()
20449     {
20450         if(this.isInline) {
20451             return;
20452         }
20453         this.picker().hide();
20454         this.fireEvent('hide', this, this.date);
20455         
20456     },
20457     
20458     onMousedown: function(e)
20459     {
20460         e.stopPropagation();
20461         e.preventDefault();
20462     },
20463     
20464     keyup: function(e)
20465     {
20466         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20467         this.update();
20468     },
20469
20470     fireKey: function(e)
20471     {
20472         if (!this.picker().isVisible()){
20473             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20474                 this.show();
20475             }
20476             return;
20477         }
20478         
20479         var dir;
20480         
20481         switch(e.keyCode){
20482             case 27: // escape
20483                 this.hide();
20484                 e.preventDefault();
20485                 break;
20486             case 37: // left
20487             case 39: // right
20488                 dir = e.keyCode == 37 ? -1 : 1;
20489                 
20490                 this.vIndex = this.vIndex + dir;
20491                 
20492                 if(this.vIndex < 0){
20493                     this.vIndex = 0;
20494                 }
20495                 
20496                 if(this.vIndex > 11){
20497                     this.vIndex = 11;
20498                 }
20499                 
20500                 if(isNaN(this.vIndex)){
20501                     this.vIndex = 0;
20502                 }
20503                 
20504                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20505                 
20506                 break;
20507             case 38: // up
20508             case 40: // down
20509                 
20510                 dir = e.keyCode == 38 ? -1 : 1;
20511                 
20512                 this.vIndex = this.vIndex + dir * 4;
20513                 
20514                 if(this.vIndex < 0){
20515                     this.vIndex = 0;
20516                 }
20517                 
20518                 if(this.vIndex > 11){
20519                     this.vIndex = 11;
20520                 }
20521                 
20522                 if(isNaN(this.vIndex)){
20523                     this.vIndex = 0;
20524                 }
20525                 
20526                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20527                 break;
20528                 
20529             case 13: // enter
20530                 
20531                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20532                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20533                 }
20534                 
20535                 this.hide();
20536                 e.preventDefault();
20537                 break;
20538             case 9: // tab
20539                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20540                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20541                 }
20542                 this.hide();
20543                 break;
20544             case 16: // shift
20545             case 17: // ctrl
20546             case 18: // alt
20547                 break;
20548             default :
20549                 this.hide();
20550                 
20551         }
20552     },
20553     
20554     remove: function() 
20555     {
20556         this.picker().remove();
20557     }
20558    
20559 });
20560
20561 Roo.apply(Roo.bootstrap.MonthField,  {
20562     
20563     content : {
20564         tag: 'tbody',
20565         cn: [
20566         {
20567             tag: 'tr',
20568             cn: [
20569             {
20570                 tag: 'td',
20571                 colspan: '7'
20572             }
20573             ]
20574         }
20575         ]
20576     },
20577     
20578     dates:{
20579         en: {
20580             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20581             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20582         }
20583     }
20584 });
20585
20586 Roo.apply(Roo.bootstrap.MonthField,  {
20587   
20588     template : {
20589         tag: 'div',
20590         cls: 'datepicker dropdown-menu roo-dynamic',
20591         cn: [
20592             {
20593                 tag: 'div',
20594                 cls: 'datepicker-months',
20595                 cn: [
20596                 {
20597                     tag: 'table',
20598                     cls: 'table-condensed',
20599                     cn:[
20600                         Roo.bootstrap.DateField.content
20601                     ]
20602                 }
20603                 ]
20604             }
20605         ]
20606     }
20607 });
20608
20609  
20610
20611  
20612  /*
20613  * - LGPL
20614  *
20615  * CheckBox
20616  * 
20617  */
20618
20619 /**
20620  * @class Roo.bootstrap.CheckBox
20621  * @extends Roo.bootstrap.Input
20622  * Bootstrap CheckBox class
20623  * 
20624  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20625  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20626  * @cfg {String} boxLabel The text that appears beside the checkbox
20627  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20628  * @cfg {Boolean} checked initnal the element
20629  * @cfg {Boolean} inline inline the element (default false)
20630  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20631  * @cfg {String} tooltip label tooltip
20632  * 
20633  * @constructor
20634  * Create a new CheckBox
20635  * @param {Object} config The config object
20636  */
20637
20638 Roo.bootstrap.CheckBox = function(config){
20639     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20640    
20641     this.addEvents({
20642         /**
20643         * @event check
20644         * Fires when the element is checked or unchecked.
20645         * @param {Roo.bootstrap.CheckBox} this This input
20646         * @param {Boolean} checked The new checked value
20647         */
20648        check : true,
20649        /**
20650         * @event click
20651         * Fires when the element is click.
20652         * @param {Roo.bootstrap.CheckBox} this This input
20653         */
20654        click : true
20655     });
20656     
20657 };
20658
20659 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20660   
20661     inputType: 'checkbox',
20662     inputValue: 1,
20663     valueOff: 0,
20664     boxLabel: false,
20665     checked: false,
20666     weight : false,
20667     inline: false,
20668     tooltip : '',
20669     
20670     getAutoCreate : function()
20671     {
20672         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20673         
20674         var id = Roo.id();
20675         
20676         var cfg = {};
20677         
20678         cfg.cls = 'form-group ' + this.inputType; //input-group
20679         
20680         if(this.inline){
20681             cfg.cls += ' ' + this.inputType + '-inline';
20682         }
20683         
20684         var input =  {
20685             tag: 'input',
20686             id : id,
20687             type : this.inputType,
20688             value : this.inputValue,
20689             cls : 'roo-' + this.inputType, //'form-box',
20690             placeholder : this.placeholder || ''
20691             
20692         };
20693         
20694         if(this.inputType != 'radio'){
20695             var hidden =  {
20696                 tag: 'input',
20697                 type : 'hidden',
20698                 cls : 'roo-hidden-value',
20699                 value : this.checked ? this.inputValue : this.valueOff
20700             };
20701         }
20702         
20703             
20704         if (this.weight) { // Validity check?
20705             cfg.cls += " " + this.inputType + "-" + this.weight;
20706         }
20707         
20708         if (this.disabled) {
20709             input.disabled=true;
20710         }
20711         
20712         if(this.checked){
20713             input.checked = this.checked;
20714         }
20715         
20716         if (this.name) {
20717             
20718             input.name = this.name;
20719             
20720             if(this.inputType != 'radio'){
20721                 hidden.name = this.name;
20722                 input.name = '_hidden_' + this.name;
20723             }
20724         }
20725         
20726         if (this.size) {
20727             input.cls += ' input-' + this.size;
20728         }
20729         
20730         var settings=this;
20731         
20732         ['xs','sm','md','lg'].map(function(size){
20733             if (settings[size]) {
20734                 cfg.cls += ' col-' + size + '-' + settings[size];
20735             }
20736         });
20737         
20738         var inputblock = input;
20739          
20740         if (this.before || this.after) {
20741             
20742             inputblock = {
20743                 cls : 'input-group',
20744                 cn :  [] 
20745             };
20746             
20747             if (this.before) {
20748                 inputblock.cn.push({
20749                     tag :'span',
20750                     cls : 'input-group-addon',
20751                     html : this.before
20752                 });
20753             }
20754             
20755             inputblock.cn.push(input);
20756             
20757             if(this.inputType != 'radio'){
20758                 inputblock.cn.push(hidden);
20759             }
20760             
20761             if (this.after) {
20762                 inputblock.cn.push({
20763                     tag :'span',
20764                     cls : 'input-group-addon',
20765                     html : this.after
20766                 });
20767             }
20768             
20769         }
20770         
20771         if (align ==='left' && this.fieldLabel.length) {
20772 //                Roo.log("left and has label");
20773             cfg.cn = [
20774                 {
20775                     tag: 'label',
20776                     'for' :  id,
20777                     cls : 'control-label',
20778                     html : this.fieldLabel
20779                 },
20780                 {
20781                     cls : "", 
20782                     cn: [
20783                         inputblock
20784                     ]
20785                 }
20786             ];
20787             
20788             if(this.labelWidth > 12){
20789                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20790             }
20791             
20792             if(this.labelWidth < 13 && this.labelmd == 0){
20793                 this.labelmd = this.labelWidth;
20794             }
20795             
20796             if(this.labellg > 0){
20797                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20798                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20799             }
20800             
20801             if(this.labelmd > 0){
20802                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20803                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20804             }
20805             
20806             if(this.labelsm > 0){
20807                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20808                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20809             }
20810             
20811             if(this.labelxs > 0){
20812                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20813                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20814             }
20815             
20816         } else if ( this.fieldLabel.length) {
20817 //                Roo.log(" label");
20818                 cfg.cn = [
20819                    
20820                     {
20821                         tag: this.boxLabel ? 'span' : 'label',
20822                         'for': id,
20823                         cls: 'control-label box-input-label',
20824                         //cls : 'input-group-addon',
20825                         html : this.fieldLabel
20826                     },
20827                     
20828                     inputblock
20829                     
20830                 ];
20831
20832         } else {
20833             
20834 //                Roo.log(" no label && no align");
20835                 cfg.cn = [  inputblock ] ;
20836                 
20837                 
20838         }
20839         
20840         if(this.boxLabel){
20841              var boxLabelCfg = {
20842                 tag: 'label',
20843                 //'for': id, // box label is handled by onclick - so no for...
20844                 cls: 'box-label',
20845                 html: this.boxLabel
20846             };
20847             
20848             if(this.tooltip){
20849                 boxLabelCfg.tooltip = this.tooltip;
20850             }
20851              
20852             cfg.cn.push(boxLabelCfg);
20853         }
20854         
20855         if(this.inputType != 'radio'){
20856             cfg.cn.push(hidden);
20857         }
20858         
20859         return cfg;
20860         
20861     },
20862     
20863     /**
20864      * return the real input element.
20865      */
20866     inputEl: function ()
20867     {
20868         return this.el.select('input.roo-' + this.inputType,true).first();
20869     },
20870     hiddenEl: function ()
20871     {
20872         return this.el.select('input.roo-hidden-value',true).first();
20873     },
20874     
20875     labelEl: function()
20876     {
20877         return this.el.select('label.control-label',true).first();
20878     },
20879     /* depricated... */
20880     
20881     label: function()
20882     {
20883         return this.labelEl();
20884     },
20885     
20886     boxLabelEl: function()
20887     {
20888         return this.el.select('label.box-label',true).first();
20889     },
20890     
20891     initEvents : function()
20892     {
20893 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20894         
20895         this.inputEl().on('click', this.onClick,  this);
20896         
20897         if (this.boxLabel) { 
20898             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20899         }
20900         
20901         this.startValue = this.getValue();
20902         
20903         if(this.groupId){
20904             Roo.bootstrap.CheckBox.register(this);
20905         }
20906     },
20907     
20908     onClick : function(e)
20909     {   
20910         if(this.fireEvent('click', this, e) !== false){
20911             this.setChecked(!this.checked);
20912         }
20913         
20914     },
20915     
20916     setChecked : function(state,suppressEvent)
20917     {
20918         this.startValue = this.getValue();
20919
20920         if(this.inputType == 'radio'){
20921             
20922             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20923                 e.dom.checked = false;
20924             });
20925             
20926             this.inputEl().dom.checked = true;
20927             
20928             this.inputEl().dom.value = this.inputValue;
20929             
20930             if(suppressEvent !== true){
20931                 this.fireEvent('check', this, true);
20932             }
20933             
20934             this.validate();
20935             
20936             return;
20937         }
20938         
20939         this.checked = state;
20940         
20941         this.inputEl().dom.checked = state;
20942         
20943         
20944         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20945         
20946         if(suppressEvent !== true){
20947             this.fireEvent('check', this, state);
20948         }
20949         
20950         this.validate();
20951     },
20952     
20953     getValue : function()
20954     {
20955         if(this.inputType == 'radio'){
20956             return this.getGroupValue();
20957         }
20958         
20959         return this.hiddenEl().dom.value;
20960         
20961     },
20962     
20963     getGroupValue : function()
20964     {
20965         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20966             return '';
20967         }
20968         
20969         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20970     },
20971     
20972     setValue : function(v,suppressEvent)
20973     {
20974         if(this.inputType == 'radio'){
20975             this.setGroupValue(v, suppressEvent);
20976             return;
20977         }
20978         
20979         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20980         
20981         this.validate();
20982     },
20983     
20984     setGroupValue : function(v, suppressEvent)
20985     {
20986         this.startValue = this.getValue();
20987         
20988         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20989             e.dom.checked = false;
20990             
20991             if(e.dom.value == v){
20992                 e.dom.checked = true;
20993             }
20994         });
20995         
20996         if(suppressEvent !== true){
20997             this.fireEvent('check', this, true);
20998         }
20999
21000         this.validate();
21001         
21002         return;
21003     },
21004     
21005     validate : function()
21006     {
21007         if(this.getVisibilityEl().hasClass('hidden')){
21008             return true;
21009         }
21010         
21011         if(
21012                 this.disabled || 
21013                 (this.inputType == 'radio' && this.validateRadio()) ||
21014                 (this.inputType == 'checkbox' && this.validateCheckbox())
21015         ){
21016             this.markValid();
21017             return true;
21018         }
21019         
21020         this.markInvalid();
21021         return false;
21022     },
21023     
21024     validateRadio : function()
21025     {
21026         if(this.getVisibilityEl().hasClass('hidden')){
21027             return true;
21028         }
21029         
21030         if(this.allowBlank){
21031             return true;
21032         }
21033         
21034         var valid = false;
21035         
21036         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21037             if(!e.dom.checked){
21038                 return;
21039             }
21040             
21041             valid = true;
21042             
21043             return false;
21044         });
21045         
21046         return valid;
21047     },
21048     
21049     validateCheckbox : function()
21050     {
21051         if(!this.groupId){
21052             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21053             //return (this.getValue() == this.inputValue) ? true : false;
21054         }
21055         
21056         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21057         
21058         if(!group){
21059             return false;
21060         }
21061         
21062         var r = false;
21063         
21064         for(var i in group){
21065             if(group[i].el.isVisible(true)){
21066                 r = false;
21067                 break;
21068             }
21069             
21070             r = true;
21071         }
21072         
21073         for(var i in group){
21074             if(r){
21075                 break;
21076             }
21077             
21078             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21079         }
21080         
21081         return r;
21082     },
21083     
21084     /**
21085      * Mark this field as valid
21086      */
21087     markValid : function()
21088     {
21089         var _this = this;
21090         
21091         this.fireEvent('valid', this);
21092         
21093         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21094         
21095         if(this.groupId){
21096             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21097         }
21098         
21099         if(label){
21100             label.markValid();
21101         }
21102
21103         if(this.inputType == 'radio'){
21104             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21105                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21106                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21107             });
21108             
21109             return;
21110         }
21111
21112         if(!this.groupId){
21113             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21114             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21115             return;
21116         }
21117         
21118         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21119         
21120         if(!group){
21121             return;
21122         }
21123         
21124         for(var i in group){
21125             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21126             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21127         }
21128     },
21129     
21130      /**
21131      * Mark this field as invalid
21132      * @param {String} msg The validation message
21133      */
21134     markInvalid : function(msg)
21135     {
21136         if(this.allowBlank){
21137             return;
21138         }
21139         
21140         var _this = this;
21141         
21142         this.fireEvent('invalid', this, msg);
21143         
21144         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21145         
21146         if(this.groupId){
21147             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21148         }
21149         
21150         if(label){
21151             label.markInvalid();
21152         }
21153             
21154         if(this.inputType == 'radio'){
21155             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21156                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21157                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21158             });
21159             
21160             return;
21161         }
21162         
21163         if(!this.groupId){
21164             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21165             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21166             return;
21167         }
21168         
21169         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21170         
21171         if(!group){
21172             return;
21173         }
21174         
21175         for(var i in group){
21176             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21177             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21178         }
21179         
21180     },
21181     
21182     clearInvalid : function()
21183     {
21184         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21185         
21186         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21187         
21188         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21189         
21190         if (label && label.iconEl) {
21191             label.iconEl.removeClass(label.validClass);
21192             label.iconEl.removeClass(label.invalidClass);
21193         }
21194     },
21195     
21196     disable : function()
21197     {
21198         if(this.inputType != 'radio'){
21199             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21200             return;
21201         }
21202         
21203         var _this = this;
21204         
21205         if(this.rendered){
21206             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21207                 _this.getActionEl().addClass(this.disabledClass);
21208                 e.dom.disabled = true;
21209             });
21210         }
21211         
21212         this.disabled = true;
21213         this.fireEvent("disable", this);
21214         return this;
21215     },
21216
21217     enable : function()
21218     {
21219         if(this.inputType != 'radio'){
21220             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21221             return;
21222         }
21223         
21224         var _this = this;
21225         
21226         if(this.rendered){
21227             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21228                 _this.getActionEl().removeClass(this.disabledClass);
21229                 e.dom.disabled = false;
21230             });
21231         }
21232         
21233         this.disabled = false;
21234         this.fireEvent("enable", this);
21235         return this;
21236     },
21237     
21238     setBoxLabel : function(v)
21239     {
21240         this.boxLabel = v;
21241         
21242         if(this.rendered){
21243             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21244         }
21245     }
21246
21247 });
21248
21249 Roo.apply(Roo.bootstrap.CheckBox, {
21250     
21251     groups: {},
21252     
21253      /**
21254     * register a CheckBox Group
21255     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21256     */
21257     register : function(checkbox)
21258     {
21259         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21260             this.groups[checkbox.groupId] = {};
21261         }
21262         
21263         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21264             return;
21265         }
21266         
21267         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21268         
21269     },
21270     /**
21271     * fetch a CheckBox Group based on the group ID
21272     * @param {string} the group ID
21273     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21274     */
21275     get: function(groupId) {
21276         if (typeof(this.groups[groupId]) == 'undefined') {
21277             return false;
21278         }
21279         
21280         return this.groups[groupId] ;
21281     }
21282     
21283     
21284 });
21285 /*
21286  * - LGPL
21287  *
21288  * RadioItem
21289  * 
21290  */
21291
21292 /**
21293  * @class Roo.bootstrap.Radio
21294  * @extends Roo.bootstrap.Component
21295  * Bootstrap Radio class
21296  * @cfg {String} boxLabel - the label associated
21297  * @cfg {String} value - the value of radio
21298  * 
21299  * @constructor
21300  * Create a new Radio
21301  * @param {Object} config The config object
21302  */
21303 Roo.bootstrap.Radio = function(config){
21304     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21305     
21306 };
21307
21308 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21309     
21310     boxLabel : '',
21311     
21312     value : '',
21313     
21314     getAutoCreate : function()
21315     {
21316         var cfg = {
21317             tag : 'div',
21318             cls : 'form-group radio',
21319             cn : [
21320                 {
21321                     tag : 'label',
21322                     cls : 'box-label',
21323                     html : this.boxLabel
21324                 }
21325             ]
21326         };
21327         
21328         return cfg;
21329     },
21330     
21331     initEvents : function() 
21332     {
21333         this.parent().register(this);
21334         
21335         this.el.on('click', this.onClick, this);
21336         
21337     },
21338     
21339     onClick : function(e)
21340     {
21341         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21342             this.setChecked(true);
21343         }
21344     },
21345     
21346     setChecked : function(state, suppressEvent)
21347     {
21348         this.parent().setValue(this.value, suppressEvent);
21349         
21350     },
21351     
21352     setBoxLabel : function(v)
21353     {
21354         this.boxLabel = v;
21355         
21356         if(this.rendered){
21357             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21358         }
21359     }
21360     
21361 });
21362  
21363
21364  /*
21365  * - LGPL
21366  *
21367  * Input
21368  * 
21369  */
21370
21371 /**
21372  * @class Roo.bootstrap.SecurePass
21373  * @extends Roo.bootstrap.Input
21374  * Bootstrap SecurePass class
21375  *
21376  * 
21377  * @constructor
21378  * Create a new SecurePass
21379  * @param {Object} config The config object
21380  */
21381  
21382 Roo.bootstrap.SecurePass = function (config) {
21383     // these go here, so the translation tool can replace them..
21384     this.errors = {
21385         PwdEmpty: "Please type a password, and then retype it to confirm.",
21386         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21387         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21388         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21389         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21390         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21391         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21392         TooWeak: "Your password is Too Weak."
21393     },
21394     this.meterLabel = "Password strength:";
21395     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21396     this.meterClass = [
21397         "roo-password-meter-tooweak", 
21398         "roo-password-meter-weak", 
21399         "roo-password-meter-medium", 
21400         "roo-password-meter-strong", 
21401         "roo-password-meter-grey"
21402     ];
21403     
21404     this.errors = {};
21405     
21406     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21407 }
21408
21409 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21410     /**
21411      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21412      * {
21413      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21414      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21415      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21416      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21417      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21418      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21419      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21420      * })
21421      */
21422     // private
21423     
21424     meterWidth: 300,
21425     errorMsg :'',    
21426     errors: false,
21427     imageRoot: '/',
21428     /**
21429      * @cfg {String/Object} Label for the strength meter (defaults to
21430      * 'Password strength:')
21431      */
21432     // private
21433     meterLabel: '',
21434     /**
21435      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21436      * ['Weak', 'Medium', 'Strong'])
21437      */
21438     // private    
21439     pwdStrengths: false,    
21440     // private
21441     strength: 0,
21442     // private
21443     _lastPwd: null,
21444     // private
21445     kCapitalLetter: 0,
21446     kSmallLetter: 1,
21447     kDigit: 2,
21448     kPunctuation: 3,
21449     
21450     insecure: false,
21451     // private
21452     initEvents: function ()
21453     {
21454         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21455
21456         if (this.el.is('input[type=password]') && Roo.isSafari) {
21457             this.el.on('keydown', this.SafariOnKeyDown, this);
21458         }
21459
21460         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21461     },
21462     // private
21463     onRender: function (ct, position)
21464     {
21465         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21466         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21467         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21468
21469         this.trigger.createChild({
21470                    cn: [
21471                     {
21472                     //id: 'PwdMeter',
21473                     tag: 'div',
21474                     cls: 'roo-password-meter-grey col-xs-12',
21475                     style: {
21476                         //width: 0,
21477                         //width: this.meterWidth + 'px'                                                
21478                         }
21479                     },
21480                     {                            
21481                          cls: 'roo-password-meter-text'                          
21482                     }
21483                 ]            
21484         });
21485
21486          
21487         if (this.hideTrigger) {
21488             this.trigger.setDisplayed(false);
21489         }
21490         this.setSize(this.width || '', this.height || '');
21491     },
21492     // private
21493     onDestroy: function ()
21494     {
21495         if (this.trigger) {
21496             this.trigger.removeAllListeners();
21497             this.trigger.remove();
21498         }
21499         if (this.wrap) {
21500             this.wrap.remove();
21501         }
21502         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21503     },
21504     // private
21505     checkStrength: function ()
21506     {
21507         var pwd = this.inputEl().getValue();
21508         if (pwd == this._lastPwd) {
21509             return;
21510         }
21511
21512         var strength;
21513         if (this.ClientSideStrongPassword(pwd)) {
21514             strength = 3;
21515         } else if (this.ClientSideMediumPassword(pwd)) {
21516             strength = 2;
21517         } else if (this.ClientSideWeakPassword(pwd)) {
21518             strength = 1;
21519         } else {
21520             strength = 0;
21521         }
21522         
21523         Roo.log('strength1: ' + strength);
21524         
21525         //var pm = this.trigger.child('div/div/div').dom;
21526         var pm = this.trigger.child('div/div');
21527         pm.removeClass(this.meterClass);
21528         pm.addClass(this.meterClass[strength]);
21529                 
21530         
21531         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21532                 
21533         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21534         
21535         this._lastPwd = pwd;
21536     },
21537     reset: function ()
21538     {
21539         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21540         
21541         this._lastPwd = '';
21542         
21543         var pm = this.trigger.child('div/div');
21544         pm.removeClass(this.meterClass);
21545         pm.addClass('roo-password-meter-grey');        
21546         
21547         
21548         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21549         
21550         pt.innerHTML = '';
21551         this.inputEl().dom.type='password';
21552     },
21553     // private
21554     validateValue: function (value)
21555     {
21556         
21557         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21558             return false;
21559         }
21560         if (value.length == 0) {
21561             if (this.allowBlank) {
21562                 this.clearInvalid();
21563                 return true;
21564             }
21565
21566             this.markInvalid(this.errors.PwdEmpty);
21567             this.errorMsg = this.errors.PwdEmpty;
21568             return false;
21569         }
21570         
21571         if(this.insecure){
21572             return true;
21573         }
21574         
21575         if ('[\x21-\x7e]*'.match(value)) {
21576             this.markInvalid(this.errors.PwdBadChar);
21577             this.errorMsg = this.errors.PwdBadChar;
21578             return false;
21579         }
21580         if (value.length < 6) {
21581             this.markInvalid(this.errors.PwdShort);
21582             this.errorMsg = this.errors.PwdShort;
21583             return false;
21584         }
21585         if (value.length > 16) {
21586             this.markInvalid(this.errors.PwdLong);
21587             this.errorMsg = this.errors.PwdLong;
21588             return false;
21589         }
21590         var strength;
21591         if (this.ClientSideStrongPassword(value)) {
21592             strength = 3;
21593         } else if (this.ClientSideMediumPassword(value)) {
21594             strength = 2;
21595         } else if (this.ClientSideWeakPassword(value)) {
21596             strength = 1;
21597         } else {
21598             strength = 0;
21599         }
21600
21601         
21602         if (strength < 2) {
21603             //this.markInvalid(this.errors.TooWeak);
21604             this.errorMsg = this.errors.TooWeak;
21605             //return false;
21606         }
21607         
21608         
21609         console.log('strength2: ' + strength);
21610         
21611         //var pm = this.trigger.child('div/div/div').dom;
21612         
21613         var pm = this.trigger.child('div/div');
21614         pm.removeClass(this.meterClass);
21615         pm.addClass(this.meterClass[strength]);
21616                 
21617         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21618                 
21619         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21620         
21621         this.errorMsg = ''; 
21622         return true;
21623     },
21624     // private
21625     CharacterSetChecks: function (type)
21626     {
21627         this.type = type;
21628         this.fResult = false;
21629     },
21630     // private
21631     isctype: function (character, type)
21632     {
21633         switch (type) {  
21634             case this.kCapitalLetter:
21635                 if (character >= 'A' && character <= 'Z') {
21636                     return true;
21637                 }
21638                 break;
21639             
21640             case this.kSmallLetter:
21641                 if (character >= 'a' && character <= 'z') {
21642                     return true;
21643                 }
21644                 break;
21645             
21646             case this.kDigit:
21647                 if (character >= '0' && character <= '9') {
21648                     return true;
21649                 }
21650                 break;
21651             
21652             case this.kPunctuation:
21653                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21654                     return true;
21655                 }
21656                 break;
21657             
21658             default:
21659                 return false;
21660         }
21661
21662     },
21663     // private
21664     IsLongEnough: function (pwd, size)
21665     {
21666         return !(pwd == null || isNaN(size) || pwd.length < size);
21667     },
21668     // private
21669     SpansEnoughCharacterSets: function (word, nb)
21670     {
21671         if (!this.IsLongEnough(word, nb))
21672         {
21673             return false;
21674         }
21675
21676         var characterSetChecks = new Array(
21677             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21678             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21679         );
21680         
21681         for (var index = 0; index < word.length; ++index) {
21682             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21683                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21684                     characterSetChecks[nCharSet].fResult = true;
21685                     break;
21686                 }
21687             }
21688         }
21689
21690         var nCharSets = 0;
21691         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21692             if (characterSetChecks[nCharSet].fResult) {
21693                 ++nCharSets;
21694             }
21695         }
21696
21697         if (nCharSets < nb) {
21698             return false;
21699         }
21700         return true;
21701     },
21702     // private
21703     ClientSideStrongPassword: function (pwd)
21704     {
21705         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21706     },
21707     // private
21708     ClientSideMediumPassword: function (pwd)
21709     {
21710         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21711     },
21712     // private
21713     ClientSideWeakPassword: function (pwd)
21714     {
21715         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21716     }
21717           
21718 })//<script type="text/javascript">
21719
21720 /*
21721  * Based  Ext JS Library 1.1.1
21722  * Copyright(c) 2006-2007, Ext JS, LLC.
21723  * LGPL
21724  *
21725  */
21726  
21727 /**
21728  * @class Roo.HtmlEditorCore
21729  * @extends Roo.Component
21730  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21731  *
21732  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21733  */
21734
21735 Roo.HtmlEditorCore = function(config){
21736     
21737     
21738     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21739     
21740     
21741     this.addEvents({
21742         /**
21743          * @event initialize
21744          * Fires when the editor is fully initialized (including the iframe)
21745          * @param {Roo.HtmlEditorCore} this
21746          */
21747         initialize: true,
21748         /**
21749          * @event activate
21750          * Fires when the editor is first receives the focus. Any insertion must wait
21751          * until after this event.
21752          * @param {Roo.HtmlEditorCore} this
21753          */
21754         activate: true,
21755          /**
21756          * @event beforesync
21757          * Fires before the textarea is updated with content from the editor iframe. Return false
21758          * to cancel the sync.
21759          * @param {Roo.HtmlEditorCore} this
21760          * @param {String} html
21761          */
21762         beforesync: true,
21763          /**
21764          * @event beforepush
21765          * Fires before the iframe editor is updated with content from the textarea. Return false
21766          * to cancel the push.
21767          * @param {Roo.HtmlEditorCore} this
21768          * @param {String} html
21769          */
21770         beforepush: true,
21771          /**
21772          * @event sync
21773          * Fires when the textarea is updated with content from the editor iframe.
21774          * @param {Roo.HtmlEditorCore} this
21775          * @param {String} html
21776          */
21777         sync: true,
21778          /**
21779          * @event push
21780          * Fires when the iframe editor is updated with content from the textarea.
21781          * @param {Roo.HtmlEditorCore} this
21782          * @param {String} html
21783          */
21784         push: true,
21785         
21786         /**
21787          * @event editorevent
21788          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21789          * @param {Roo.HtmlEditorCore} this
21790          */
21791         editorevent: true
21792         
21793     });
21794     
21795     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21796     
21797     // defaults : white / black...
21798     this.applyBlacklists();
21799     
21800     
21801     
21802 };
21803
21804
21805 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21806
21807
21808      /**
21809      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21810      */
21811     
21812     owner : false,
21813     
21814      /**
21815      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21816      *                        Roo.resizable.
21817      */
21818     resizable : false,
21819      /**
21820      * @cfg {Number} height (in pixels)
21821      */   
21822     height: 300,
21823    /**
21824      * @cfg {Number} width (in pixels)
21825      */   
21826     width: 500,
21827     
21828     /**
21829      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21830      * 
21831      */
21832     stylesheets: false,
21833     
21834     // id of frame..
21835     frameId: false,
21836     
21837     // private properties
21838     validationEvent : false,
21839     deferHeight: true,
21840     initialized : false,
21841     activated : false,
21842     sourceEditMode : false,
21843     onFocus : Roo.emptyFn,
21844     iframePad:3,
21845     hideMode:'offsets',
21846     
21847     clearUp: true,
21848     
21849     // blacklist + whitelisted elements..
21850     black: false,
21851     white: false,
21852      
21853     bodyCls : '',
21854
21855     /**
21856      * Protected method that will not generally be called directly. It
21857      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21858      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21859      */
21860     getDocMarkup : function(){
21861         // body styles..
21862         var st = '';
21863         
21864         // inherit styels from page...?? 
21865         if (this.stylesheets === false) {
21866             
21867             Roo.get(document.head).select('style').each(function(node) {
21868                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21869             });
21870             
21871             Roo.get(document.head).select('link').each(function(node) { 
21872                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21873             });
21874             
21875         } else if (!this.stylesheets.length) {
21876                 // simple..
21877                 st = '<style type="text/css">' +
21878                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21879                    '</style>';
21880         } else { 
21881             st = '<style type="text/css">' +
21882                     this.stylesheets +
21883                 '</style>';
21884         }
21885         
21886         st +=  '<style type="text/css">' +
21887             'IMG { cursor: pointer } ' +
21888         '</style>';
21889
21890         var cls = 'roo-htmleditor-body';
21891         
21892         if(this.bodyCls.length){
21893             cls += ' ' + this.bodyCls;
21894         }
21895         
21896         return '<html><head>' + st  +
21897             //<style type="text/css">' +
21898             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21899             //'</style>' +
21900             ' </head><body class="' +  cls + '"></body></html>';
21901     },
21902
21903     // private
21904     onRender : function(ct, position)
21905     {
21906         var _t = this;
21907         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21908         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21909         
21910         
21911         this.el.dom.style.border = '0 none';
21912         this.el.dom.setAttribute('tabIndex', -1);
21913         this.el.addClass('x-hidden hide');
21914         
21915         
21916         
21917         if(Roo.isIE){ // fix IE 1px bogus margin
21918             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21919         }
21920        
21921         
21922         this.frameId = Roo.id();
21923         
21924          
21925         
21926         var iframe = this.owner.wrap.createChild({
21927             tag: 'iframe',
21928             cls: 'form-control', // bootstrap..
21929             id: this.frameId,
21930             name: this.frameId,
21931             frameBorder : 'no',
21932             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21933         }, this.el
21934         );
21935         
21936         
21937         this.iframe = iframe.dom;
21938
21939          this.assignDocWin();
21940         
21941         this.doc.designMode = 'on';
21942        
21943         this.doc.open();
21944         this.doc.write(this.getDocMarkup());
21945         this.doc.close();
21946
21947         
21948         var task = { // must defer to wait for browser to be ready
21949             run : function(){
21950                 //console.log("run task?" + this.doc.readyState);
21951                 this.assignDocWin();
21952                 if(this.doc.body || this.doc.readyState == 'complete'){
21953                     try {
21954                         this.doc.designMode="on";
21955                     } catch (e) {
21956                         return;
21957                     }
21958                     Roo.TaskMgr.stop(task);
21959                     this.initEditor.defer(10, this);
21960                 }
21961             },
21962             interval : 10,
21963             duration: 10000,
21964             scope: this
21965         };
21966         Roo.TaskMgr.start(task);
21967
21968     },
21969
21970     // private
21971     onResize : function(w, h)
21972     {
21973          Roo.log('resize: ' +w + ',' + h );
21974         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21975         if(!this.iframe){
21976             return;
21977         }
21978         if(typeof w == 'number'){
21979             
21980             this.iframe.style.width = w + 'px';
21981         }
21982         if(typeof h == 'number'){
21983             
21984             this.iframe.style.height = h + 'px';
21985             if(this.doc){
21986                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21987             }
21988         }
21989         
21990     },
21991
21992     /**
21993      * Toggles the editor between standard and source edit mode.
21994      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21995      */
21996     toggleSourceEdit : function(sourceEditMode){
21997         
21998         this.sourceEditMode = sourceEditMode === true;
21999         
22000         if(this.sourceEditMode){
22001  
22002             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22003             
22004         }else{
22005             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22006             //this.iframe.className = '';
22007             this.deferFocus();
22008         }
22009         //this.setSize(this.owner.wrap.getSize());
22010         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22011     },
22012
22013     
22014   
22015
22016     /**
22017      * Protected method that will not generally be called directly. If you need/want
22018      * custom HTML cleanup, this is the method you should override.
22019      * @param {String} html The HTML to be cleaned
22020      * return {String} The cleaned HTML
22021      */
22022     cleanHtml : function(html){
22023         html = String(html);
22024         if(html.length > 5){
22025             if(Roo.isSafari){ // strip safari nonsense
22026                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22027             }
22028         }
22029         if(html == '&nbsp;'){
22030             html = '';
22031         }
22032         return html;
22033     },
22034
22035     /**
22036      * HTML Editor -> Textarea
22037      * Protected method that will not generally be called directly. Syncs the contents
22038      * of the editor iframe with the textarea.
22039      */
22040     syncValue : function(){
22041         if(this.initialized){
22042             var bd = (this.doc.body || this.doc.documentElement);
22043             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22044             var html = bd.innerHTML;
22045             if(Roo.isSafari){
22046                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22047                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22048                 if(m && m[1]){
22049                     html = '<div style="'+m[0]+'">' + html + '</div>';
22050                 }
22051             }
22052             html = this.cleanHtml(html);
22053             // fix up the special chars.. normaly like back quotes in word...
22054             // however we do not want to do this with chinese..
22055             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22056                 var cc = b.charCodeAt();
22057                 if (
22058                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22059                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22060                     (cc >= 0xf900 && cc < 0xfb00 )
22061                 ) {
22062                         return b;
22063                 }
22064                 return "&#"+cc+";" 
22065             });
22066             if(this.owner.fireEvent('beforesync', this, html) !== false){
22067                 this.el.dom.value = html;
22068                 this.owner.fireEvent('sync', this, html);
22069             }
22070         }
22071     },
22072
22073     /**
22074      * Protected method that will not generally be called directly. Pushes the value of the textarea
22075      * into the iframe editor.
22076      */
22077     pushValue : function(){
22078         if(this.initialized){
22079             var v = this.el.dom.value.trim();
22080             
22081 //            if(v.length < 1){
22082 //                v = '&#160;';
22083 //            }
22084             
22085             if(this.owner.fireEvent('beforepush', this, v) !== false){
22086                 var d = (this.doc.body || this.doc.documentElement);
22087                 d.innerHTML = v;
22088                 this.cleanUpPaste();
22089                 this.el.dom.value = d.innerHTML;
22090                 this.owner.fireEvent('push', this, v);
22091             }
22092         }
22093     },
22094
22095     // private
22096     deferFocus : function(){
22097         this.focus.defer(10, this);
22098     },
22099
22100     // doc'ed in Field
22101     focus : function(){
22102         if(this.win && !this.sourceEditMode){
22103             this.win.focus();
22104         }else{
22105             this.el.focus();
22106         }
22107     },
22108     
22109     assignDocWin: function()
22110     {
22111         var iframe = this.iframe;
22112         
22113          if(Roo.isIE){
22114             this.doc = iframe.contentWindow.document;
22115             this.win = iframe.contentWindow;
22116         } else {
22117 //            if (!Roo.get(this.frameId)) {
22118 //                return;
22119 //            }
22120 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22121 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22122             
22123             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22124                 return;
22125             }
22126             
22127             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22128             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22129         }
22130     },
22131     
22132     // private
22133     initEditor : function(){
22134         //console.log("INIT EDITOR");
22135         this.assignDocWin();
22136         
22137         
22138         
22139         this.doc.designMode="on";
22140         this.doc.open();
22141         this.doc.write(this.getDocMarkup());
22142         this.doc.close();
22143         
22144         var dbody = (this.doc.body || this.doc.documentElement);
22145         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22146         // this copies styles from the containing element into thsi one..
22147         // not sure why we need all of this..
22148         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22149         
22150         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22151         //ss['background-attachment'] = 'fixed'; // w3c
22152         dbody.bgProperties = 'fixed'; // ie
22153         //Roo.DomHelper.applyStyles(dbody, ss);
22154         Roo.EventManager.on(this.doc, {
22155             //'mousedown': this.onEditorEvent,
22156             'mouseup': this.onEditorEvent,
22157             'dblclick': this.onEditorEvent,
22158             'click': this.onEditorEvent,
22159             'keyup': this.onEditorEvent,
22160             buffer:100,
22161             scope: this
22162         });
22163         if(Roo.isGecko){
22164             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22165         }
22166         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22167             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22168         }
22169         this.initialized = true;
22170
22171         this.owner.fireEvent('initialize', this);
22172         this.pushValue();
22173     },
22174
22175     // private
22176     onDestroy : function(){
22177         
22178         
22179         
22180         if(this.rendered){
22181             
22182             //for (var i =0; i < this.toolbars.length;i++) {
22183             //    // fixme - ask toolbars for heights?
22184             //    this.toolbars[i].onDestroy();
22185            // }
22186             
22187             //this.wrap.dom.innerHTML = '';
22188             //this.wrap.remove();
22189         }
22190     },
22191
22192     // private
22193     onFirstFocus : function(){
22194         
22195         this.assignDocWin();
22196         
22197         
22198         this.activated = true;
22199          
22200     
22201         if(Roo.isGecko){ // prevent silly gecko errors
22202             this.win.focus();
22203             var s = this.win.getSelection();
22204             if(!s.focusNode || s.focusNode.nodeType != 3){
22205                 var r = s.getRangeAt(0);
22206                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22207                 r.collapse(true);
22208                 this.deferFocus();
22209             }
22210             try{
22211                 this.execCmd('useCSS', true);
22212                 this.execCmd('styleWithCSS', false);
22213             }catch(e){}
22214         }
22215         this.owner.fireEvent('activate', this);
22216     },
22217
22218     // private
22219     adjustFont: function(btn){
22220         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22221         //if(Roo.isSafari){ // safari
22222         //    adjust *= 2;
22223        // }
22224         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22225         if(Roo.isSafari){ // safari
22226             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22227             v =  (v < 10) ? 10 : v;
22228             v =  (v > 48) ? 48 : v;
22229             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22230             
22231         }
22232         
22233         
22234         v = Math.max(1, v+adjust);
22235         
22236         this.execCmd('FontSize', v  );
22237     },
22238
22239     onEditorEvent : function(e)
22240     {
22241         this.owner.fireEvent('editorevent', this, e);
22242       //  this.updateToolbar();
22243         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22244     },
22245
22246     insertTag : function(tg)
22247     {
22248         // could be a bit smarter... -> wrap the current selected tRoo..
22249         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22250             
22251             range = this.createRange(this.getSelection());
22252             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22253             wrappingNode.appendChild(range.extractContents());
22254             range.insertNode(wrappingNode);
22255
22256             return;
22257             
22258             
22259             
22260         }
22261         this.execCmd("formatblock",   tg);
22262         
22263     },
22264     
22265     insertText : function(txt)
22266     {
22267         
22268         
22269         var range = this.createRange();
22270         range.deleteContents();
22271                //alert(Sender.getAttribute('label'));
22272                
22273         range.insertNode(this.doc.createTextNode(txt));
22274     } ,
22275     
22276      
22277
22278     /**
22279      * Executes a Midas editor command on the editor document and performs necessary focus and
22280      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22281      * @param {String} cmd The Midas command
22282      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22283      */
22284     relayCmd : function(cmd, value){
22285         this.win.focus();
22286         this.execCmd(cmd, value);
22287         this.owner.fireEvent('editorevent', this);
22288         //this.updateToolbar();
22289         this.owner.deferFocus();
22290     },
22291
22292     /**
22293      * Executes a Midas editor command directly on the editor document.
22294      * For visual commands, you should use {@link #relayCmd} instead.
22295      * <b>This should only be called after the editor is initialized.</b>
22296      * @param {String} cmd The Midas command
22297      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22298      */
22299     execCmd : function(cmd, value){
22300         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22301         this.syncValue();
22302     },
22303  
22304  
22305    
22306     /**
22307      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22308      * to insert tRoo.
22309      * @param {String} text | dom node.. 
22310      */
22311     insertAtCursor : function(text)
22312     {
22313         
22314         if(!this.activated){
22315             return;
22316         }
22317         /*
22318         if(Roo.isIE){
22319             this.win.focus();
22320             var r = this.doc.selection.createRange();
22321             if(r){
22322                 r.collapse(true);
22323                 r.pasteHTML(text);
22324                 this.syncValue();
22325                 this.deferFocus();
22326             
22327             }
22328             return;
22329         }
22330         */
22331         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22332             this.win.focus();
22333             
22334             
22335             // from jquery ui (MIT licenced)
22336             var range, node;
22337             var win = this.win;
22338             
22339             if (win.getSelection && win.getSelection().getRangeAt) {
22340                 range = win.getSelection().getRangeAt(0);
22341                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22342                 range.insertNode(node);
22343             } else if (win.document.selection && win.document.selection.createRange) {
22344                 // no firefox support
22345                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22346                 win.document.selection.createRange().pasteHTML(txt);
22347             } else {
22348                 // no firefox support
22349                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22350                 this.execCmd('InsertHTML', txt);
22351             } 
22352             
22353             this.syncValue();
22354             
22355             this.deferFocus();
22356         }
22357     },
22358  // private
22359     mozKeyPress : function(e){
22360         if(e.ctrlKey){
22361             var c = e.getCharCode(), cmd;
22362           
22363             if(c > 0){
22364                 c = String.fromCharCode(c).toLowerCase();
22365                 switch(c){
22366                     case 'b':
22367                         cmd = 'bold';
22368                         break;
22369                     case 'i':
22370                         cmd = 'italic';
22371                         break;
22372                     
22373                     case 'u':
22374                         cmd = 'underline';
22375                         break;
22376                     
22377                     case 'v':
22378                         this.cleanUpPaste.defer(100, this);
22379                         return;
22380                         
22381                 }
22382                 if(cmd){
22383                     this.win.focus();
22384                     this.execCmd(cmd);
22385                     this.deferFocus();
22386                     e.preventDefault();
22387                 }
22388                 
22389             }
22390         }
22391     },
22392
22393     // private
22394     fixKeys : function(){ // load time branching for fastest keydown performance
22395         if(Roo.isIE){
22396             return function(e){
22397                 var k = e.getKey(), r;
22398                 if(k == e.TAB){
22399                     e.stopEvent();
22400                     r = this.doc.selection.createRange();
22401                     if(r){
22402                         r.collapse(true);
22403                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22404                         this.deferFocus();
22405                     }
22406                     return;
22407                 }
22408                 
22409                 if(k == e.ENTER){
22410                     r = this.doc.selection.createRange();
22411                     if(r){
22412                         var target = r.parentElement();
22413                         if(!target || target.tagName.toLowerCase() != 'li'){
22414                             e.stopEvent();
22415                             r.pasteHTML('<br />');
22416                             r.collapse(false);
22417                             r.select();
22418                         }
22419                     }
22420                 }
22421                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22422                     this.cleanUpPaste.defer(100, this);
22423                     return;
22424                 }
22425                 
22426                 
22427             };
22428         }else if(Roo.isOpera){
22429             return function(e){
22430                 var k = e.getKey();
22431                 if(k == e.TAB){
22432                     e.stopEvent();
22433                     this.win.focus();
22434                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22435                     this.deferFocus();
22436                 }
22437                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22438                     this.cleanUpPaste.defer(100, this);
22439                     return;
22440                 }
22441                 
22442             };
22443         }else if(Roo.isSafari){
22444             return function(e){
22445                 var k = e.getKey();
22446                 
22447                 if(k == e.TAB){
22448                     e.stopEvent();
22449                     this.execCmd('InsertText','\t');
22450                     this.deferFocus();
22451                     return;
22452                 }
22453                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22454                     this.cleanUpPaste.defer(100, this);
22455                     return;
22456                 }
22457                 
22458              };
22459         }
22460     }(),
22461     
22462     getAllAncestors: function()
22463     {
22464         var p = this.getSelectedNode();
22465         var a = [];
22466         if (!p) {
22467             a.push(p); // push blank onto stack..
22468             p = this.getParentElement();
22469         }
22470         
22471         
22472         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22473             a.push(p);
22474             p = p.parentNode;
22475         }
22476         a.push(this.doc.body);
22477         return a;
22478     },
22479     lastSel : false,
22480     lastSelNode : false,
22481     
22482     
22483     getSelection : function() 
22484     {
22485         this.assignDocWin();
22486         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22487     },
22488     
22489     getSelectedNode: function() 
22490     {
22491         // this may only work on Gecko!!!
22492         
22493         // should we cache this!!!!
22494         
22495         
22496         
22497          
22498         var range = this.createRange(this.getSelection()).cloneRange();
22499         
22500         if (Roo.isIE) {
22501             var parent = range.parentElement();
22502             while (true) {
22503                 var testRange = range.duplicate();
22504                 testRange.moveToElementText(parent);
22505                 if (testRange.inRange(range)) {
22506                     break;
22507                 }
22508                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22509                     break;
22510                 }
22511                 parent = parent.parentElement;
22512             }
22513             return parent;
22514         }
22515         
22516         // is ancestor a text element.
22517         var ac =  range.commonAncestorContainer;
22518         if (ac.nodeType == 3) {
22519             ac = ac.parentNode;
22520         }
22521         
22522         var ar = ac.childNodes;
22523          
22524         var nodes = [];
22525         var other_nodes = [];
22526         var has_other_nodes = false;
22527         for (var i=0;i<ar.length;i++) {
22528             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22529                 continue;
22530             }
22531             // fullly contained node.
22532             
22533             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22534                 nodes.push(ar[i]);
22535                 continue;
22536             }
22537             
22538             // probably selected..
22539             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22540                 other_nodes.push(ar[i]);
22541                 continue;
22542             }
22543             // outer..
22544             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22545                 continue;
22546             }
22547             
22548             
22549             has_other_nodes = true;
22550         }
22551         if (!nodes.length && other_nodes.length) {
22552             nodes= other_nodes;
22553         }
22554         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22555             return false;
22556         }
22557         
22558         return nodes[0];
22559     },
22560     createRange: function(sel)
22561     {
22562         // this has strange effects when using with 
22563         // top toolbar - not sure if it's a great idea.
22564         //this.editor.contentWindow.focus();
22565         if (typeof sel != "undefined") {
22566             try {
22567                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22568             } catch(e) {
22569                 return this.doc.createRange();
22570             }
22571         } else {
22572             return this.doc.createRange();
22573         }
22574     },
22575     getParentElement: function()
22576     {
22577         
22578         this.assignDocWin();
22579         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22580         
22581         var range = this.createRange(sel);
22582          
22583         try {
22584             var p = range.commonAncestorContainer;
22585             while (p.nodeType == 3) { // text node
22586                 p = p.parentNode;
22587             }
22588             return p;
22589         } catch (e) {
22590             return null;
22591         }
22592     
22593     },
22594     /***
22595      *
22596      * Range intersection.. the hard stuff...
22597      *  '-1' = before
22598      *  '0' = hits..
22599      *  '1' = after.
22600      *         [ -- selected range --- ]
22601      *   [fail]                        [fail]
22602      *
22603      *    basically..
22604      *      if end is before start or  hits it. fail.
22605      *      if start is after end or hits it fail.
22606      *
22607      *   if either hits (but other is outside. - then it's not 
22608      *   
22609      *    
22610      **/
22611     
22612     
22613     // @see http://www.thismuchiknow.co.uk/?p=64.
22614     rangeIntersectsNode : function(range, node)
22615     {
22616         var nodeRange = node.ownerDocument.createRange();
22617         try {
22618             nodeRange.selectNode(node);
22619         } catch (e) {
22620             nodeRange.selectNodeContents(node);
22621         }
22622     
22623         var rangeStartRange = range.cloneRange();
22624         rangeStartRange.collapse(true);
22625     
22626         var rangeEndRange = range.cloneRange();
22627         rangeEndRange.collapse(false);
22628     
22629         var nodeStartRange = nodeRange.cloneRange();
22630         nodeStartRange.collapse(true);
22631     
22632         var nodeEndRange = nodeRange.cloneRange();
22633         nodeEndRange.collapse(false);
22634     
22635         return rangeStartRange.compareBoundaryPoints(
22636                  Range.START_TO_START, nodeEndRange) == -1 &&
22637                rangeEndRange.compareBoundaryPoints(
22638                  Range.START_TO_START, nodeStartRange) == 1;
22639         
22640          
22641     },
22642     rangeCompareNode : function(range, node)
22643     {
22644         var nodeRange = node.ownerDocument.createRange();
22645         try {
22646             nodeRange.selectNode(node);
22647         } catch (e) {
22648             nodeRange.selectNodeContents(node);
22649         }
22650         
22651         
22652         range.collapse(true);
22653     
22654         nodeRange.collapse(true);
22655      
22656         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22657         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22658          
22659         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22660         
22661         var nodeIsBefore   =  ss == 1;
22662         var nodeIsAfter    = ee == -1;
22663         
22664         if (nodeIsBefore && nodeIsAfter) {
22665             return 0; // outer
22666         }
22667         if (!nodeIsBefore && nodeIsAfter) {
22668             return 1; //right trailed.
22669         }
22670         
22671         if (nodeIsBefore && !nodeIsAfter) {
22672             return 2;  // left trailed.
22673         }
22674         // fully contined.
22675         return 3;
22676     },
22677
22678     // private? - in a new class?
22679     cleanUpPaste :  function()
22680     {
22681         // cleans up the whole document..
22682         Roo.log('cleanuppaste');
22683         
22684         this.cleanUpChildren(this.doc.body);
22685         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22686         if (clean != this.doc.body.innerHTML) {
22687             this.doc.body.innerHTML = clean;
22688         }
22689         
22690     },
22691     
22692     cleanWordChars : function(input) {// change the chars to hex code
22693         var he = Roo.HtmlEditorCore;
22694         
22695         var output = input;
22696         Roo.each(he.swapCodes, function(sw) { 
22697             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22698             
22699             output = output.replace(swapper, sw[1]);
22700         });
22701         
22702         return output;
22703     },
22704     
22705     
22706     cleanUpChildren : function (n)
22707     {
22708         if (!n.childNodes.length) {
22709             return;
22710         }
22711         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22712            this.cleanUpChild(n.childNodes[i]);
22713         }
22714     },
22715     
22716     
22717         
22718     
22719     cleanUpChild : function (node)
22720     {
22721         var ed = this;
22722         //console.log(node);
22723         if (node.nodeName == "#text") {
22724             // clean up silly Windows -- stuff?
22725             return; 
22726         }
22727         if (node.nodeName == "#comment") {
22728             node.parentNode.removeChild(node);
22729             // clean up silly Windows -- stuff?
22730             return; 
22731         }
22732         var lcname = node.tagName.toLowerCase();
22733         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22734         // whitelist of tags..
22735         
22736         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22737             // remove node.
22738             node.parentNode.removeChild(node);
22739             return;
22740             
22741         }
22742         
22743         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22744         
22745         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22746         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22747         
22748         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22749         //    remove_keep_children = true;
22750         //}
22751         
22752         if (remove_keep_children) {
22753             this.cleanUpChildren(node);
22754             // inserts everything just before this node...
22755             while (node.childNodes.length) {
22756                 var cn = node.childNodes[0];
22757                 node.removeChild(cn);
22758                 node.parentNode.insertBefore(cn, node);
22759             }
22760             node.parentNode.removeChild(node);
22761             return;
22762         }
22763         
22764         if (!node.attributes || !node.attributes.length) {
22765             this.cleanUpChildren(node);
22766             return;
22767         }
22768         
22769         function cleanAttr(n,v)
22770         {
22771             
22772             if (v.match(/^\./) || v.match(/^\//)) {
22773                 return;
22774             }
22775             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22776                 return;
22777             }
22778             if (v.match(/^#/)) {
22779                 return;
22780             }
22781 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22782             node.removeAttribute(n);
22783             
22784         }
22785         
22786         var cwhite = this.cwhite;
22787         var cblack = this.cblack;
22788             
22789         function cleanStyle(n,v)
22790         {
22791             if (v.match(/expression/)) { //XSS?? should we even bother..
22792                 node.removeAttribute(n);
22793                 return;
22794             }
22795             
22796             var parts = v.split(/;/);
22797             var clean = [];
22798             
22799             Roo.each(parts, function(p) {
22800                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22801                 if (!p.length) {
22802                     return true;
22803                 }
22804                 var l = p.split(':').shift().replace(/\s+/g,'');
22805                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22806                 
22807                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22808 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22809                     //node.removeAttribute(n);
22810                     return true;
22811                 }
22812                 //Roo.log()
22813                 // only allow 'c whitelisted system attributes'
22814                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22815 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22816                     //node.removeAttribute(n);
22817                     return true;
22818                 }
22819                 
22820                 
22821                  
22822                 
22823                 clean.push(p);
22824                 return true;
22825             });
22826             if (clean.length) { 
22827                 node.setAttribute(n, clean.join(';'));
22828             } else {
22829                 node.removeAttribute(n);
22830             }
22831             
22832         }
22833         
22834         
22835         for (var i = node.attributes.length-1; i > -1 ; i--) {
22836             var a = node.attributes[i];
22837             //console.log(a);
22838             
22839             if (a.name.toLowerCase().substr(0,2)=='on')  {
22840                 node.removeAttribute(a.name);
22841                 continue;
22842             }
22843             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22844                 node.removeAttribute(a.name);
22845                 continue;
22846             }
22847             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22848                 cleanAttr(a.name,a.value); // fixme..
22849                 continue;
22850             }
22851             if (a.name == 'style') {
22852                 cleanStyle(a.name,a.value);
22853                 continue;
22854             }
22855             /// clean up MS crap..
22856             // tecnically this should be a list of valid class'es..
22857             
22858             
22859             if (a.name == 'class') {
22860                 if (a.value.match(/^Mso/)) {
22861                     node.className = '';
22862                 }
22863                 
22864                 if (a.value.match(/^body$/)) {
22865                     node.className = '';
22866                 }
22867                 continue;
22868             }
22869             
22870             // style cleanup!?
22871             // class cleanup?
22872             
22873         }
22874         
22875         
22876         this.cleanUpChildren(node);
22877         
22878         
22879     },
22880     
22881     /**
22882      * Clean up MS wordisms...
22883      */
22884     cleanWord : function(node)
22885     {
22886         
22887         
22888         if (!node) {
22889             this.cleanWord(this.doc.body);
22890             return;
22891         }
22892         if (node.nodeName == "#text") {
22893             // clean up silly Windows -- stuff?
22894             return; 
22895         }
22896         if (node.nodeName == "#comment") {
22897             node.parentNode.removeChild(node);
22898             // clean up silly Windows -- stuff?
22899             return; 
22900         }
22901         
22902         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22903             node.parentNode.removeChild(node);
22904             return;
22905         }
22906         
22907         // remove - but keep children..
22908         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22909             while (node.childNodes.length) {
22910                 var cn = node.childNodes[0];
22911                 node.removeChild(cn);
22912                 node.parentNode.insertBefore(cn, node);
22913             }
22914             node.parentNode.removeChild(node);
22915             this.iterateChildren(node, this.cleanWord);
22916             return;
22917         }
22918         // clean styles
22919         if (node.className.length) {
22920             
22921             var cn = node.className.split(/\W+/);
22922             var cna = [];
22923             Roo.each(cn, function(cls) {
22924                 if (cls.match(/Mso[a-zA-Z]+/)) {
22925                     return;
22926                 }
22927                 cna.push(cls);
22928             });
22929             node.className = cna.length ? cna.join(' ') : '';
22930             if (!cna.length) {
22931                 node.removeAttribute("class");
22932             }
22933         }
22934         
22935         if (node.hasAttribute("lang")) {
22936             node.removeAttribute("lang");
22937         }
22938         
22939         if (node.hasAttribute("style")) {
22940             
22941             var styles = node.getAttribute("style").split(";");
22942             var nstyle = [];
22943             Roo.each(styles, function(s) {
22944                 if (!s.match(/:/)) {
22945                     return;
22946                 }
22947                 var kv = s.split(":");
22948                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22949                     return;
22950                 }
22951                 // what ever is left... we allow.
22952                 nstyle.push(s);
22953             });
22954             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22955             if (!nstyle.length) {
22956                 node.removeAttribute('style');
22957             }
22958         }
22959         this.iterateChildren(node, this.cleanWord);
22960         
22961         
22962         
22963     },
22964     /**
22965      * iterateChildren of a Node, calling fn each time, using this as the scole..
22966      * @param {DomNode} node node to iterate children of.
22967      * @param {Function} fn method of this class to call on each item.
22968      */
22969     iterateChildren : function(node, fn)
22970     {
22971         if (!node.childNodes.length) {
22972                 return;
22973         }
22974         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22975            fn.call(this, node.childNodes[i])
22976         }
22977     },
22978     
22979     
22980     /**
22981      * cleanTableWidths.
22982      *
22983      * Quite often pasting from word etc.. results in tables with column and widths.
22984      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22985      *
22986      */
22987     cleanTableWidths : function(node)
22988     {
22989          
22990          
22991         if (!node) {
22992             this.cleanTableWidths(this.doc.body);
22993             return;
22994         }
22995         
22996         // ignore list...
22997         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22998             return; 
22999         }
23000         Roo.log(node.tagName);
23001         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23002             this.iterateChildren(node, this.cleanTableWidths);
23003             return;
23004         }
23005         if (node.hasAttribute('width')) {
23006             node.removeAttribute('width');
23007         }
23008         
23009          
23010         if (node.hasAttribute("style")) {
23011             // pretty basic...
23012             
23013             var styles = node.getAttribute("style").split(";");
23014             var nstyle = [];
23015             Roo.each(styles, function(s) {
23016                 if (!s.match(/:/)) {
23017                     return;
23018                 }
23019                 var kv = s.split(":");
23020                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23021                     return;
23022                 }
23023                 // what ever is left... we allow.
23024                 nstyle.push(s);
23025             });
23026             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23027             if (!nstyle.length) {
23028                 node.removeAttribute('style');
23029             }
23030         }
23031         
23032         this.iterateChildren(node, this.cleanTableWidths);
23033         
23034         
23035     },
23036     
23037     
23038     
23039     
23040     domToHTML : function(currentElement, depth, nopadtext) {
23041         
23042         depth = depth || 0;
23043         nopadtext = nopadtext || false;
23044     
23045         if (!currentElement) {
23046             return this.domToHTML(this.doc.body);
23047         }
23048         
23049         //Roo.log(currentElement);
23050         var j;
23051         var allText = false;
23052         var nodeName = currentElement.nodeName;
23053         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23054         
23055         if  (nodeName == '#text') {
23056             
23057             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23058         }
23059         
23060         
23061         var ret = '';
23062         if (nodeName != 'BODY') {
23063              
23064             var i = 0;
23065             // Prints the node tagName, such as <A>, <IMG>, etc
23066             if (tagName) {
23067                 var attr = [];
23068                 for(i = 0; i < currentElement.attributes.length;i++) {
23069                     // quoting?
23070                     var aname = currentElement.attributes.item(i).name;
23071                     if (!currentElement.attributes.item(i).value.length) {
23072                         continue;
23073                     }
23074                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23075                 }
23076                 
23077                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23078             } 
23079             else {
23080                 
23081                 // eack
23082             }
23083         } else {
23084             tagName = false;
23085         }
23086         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23087             return ret;
23088         }
23089         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23090             nopadtext = true;
23091         }
23092         
23093         
23094         // Traverse the tree
23095         i = 0;
23096         var currentElementChild = currentElement.childNodes.item(i);
23097         var allText = true;
23098         var innerHTML  = '';
23099         lastnode = '';
23100         while (currentElementChild) {
23101             // Formatting code (indent the tree so it looks nice on the screen)
23102             var nopad = nopadtext;
23103             if (lastnode == 'SPAN') {
23104                 nopad  = true;
23105             }
23106             // text
23107             if  (currentElementChild.nodeName == '#text') {
23108                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23109                 toadd = nopadtext ? toadd : toadd.trim();
23110                 if (!nopad && toadd.length > 80) {
23111                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23112                 }
23113                 innerHTML  += toadd;
23114                 
23115                 i++;
23116                 currentElementChild = currentElement.childNodes.item(i);
23117                 lastNode = '';
23118                 continue;
23119             }
23120             allText = false;
23121             
23122             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23123                 
23124             // Recursively traverse the tree structure of the child node
23125             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23126             lastnode = currentElementChild.nodeName;
23127             i++;
23128             currentElementChild=currentElement.childNodes.item(i);
23129         }
23130         
23131         ret += innerHTML;
23132         
23133         if (!allText) {
23134                 // The remaining code is mostly for formatting the tree
23135             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23136         }
23137         
23138         
23139         if (tagName) {
23140             ret+= "</"+tagName+">";
23141         }
23142         return ret;
23143         
23144     },
23145         
23146     applyBlacklists : function()
23147     {
23148         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23149         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23150         
23151         this.white = [];
23152         this.black = [];
23153         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23154             if (b.indexOf(tag) > -1) {
23155                 return;
23156             }
23157             this.white.push(tag);
23158             
23159         }, this);
23160         
23161         Roo.each(w, function(tag) {
23162             if (b.indexOf(tag) > -1) {
23163                 return;
23164             }
23165             if (this.white.indexOf(tag) > -1) {
23166                 return;
23167             }
23168             this.white.push(tag);
23169             
23170         }, this);
23171         
23172         
23173         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23174             if (w.indexOf(tag) > -1) {
23175                 return;
23176             }
23177             this.black.push(tag);
23178             
23179         }, this);
23180         
23181         Roo.each(b, function(tag) {
23182             if (w.indexOf(tag) > -1) {
23183                 return;
23184             }
23185             if (this.black.indexOf(tag) > -1) {
23186                 return;
23187             }
23188             this.black.push(tag);
23189             
23190         }, this);
23191         
23192         
23193         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23194         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23195         
23196         this.cwhite = [];
23197         this.cblack = [];
23198         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23199             if (b.indexOf(tag) > -1) {
23200                 return;
23201             }
23202             this.cwhite.push(tag);
23203             
23204         }, this);
23205         
23206         Roo.each(w, function(tag) {
23207             if (b.indexOf(tag) > -1) {
23208                 return;
23209             }
23210             if (this.cwhite.indexOf(tag) > -1) {
23211                 return;
23212             }
23213             this.cwhite.push(tag);
23214             
23215         }, this);
23216         
23217         
23218         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23219             if (w.indexOf(tag) > -1) {
23220                 return;
23221             }
23222             this.cblack.push(tag);
23223             
23224         }, this);
23225         
23226         Roo.each(b, function(tag) {
23227             if (w.indexOf(tag) > -1) {
23228                 return;
23229             }
23230             if (this.cblack.indexOf(tag) > -1) {
23231                 return;
23232             }
23233             this.cblack.push(tag);
23234             
23235         }, this);
23236     },
23237     
23238     setStylesheets : function(stylesheets)
23239     {
23240         if(typeof(stylesheets) == 'string'){
23241             Roo.get(this.iframe.contentDocument.head).createChild({
23242                 tag : 'link',
23243                 rel : 'stylesheet',
23244                 type : 'text/css',
23245                 href : stylesheets
23246             });
23247             
23248             return;
23249         }
23250         var _this = this;
23251      
23252         Roo.each(stylesheets, function(s) {
23253             if(!s.length){
23254                 return;
23255             }
23256             
23257             Roo.get(_this.iframe.contentDocument.head).createChild({
23258                 tag : 'link',
23259                 rel : 'stylesheet',
23260                 type : 'text/css',
23261                 href : s
23262             });
23263         });
23264
23265         
23266     },
23267     
23268     removeStylesheets : function()
23269     {
23270         var _this = this;
23271         
23272         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23273             s.remove();
23274         });
23275     },
23276     
23277     setStyle : function(style)
23278     {
23279         Roo.get(this.iframe.contentDocument.head).createChild({
23280             tag : 'style',
23281             type : 'text/css',
23282             html : style
23283         });
23284
23285         return;
23286     }
23287     
23288     // hide stuff that is not compatible
23289     /**
23290      * @event blur
23291      * @hide
23292      */
23293     /**
23294      * @event change
23295      * @hide
23296      */
23297     /**
23298      * @event focus
23299      * @hide
23300      */
23301     /**
23302      * @event specialkey
23303      * @hide
23304      */
23305     /**
23306      * @cfg {String} fieldClass @hide
23307      */
23308     /**
23309      * @cfg {String} focusClass @hide
23310      */
23311     /**
23312      * @cfg {String} autoCreate @hide
23313      */
23314     /**
23315      * @cfg {String} inputType @hide
23316      */
23317     /**
23318      * @cfg {String} invalidClass @hide
23319      */
23320     /**
23321      * @cfg {String} invalidText @hide
23322      */
23323     /**
23324      * @cfg {String} msgFx @hide
23325      */
23326     /**
23327      * @cfg {String} validateOnBlur @hide
23328      */
23329 });
23330
23331 Roo.HtmlEditorCore.white = [
23332         'area', 'br', 'img', 'input', 'hr', 'wbr',
23333         
23334        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23335        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23336        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23337        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23338        'table',   'ul',         'xmp', 
23339        
23340        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23341       'thead',   'tr', 
23342      
23343       'dir', 'menu', 'ol', 'ul', 'dl',
23344        
23345       'embed',  'object'
23346 ];
23347
23348
23349 Roo.HtmlEditorCore.black = [
23350     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23351         'applet', // 
23352         'base',   'basefont', 'bgsound', 'blink',  'body', 
23353         'frame',  'frameset', 'head',    'html',   'ilayer', 
23354         'iframe', 'layer',  'link',     'meta',    'object',   
23355         'script', 'style' ,'title',  'xml' // clean later..
23356 ];
23357 Roo.HtmlEditorCore.clean = [
23358     'script', 'style', 'title', 'xml'
23359 ];
23360 Roo.HtmlEditorCore.remove = [
23361     'font'
23362 ];
23363 // attributes..
23364
23365 Roo.HtmlEditorCore.ablack = [
23366     'on'
23367 ];
23368     
23369 Roo.HtmlEditorCore.aclean = [ 
23370     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23371 ];
23372
23373 // protocols..
23374 Roo.HtmlEditorCore.pwhite= [
23375         'http',  'https',  'mailto'
23376 ];
23377
23378 // white listed style attributes.
23379 Roo.HtmlEditorCore.cwhite= [
23380       //  'text-align', /// default is to allow most things..
23381       
23382          
23383 //        'font-size'//??
23384 ];
23385
23386 // black listed style attributes.
23387 Roo.HtmlEditorCore.cblack= [
23388       //  'font-size' -- this can be set by the project 
23389 ];
23390
23391
23392 Roo.HtmlEditorCore.swapCodes   =[ 
23393     [    8211, "--" ], 
23394     [    8212, "--" ], 
23395     [    8216,  "'" ],  
23396     [    8217, "'" ],  
23397     [    8220, '"' ],  
23398     [    8221, '"' ],  
23399     [    8226, "*" ],  
23400     [    8230, "..." ]
23401 ]; 
23402
23403     /*
23404  * - LGPL
23405  *
23406  * HtmlEditor
23407  * 
23408  */
23409
23410 /**
23411  * @class Roo.bootstrap.HtmlEditor
23412  * @extends Roo.bootstrap.TextArea
23413  * Bootstrap HtmlEditor class
23414
23415  * @constructor
23416  * Create a new HtmlEditor
23417  * @param {Object} config The config object
23418  */
23419
23420 Roo.bootstrap.HtmlEditor = function(config){
23421     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23422     if (!this.toolbars) {
23423         this.toolbars = [];
23424     }
23425     
23426     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23427     this.addEvents({
23428             /**
23429              * @event initialize
23430              * Fires when the editor is fully initialized (including the iframe)
23431              * @param {HtmlEditor} this
23432              */
23433             initialize: true,
23434             /**
23435              * @event activate
23436              * Fires when the editor is first receives the focus. Any insertion must wait
23437              * until after this event.
23438              * @param {HtmlEditor} this
23439              */
23440             activate: true,
23441              /**
23442              * @event beforesync
23443              * Fires before the textarea is updated with content from the editor iframe. Return false
23444              * to cancel the sync.
23445              * @param {HtmlEditor} this
23446              * @param {String} html
23447              */
23448             beforesync: true,
23449              /**
23450              * @event beforepush
23451              * Fires before the iframe editor is updated with content from the textarea. Return false
23452              * to cancel the push.
23453              * @param {HtmlEditor} this
23454              * @param {String} html
23455              */
23456             beforepush: true,
23457              /**
23458              * @event sync
23459              * Fires when the textarea is updated with content from the editor iframe.
23460              * @param {HtmlEditor} this
23461              * @param {String} html
23462              */
23463             sync: true,
23464              /**
23465              * @event push
23466              * Fires when the iframe editor is updated with content from the textarea.
23467              * @param {HtmlEditor} this
23468              * @param {String} html
23469              */
23470             push: true,
23471              /**
23472              * @event editmodechange
23473              * Fires when the editor switches edit modes
23474              * @param {HtmlEditor} this
23475              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23476              */
23477             editmodechange: true,
23478             /**
23479              * @event editorevent
23480              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23481              * @param {HtmlEditor} this
23482              */
23483             editorevent: true,
23484             /**
23485              * @event firstfocus
23486              * Fires when on first focus - needed by toolbars..
23487              * @param {HtmlEditor} this
23488              */
23489             firstfocus: true,
23490             /**
23491              * @event autosave
23492              * Auto save the htmlEditor value as a file into Events
23493              * @param {HtmlEditor} this
23494              */
23495             autosave: true,
23496             /**
23497              * @event savedpreview
23498              * preview the saved version of htmlEditor
23499              * @param {HtmlEditor} this
23500              */
23501             savedpreview: true
23502         });
23503 };
23504
23505
23506 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23507     
23508     
23509       /**
23510      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23511      */
23512     toolbars : false,
23513     
23514      /**
23515     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23516     */
23517     btns : [],
23518    
23519      /**
23520      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23521      *                        Roo.resizable.
23522      */
23523     resizable : false,
23524      /**
23525      * @cfg {Number} height (in pixels)
23526      */   
23527     height: 300,
23528    /**
23529      * @cfg {Number} width (in pixels)
23530      */   
23531     width: false,
23532     
23533     /**
23534      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23535      * 
23536      */
23537     stylesheets: false,
23538     
23539     // id of frame..
23540     frameId: false,
23541     
23542     // private properties
23543     validationEvent : false,
23544     deferHeight: true,
23545     initialized : false,
23546     activated : false,
23547     
23548     onFocus : Roo.emptyFn,
23549     iframePad:3,
23550     hideMode:'offsets',
23551     
23552     tbContainer : false,
23553     
23554     bodyCls : '',
23555     
23556     toolbarContainer :function() {
23557         return this.wrap.select('.x-html-editor-tb',true).first();
23558     },
23559
23560     /**
23561      * Protected method that will not generally be called directly. It
23562      * is called when the editor creates its toolbar. Override this method if you need to
23563      * add custom toolbar buttons.
23564      * @param {HtmlEditor} editor
23565      */
23566     createToolbar : function(){
23567         Roo.log('renewing');
23568         Roo.log("create toolbars");
23569         
23570         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23571         this.toolbars[0].render(this.toolbarContainer());
23572         
23573         return;
23574         
23575 //        if (!editor.toolbars || !editor.toolbars.length) {
23576 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23577 //        }
23578 //        
23579 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23580 //            editor.toolbars[i] = Roo.factory(
23581 //                    typeof(editor.toolbars[i]) == 'string' ?
23582 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23583 //                Roo.bootstrap.HtmlEditor);
23584 //            editor.toolbars[i].init(editor);
23585 //        }
23586     },
23587
23588      
23589     // private
23590     onRender : function(ct, position)
23591     {
23592        // Roo.log("Call onRender: " + this.xtype);
23593         var _t = this;
23594         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23595       
23596         this.wrap = this.inputEl().wrap({
23597             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23598         });
23599         
23600         this.editorcore.onRender(ct, position);
23601          
23602         if (this.resizable) {
23603             this.resizeEl = new Roo.Resizable(this.wrap, {
23604                 pinned : true,
23605                 wrap: true,
23606                 dynamic : true,
23607                 minHeight : this.height,
23608                 height: this.height,
23609                 handles : this.resizable,
23610                 width: this.width,
23611                 listeners : {
23612                     resize : function(r, w, h) {
23613                         _t.onResize(w,h); // -something
23614                     }
23615                 }
23616             });
23617             
23618         }
23619         this.createToolbar(this);
23620        
23621         
23622         if(!this.width && this.resizable){
23623             this.setSize(this.wrap.getSize());
23624         }
23625         if (this.resizeEl) {
23626             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23627             // should trigger onReize..
23628         }
23629         
23630     },
23631
23632     // private
23633     onResize : function(w, h)
23634     {
23635         Roo.log('resize: ' +w + ',' + h );
23636         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23637         var ew = false;
23638         var eh = false;
23639         
23640         if(this.inputEl() ){
23641             if(typeof w == 'number'){
23642                 var aw = w - this.wrap.getFrameWidth('lr');
23643                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23644                 ew = aw;
23645             }
23646             if(typeof h == 'number'){
23647                  var tbh = -11;  // fixme it needs to tool bar size!
23648                 for (var i =0; i < this.toolbars.length;i++) {
23649                     // fixme - ask toolbars for heights?
23650                     tbh += this.toolbars[i].el.getHeight();
23651                     //if (this.toolbars[i].footer) {
23652                     //    tbh += this.toolbars[i].footer.el.getHeight();
23653                     //}
23654                 }
23655               
23656                 
23657                 
23658                 
23659                 
23660                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23661                 ah -= 5; // knock a few pixes off for look..
23662                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23663                 var eh = ah;
23664             }
23665         }
23666         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23667         this.editorcore.onResize(ew,eh);
23668         
23669     },
23670
23671     /**
23672      * Toggles the editor between standard and source edit mode.
23673      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23674      */
23675     toggleSourceEdit : function(sourceEditMode)
23676     {
23677         this.editorcore.toggleSourceEdit(sourceEditMode);
23678         
23679         if(this.editorcore.sourceEditMode){
23680             Roo.log('editor - showing textarea');
23681             
23682 //            Roo.log('in');
23683 //            Roo.log(this.syncValue());
23684             this.syncValue();
23685             this.inputEl().removeClass(['hide', 'x-hidden']);
23686             this.inputEl().dom.removeAttribute('tabIndex');
23687             this.inputEl().focus();
23688         }else{
23689             Roo.log('editor - hiding textarea');
23690 //            Roo.log('out')
23691 //            Roo.log(this.pushValue()); 
23692             this.pushValue();
23693             
23694             this.inputEl().addClass(['hide', 'x-hidden']);
23695             this.inputEl().dom.setAttribute('tabIndex', -1);
23696             //this.deferFocus();
23697         }
23698          
23699         if(this.resizable){
23700             this.setSize(this.wrap.getSize());
23701         }
23702         
23703         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23704     },
23705  
23706     // private (for BoxComponent)
23707     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23708
23709     // private (for BoxComponent)
23710     getResizeEl : function(){
23711         return this.wrap;
23712     },
23713
23714     // private (for BoxComponent)
23715     getPositionEl : function(){
23716         return this.wrap;
23717     },
23718
23719     // private
23720     initEvents : function(){
23721         this.originalValue = this.getValue();
23722     },
23723
23724 //    /**
23725 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23726 //     * @method
23727 //     */
23728 //    markInvalid : Roo.emptyFn,
23729 //    /**
23730 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23731 //     * @method
23732 //     */
23733 //    clearInvalid : Roo.emptyFn,
23734
23735     setValue : function(v){
23736         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23737         this.editorcore.pushValue();
23738     },
23739
23740      
23741     // private
23742     deferFocus : function(){
23743         this.focus.defer(10, this);
23744     },
23745
23746     // doc'ed in Field
23747     focus : function(){
23748         this.editorcore.focus();
23749         
23750     },
23751       
23752
23753     // private
23754     onDestroy : function(){
23755         
23756         
23757         
23758         if(this.rendered){
23759             
23760             for (var i =0; i < this.toolbars.length;i++) {
23761                 // fixme - ask toolbars for heights?
23762                 this.toolbars[i].onDestroy();
23763             }
23764             
23765             this.wrap.dom.innerHTML = '';
23766             this.wrap.remove();
23767         }
23768     },
23769
23770     // private
23771     onFirstFocus : function(){
23772         //Roo.log("onFirstFocus");
23773         this.editorcore.onFirstFocus();
23774          for (var i =0; i < this.toolbars.length;i++) {
23775             this.toolbars[i].onFirstFocus();
23776         }
23777         
23778     },
23779     
23780     // private
23781     syncValue : function()
23782     {   
23783         this.editorcore.syncValue();
23784     },
23785     
23786     pushValue : function()
23787     {   
23788         this.editorcore.pushValue();
23789     }
23790      
23791     
23792     // hide stuff that is not compatible
23793     /**
23794      * @event blur
23795      * @hide
23796      */
23797     /**
23798      * @event change
23799      * @hide
23800      */
23801     /**
23802      * @event focus
23803      * @hide
23804      */
23805     /**
23806      * @event specialkey
23807      * @hide
23808      */
23809     /**
23810      * @cfg {String} fieldClass @hide
23811      */
23812     /**
23813      * @cfg {String} focusClass @hide
23814      */
23815     /**
23816      * @cfg {String} autoCreate @hide
23817      */
23818     /**
23819      * @cfg {String} inputType @hide
23820      */
23821     /**
23822      * @cfg {String} invalidClass @hide
23823      */
23824     /**
23825      * @cfg {String} invalidText @hide
23826      */
23827     /**
23828      * @cfg {String} msgFx @hide
23829      */
23830     /**
23831      * @cfg {String} validateOnBlur @hide
23832      */
23833 });
23834  
23835     
23836    
23837    
23838    
23839       
23840 Roo.namespace('Roo.bootstrap.htmleditor');
23841 /**
23842  * @class Roo.bootstrap.HtmlEditorToolbar1
23843  * Basic Toolbar
23844  * 
23845  * Usage:
23846  *
23847  new Roo.bootstrap.HtmlEditor({
23848     ....
23849     toolbars : [
23850         new Roo.bootstrap.HtmlEditorToolbar1({
23851             disable : { fonts: 1 , format: 1, ..., ... , ...],
23852             btns : [ .... ]
23853         })
23854     }
23855      
23856  * 
23857  * @cfg {Object} disable List of elements to disable..
23858  * @cfg {Array} btns List of additional buttons.
23859  * 
23860  * 
23861  * NEEDS Extra CSS? 
23862  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23863  */
23864  
23865 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23866 {
23867     
23868     Roo.apply(this, config);
23869     
23870     // default disabled, based on 'good practice'..
23871     this.disable = this.disable || {};
23872     Roo.applyIf(this.disable, {
23873         fontSize : true,
23874         colors : true,
23875         specialElements : true
23876     });
23877     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23878     
23879     this.editor = config.editor;
23880     this.editorcore = config.editor.editorcore;
23881     
23882     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23883     
23884     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23885     // dont call parent... till later.
23886 }
23887 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23888      
23889     bar : true,
23890     
23891     editor : false,
23892     editorcore : false,
23893     
23894     
23895     formats : [
23896         "p" ,  
23897         "h1","h2","h3","h4","h5","h6", 
23898         "pre", "code", 
23899         "abbr", "acronym", "address", "cite", "samp", "var",
23900         'div','span'
23901     ],
23902     
23903     onRender : function(ct, position)
23904     {
23905        // Roo.log("Call onRender: " + this.xtype);
23906         
23907        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23908        Roo.log(this.el);
23909        this.el.dom.style.marginBottom = '0';
23910        var _this = this;
23911        var editorcore = this.editorcore;
23912        var editor= this.editor;
23913        
23914        var children = [];
23915        var btn = function(id,cmd , toggle, handler, html){
23916        
23917             var  event = toggle ? 'toggle' : 'click';
23918        
23919             var a = {
23920                 size : 'sm',
23921                 xtype: 'Button',
23922                 xns: Roo.bootstrap,
23923                 glyphicon : id,
23924                 cmd : id || cmd,
23925                 enableToggle:toggle !== false,
23926                 html : html || '',
23927                 pressed : toggle ? false : null,
23928                 listeners : {}
23929             };
23930             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23931                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23932             };
23933             children.push(a);
23934             return a;
23935        }
23936        
23937     //    var cb_box = function...
23938         
23939         var style = {
23940                 xtype: 'Button',
23941                 size : 'sm',
23942                 xns: Roo.bootstrap,
23943                 glyphicon : 'font',
23944                 //html : 'submit'
23945                 menu : {
23946                     xtype: 'Menu',
23947                     xns: Roo.bootstrap,
23948                     items:  []
23949                 }
23950         };
23951         Roo.each(this.formats, function(f) {
23952             style.menu.items.push({
23953                 xtype :'MenuItem',
23954                 xns: Roo.bootstrap,
23955                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23956                 tagname : f,
23957                 listeners : {
23958                     click : function()
23959                     {
23960                         editorcore.insertTag(this.tagname);
23961                         editor.focus();
23962                     }
23963                 }
23964                 
23965             });
23966         });
23967         children.push(style);   
23968         
23969         btn('bold',false,true);
23970         btn('italic',false,true);
23971         btn('align-left', 'justifyleft',true);
23972         btn('align-center', 'justifycenter',true);
23973         btn('align-right' , 'justifyright',true);
23974         btn('link', false, false, function(btn) {
23975             //Roo.log("create link?");
23976             var url = prompt(this.createLinkText, this.defaultLinkValue);
23977             if(url && url != 'http:/'+'/'){
23978                 this.editorcore.relayCmd('createlink', url);
23979             }
23980         }),
23981         btn('list','insertunorderedlist',true);
23982         btn('pencil', false,true, function(btn){
23983                 Roo.log(this);
23984                 this.toggleSourceEdit(btn.pressed);
23985         });
23986         
23987         if (this.editor.btns.length > 0) {
23988             for (var i = 0; i<this.editor.btns.length; i++) {
23989                 children.push(this.editor.btns[i]);
23990             }
23991         }
23992         
23993         /*
23994         var cog = {
23995                 xtype: 'Button',
23996                 size : 'sm',
23997                 xns: Roo.bootstrap,
23998                 glyphicon : 'cog',
23999                 //html : 'submit'
24000                 menu : {
24001                     xtype: 'Menu',
24002                     xns: Roo.bootstrap,
24003                     items:  []
24004                 }
24005         };
24006         
24007         cog.menu.items.push({
24008             xtype :'MenuItem',
24009             xns: Roo.bootstrap,
24010             html : Clean styles,
24011             tagname : f,
24012             listeners : {
24013                 click : function()
24014                 {
24015                     editorcore.insertTag(this.tagname);
24016                     editor.focus();
24017                 }
24018             }
24019             
24020         });
24021        */
24022         
24023          
24024        this.xtype = 'NavSimplebar';
24025         
24026         for(var i=0;i< children.length;i++) {
24027             
24028             this.buttons.add(this.addxtypeChild(children[i]));
24029             
24030         }
24031         
24032         editor.on('editorevent', this.updateToolbar, this);
24033     },
24034     onBtnClick : function(id)
24035     {
24036        this.editorcore.relayCmd(id);
24037        this.editorcore.focus();
24038     },
24039     
24040     /**
24041      * Protected method that will not generally be called directly. It triggers
24042      * a toolbar update by reading the markup state of the current selection in the editor.
24043      */
24044     updateToolbar: function(){
24045
24046         if(!this.editorcore.activated){
24047             this.editor.onFirstFocus(); // is this neeed?
24048             return;
24049         }
24050
24051         var btns = this.buttons; 
24052         var doc = this.editorcore.doc;
24053         btns.get('bold').setActive(doc.queryCommandState('bold'));
24054         btns.get('italic').setActive(doc.queryCommandState('italic'));
24055         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24056         
24057         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24058         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24059         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24060         
24061         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24062         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24063          /*
24064         
24065         var ans = this.editorcore.getAllAncestors();
24066         if (this.formatCombo) {
24067             
24068             
24069             var store = this.formatCombo.store;
24070             this.formatCombo.setValue("");
24071             for (var i =0; i < ans.length;i++) {
24072                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24073                     // select it..
24074                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24075                     break;
24076                 }
24077             }
24078         }
24079         
24080         
24081         
24082         // hides menus... - so this cant be on a menu...
24083         Roo.bootstrap.MenuMgr.hideAll();
24084         */
24085         Roo.bootstrap.MenuMgr.hideAll();
24086         //this.editorsyncValue();
24087     },
24088     onFirstFocus: function() {
24089         this.buttons.each(function(item){
24090            item.enable();
24091         });
24092     },
24093     toggleSourceEdit : function(sourceEditMode){
24094         
24095           
24096         if(sourceEditMode){
24097             Roo.log("disabling buttons");
24098            this.buttons.each( function(item){
24099                 if(item.cmd != 'pencil'){
24100                     item.disable();
24101                 }
24102             });
24103           
24104         }else{
24105             Roo.log("enabling buttons");
24106             if(this.editorcore.initialized){
24107                 this.buttons.each( function(item){
24108                     item.enable();
24109                 });
24110             }
24111             
24112         }
24113         Roo.log("calling toggole on editor");
24114         // tell the editor that it's been pressed..
24115         this.editor.toggleSourceEdit(sourceEditMode);
24116        
24117     }
24118 });
24119
24120
24121
24122
24123
24124 /**
24125  * @class Roo.bootstrap.Table.AbstractSelectionModel
24126  * @extends Roo.util.Observable
24127  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24128  * implemented by descendant classes.  This class should not be directly instantiated.
24129  * @constructor
24130  */
24131 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24132     this.locked = false;
24133     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24134 };
24135
24136
24137 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24138     /** @ignore Called by the grid automatically. Do not call directly. */
24139     init : function(grid){
24140         this.grid = grid;
24141         this.initEvents();
24142     },
24143
24144     /**
24145      * Locks the selections.
24146      */
24147     lock : function(){
24148         this.locked = true;
24149     },
24150
24151     /**
24152      * Unlocks the selections.
24153      */
24154     unlock : function(){
24155         this.locked = false;
24156     },
24157
24158     /**
24159      * Returns true if the selections are locked.
24160      * @return {Boolean}
24161      */
24162     isLocked : function(){
24163         return this.locked;
24164     }
24165 });
24166 /**
24167  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24168  * @class Roo.bootstrap.Table.RowSelectionModel
24169  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24170  * It supports multiple selections and keyboard selection/navigation. 
24171  * @constructor
24172  * @param {Object} config
24173  */
24174
24175 Roo.bootstrap.Table.RowSelectionModel = function(config){
24176     Roo.apply(this, config);
24177     this.selections = new Roo.util.MixedCollection(false, function(o){
24178         return o.id;
24179     });
24180
24181     this.last = false;
24182     this.lastActive = false;
24183
24184     this.addEvents({
24185         /**
24186              * @event selectionchange
24187              * Fires when the selection changes
24188              * @param {SelectionModel} this
24189              */
24190             "selectionchange" : true,
24191         /**
24192              * @event afterselectionchange
24193              * Fires after the selection changes (eg. by key press or clicking)
24194              * @param {SelectionModel} this
24195              */
24196             "afterselectionchange" : true,
24197         /**
24198              * @event beforerowselect
24199              * Fires when a row is selected being selected, return false to cancel.
24200              * @param {SelectionModel} this
24201              * @param {Number} rowIndex The selected index
24202              * @param {Boolean} keepExisting False if other selections will be cleared
24203              */
24204             "beforerowselect" : true,
24205         /**
24206              * @event rowselect
24207              * Fires when a row is selected.
24208              * @param {SelectionModel} this
24209              * @param {Number} rowIndex The selected index
24210              * @param {Roo.data.Record} r The record
24211              */
24212             "rowselect" : true,
24213         /**
24214              * @event rowdeselect
24215              * Fires when a row is deselected.
24216              * @param {SelectionModel} this
24217              * @param {Number} rowIndex The selected index
24218              */
24219         "rowdeselect" : true
24220     });
24221     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24222     this.locked = false;
24223  };
24224
24225 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24226     /**
24227      * @cfg {Boolean} singleSelect
24228      * True to allow selection of only one row at a time (defaults to false)
24229      */
24230     singleSelect : false,
24231
24232     // private
24233     initEvents : function()
24234     {
24235
24236         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24237         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24238         //}else{ // allow click to work like normal
24239          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24240         //}
24241         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24242         this.grid.on("rowclick", this.handleMouseDown, this);
24243         
24244         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24245             "up" : function(e){
24246                 if(!e.shiftKey){
24247                     this.selectPrevious(e.shiftKey);
24248                 }else if(this.last !== false && this.lastActive !== false){
24249                     var last = this.last;
24250                     this.selectRange(this.last,  this.lastActive-1);
24251                     this.grid.getView().focusRow(this.lastActive);
24252                     if(last !== false){
24253                         this.last = last;
24254                     }
24255                 }else{
24256                     this.selectFirstRow();
24257                 }
24258                 this.fireEvent("afterselectionchange", this);
24259             },
24260             "down" : function(e){
24261                 if(!e.shiftKey){
24262                     this.selectNext(e.shiftKey);
24263                 }else if(this.last !== false && this.lastActive !== false){
24264                     var last = this.last;
24265                     this.selectRange(this.last,  this.lastActive+1);
24266                     this.grid.getView().focusRow(this.lastActive);
24267                     if(last !== false){
24268                         this.last = last;
24269                     }
24270                 }else{
24271                     this.selectFirstRow();
24272                 }
24273                 this.fireEvent("afterselectionchange", this);
24274             },
24275             scope: this
24276         });
24277         this.grid.store.on('load', function(){
24278             this.selections.clear();
24279         },this);
24280         /*
24281         var view = this.grid.view;
24282         view.on("refresh", this.onRefresh, this);
24283         view.on("rowupdated", this.onRowUpdated, this);
24284         view.on("rowremoved", this.onRemove, this);
24285         */
24286     },
24287
24288     // private
24289     onRefresh : function()
24290     {
24291         var ds = this.grid.store, i, v = this.grid.view;
24292         var s = this.selections;
24293         s.each(function(r){
24294             if((i = ds.indexOfId(r.id)) != -1){
24295                 v.onRowSelect(i);
24296             }else{
24297                 s.remove(r);
24298             }
24299         });
24300     },
24301
24302     // private
24303     onRemove : function(v, index, r){
24304         this.selections.remove(r);
24305     },
24306
24307     // private
24308     onRowUpdated : function(v, index, r){
24309         if(this.isSelected(r)){
24310             v.onRowSelect(index);
24311         }
24312     },
24313
24314     /**
24315      * Select records.
24316      * @param {Array} records The records to select
24317      * @param {Boolean} keepExisting (optional) True to keep existing selections
24318      */
24319     selectRecords : function(records, keepExisting)
24320     {
24321         if(!keepExisting){
24322             this.clearSelections();
24323         }
24324             var ds = this.grid.store;
24325         for(var i = 0, len = records.length; i < len; i++){
24326             this.selectRow(ds.indexOf(records[i]), true);
24327         }
24328     },
24329
24330     /**
24331      * Gets the number of selected rows.
24332      * @return {Number}
24333      */
24334     getCount : function(){
24335         return this.selections.length;
24336     },
24337
24338     /**
24339      * Selects the first row in the grid.
24340      */
24341     selectFirstRow : function(){
24342         this.selectRow(0);
24343     },
24344
24345     /**
24346      * Select the last row.
24347      * @param {Boolean} keepExisting (optional) True to keep existing selections
24348      */
24349     selectLastRow : function(keepExisting){
24350         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24351         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24352     },
24353
24354     /**
24355      * Selects the row immediately following the last selected row.
24356      * @param {Boolean} keepExisting (optional) True to keep existing selections
24357      */
24358     selectNext : function(keepExisting)
24359     {
24360             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24361             this.selectRow(this.last+1, keepExisting);
24362             this.grid.getView().focusRow(this.last);
24363         }
24364     },
24365
24366     /**
24367      * Selects the row that precedes the last selected row.
24368      * @param {Boolean} keepExisting (optional) True to keep existing selections
24369      */
24370     selectPrevious : function(keepExisting){
24371         if(this.last){
24372             this.selectRow(this.last-1, keepExisting);
24373             this.grid.getView().focusRow(this.last);
24374         }
24375     },
24376
24377     /**
24378      * Returns the selected records
24379      * @return {Array} Array of selected records
24380      */
24381     getSelections : function(){
24382         return [].concat(this.selections.items);
24383     },
24384
24385     /**
24386      * Returns the first selected record.
24387      * @return {Record}
24388      */
24389     getSelected : function(){
24390         return this.selections.itemAt(0);
24391     },
24392
24393
24394     /**
24395      * Clears all selections.
24396      */
24397     clearSelections : function(fast)
24398     {
24399         if(this.locked) {
24400             return;
24401         }
24402         if(fast !== true){
24403                 var ds = this.grid.store;
24404             var s = this.selections;
24405             s.each(function(r){
24406                 this.deselectRow(ds.indexOfId(r.id));
24407             }, this);
24408             s.clear();
24409         }else{
24410             this.selections.clear();
24411         }
24412         this.last = false;
24413     },
24414
24415
24416     /**
24417      * Selects all rows.
24418      */
24419     selectAll : function(){
24420         if(this.locked) {
24421             return;
24422         }
24423         this.selections.clear();
24424         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24425             this.selectRow(i, true);
24426         }
24427     },
24428
24429     /**
24430      * Returns True if there is a selection.
24431      * @return {Boolean}
24432      */
24433     hasSelection : function(){
24434         return this.selections.length > 0;
24435     },
24436
24437     /**
24438      * Returns True if the specified row is selected.
24439      * @param {Number/Record} record The record or index of the record to check
24440      * @return {Boolean}
24441      */
24442     isSelected : function(index){
24443             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24444         return (r && this.selections.key(r.id) ? true : false);
24445     },
24446
24447     /**
24448      * Returns True if the specified record id is selected.
24449      * @param {String} id The id of record to check
24450      * @return {Boolean}
24451      */
24452     isIdSelected : function(id){
24453         return (this.selections.key(id) ? true : false);
24454     },
24455
24456
24457     // private
24458     handleMouseDBClick : function(e, t){
24459         
24460     },
24461     // private
24462     handleMouseDown : function(e, t)
24463     {
24464             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24465         if(this.isLocked() || rowIndex < 0 ){
24466             return;
24467         };
24468         if(e.shiftKey && this.last !== false){
24469             var last = this.last;
24470             this.selectRange(last, rowIndex, e.ctrlKey);
24471             this.last = last; // reset the last
24472             t.focus();
24473     
24474         }else{
24475             var isSelected = this.isSelected(rowIndex);
24476             //Roo.log("select row:" + rowIndex);
24477             if(isSelected){
24478                 this.deselectRow(rowIndex);
24479             } else {
24480                         this.selectRow(rowIndex, true);
24481             }
24482     
24483             /*
24484                 if(e.button !== 0 && isSelected){
24485                 alert('rowIndex 2: ' + rowIndex);
24486                     view.focusRow(rowIndex);
24487                 }else if(e.ctrlKey && isSelected){
24488                     this.deselectRow(rowIndex);
24489                 }else if(!isSelected){
24490                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24491                     view.focusRow(rowIndex);
24492                 }
24493             */
24494         }
24495         this.fireEvent("afterselectionchange", this);
24496     },
24497     // private
24498     handleDragableRowClick :  function(grid, rowIndex, e) 
24499     {
24500         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24501             this.selectRow(rowIndex, false);
24502             grid.view.focusRow(rowIndex);
24503              this.fireEvent("afterselectionchange", this);
24504         }
24505     },
24506     
24507     /**
24508      * Selects multiple rows.
24509      * @param {Array} rows Array of the indexes of the row to select
24510      * @param {Boolean} keepExisting (optional) True to keep existing selections
24511      */
24512     selectRows : function(rows, keepExisting){
24513         if(!keepExisting){
24514             this.clearSelections();
24515         }
24516         for(var i = 0, len = rows.length; i < len; i++){
24517             this.selectRow(rows[i], true);
24518         }
24519     },
24520
24521     /**
24522      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24523      * @param {Number} startRow The index of the first row in the range
24524      * @param {Number} endRow The index of the last row in the range
24525      * @param {Boolean} keepExisting (optional) True to retain existing selections
24526      */
24527     selectRange : function(startRow, endRow, keepExisting){
24528         if(this.locked) {
24529             return;
24530         }
24531         if(!keepExisting){
24532             this.clearSelections();
24533         }
24534         if(startRow <= endRow){
24535             for(var i = startRow; i <= endRow; i++){
24536                 this.selectRow(i, true);
24537             }
24538         }else{
24539             for(var i = startRow; i >= endRow; i--){
24540                 this.selectRow(i, true);
24541             }
24542         }
24543     },
24544
24545     /**
24546      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24547      * @param {Number} startRow The index of the first row in the range
24548      * @param {Number} endRow The index of the last row in the range
24549      */
24550     deselectRange : function(startRow, endRow, preventViewNotify){
24551         if(this.locked) {
24552             return;
24553         }
24554         for(var i = startRow; i <= endRow; i++){
24555             this.deselectRow(i, preventViewNotify);
24556         }
24557     },
24558
24559     /**
24560      * Selects a row.
24561      * @param {Number} row The index of the row to select
24562      * @param {Boolean} keepExisting (optional) True to keep existing selections
24563      */
24564     selectRow : function(index, keepExisting, preventViewNotify)
24565     {
24566             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24567             return;
24568         }
24569         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24570             if(!keepExisting || this.singleSelect){
24571                 this.clearSelections();
24572             }
24573             
24574             var r = this.grid.store.getAt(index);
24575             //console.log('selectRow - record id :' + r.id);
24576             
24577             this.selections.add(r);
24578             this.last = this.lastActive = index;
24579             if(!preventViewNotify){
24580                 var proxy = new Roo.Element(
24581                                 this.grid.getRowDom(index)
24582                 );
24583                 proxy.addClass('bg-info info');
24584             }
24585             this.fireEvent("rowselect", this, index, r);
24586             this.fireEvent("selectionchange", this);
24587         }
24588     },
24589
24590     /**
24591      * Deselects a row.
24592      * @param {Number} row The index of the row to deselect
24593      */
24594     deselectRow : function(index, preventViewNotify)
24595     {
24596         if(this.locked) {
24597             return;
24598         }
24599         if(this.last == index){
24600             this.last = false;
24601         }
24602         if(this.lastActive == index){
24603             this.lastActive = false;
24604         }
24605         
24606         var r = this.grid.store.getAt(index);
24607         if (!r) {
24608             return;
24609         }
24610         
24611         this.selections.remove(r);
24612         //.console.log('deselectRow - record id :' + r.id);
24613         if(!preventViewNotify){
24614         
24615             var proxy = new Roo.Element(
24616                 this.grid.getRowDom(index)
24617             );
24618             proxy.removeClass('bg-info info');
24619         }
24620         this.fireEvent("rowdeselect", this, index);
24621         this.fireEvent("selectionchange", this);
24622     },
24623
24624     // private
24625     restoreLast : function(){
24626         if(this._last){
24627             this.last = this._last;
24628         }
24629     },
24630
24631     // private
24632     acceptsNav : function(row, col, cm){
24633         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24634     },
24635
24636     // private
24637     onEditorKey : function(field, e){
24638         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24639         if(k == e.TAB){
24640             e.stopEvent();
24641             ed.completeEdit();
24642             if(e.shiftKey){
24643                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24644             }else{
24645                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24646             }
24647         }else if(k == e.ENTER && !e.ctrlKey){
24648             e.stopEvent();
24649             ed.completeEdit();
24650             if(e.shiftKey){
24651                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24652             }else{
24653                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24654             }
24655         }else if(k == e.ESC){
24656             ed.cancelEdit();
24657         }
24658         if(newCell){
24659             g.startEditing(newCell[0], newCell[1]);
24660         }
24661     }
24662 });
24663 /*
24664  * Based on:
24665  * Ext JS Library 1.1.1
24666  * Copyright(c) 2006-2007, Ext JS, LLC.
24667  *
24668  * Originally Released Under LGPL - original licence link has changed is not relivant.
24669  *
24670  * Fork - LGPL
24671  * <script type="text/javascript">
24672  */
24673  
24674 /**
24675  * @class Roo.bootstrap.PagingToolbar
24676  * @extends Roo.bootstrap.NavSimplebar
24677  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24678  * @constructor
24679  * Create a new PagingToolbar
24680  * @param {Object} config The config object
24681  * @param {Roo.data.Store} store
24682  */
24683 Roo.bootstrap.PagingToolbar = function(config)
24684 {
24685     // old args format still supported... - xtype is prefered..
24686         // created from xtype...
24687     
24688     this.ds = config.dataSource;
24689     
24690     if (config.store && !this.ds) {
24691         this.store= Roo.factory(config.store, Roo.data);
24692         this.ds = this.store;
24693         this.ds.xmodule = this.xmodule || false;
24694     }
24695     
24696     this.toolbarItems = [];
24697     if (config.items) {
24698         this.toolbarItems = config.items;
24699     }
24700     
24701     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24702     
24703     this.cursor = 0;
24704     
24705     if (this.ds) { 
24706         this.bind(this.ds);
24707     }
24708     
24709     if (Roo.bootstrap.version == 4) {
24710         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24711     } else {
24712         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24713     }
24714     
24715 };
24716
24717 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24718     /**
24719      * @cfg {Roo.data.Store} dataSource
24720      * The underlying data store providing the paged data
24721      */
24722     /**
24723      * @cfg {String/HTMLElement/Element} container
24724      * container The id or element that will contain the toolbar
24725      */
24726     /**
24727      * @cfg {Boolean} displayInfo
24728      * True to display the displayMsg (defaults to false)
24729      */
24730     /**
24731      * @cfg {Number} pageSize
24732      * The number of records to display per page (defaults to 20)
24733      */
24734     pageSize: 20,
24735     /**
24736      * @cfg {String} displayMsg
24737      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24738      */
24739     displayMsg : 'Displaying {0} - {1} of {2}',
24740     /**
24741      * @cfg {String} emptyMsg
24742      * The message to display when no records are found (defaults to "No data to display")
24743      */
24744     emptyMsg : 'No data to display',
24745     /**
24746      * Customizable piece of the default paging text (defaults to "Page")
24747      * @type String
24748      */
24749     beforePageText : "Page",
24750     /**
24751      * Customizable piece of the default paging text (defaults to "of %0")
24752      * @type String
24753      */
24754     afterPageText : "of {0}",
24755     /**
24756      * Customizable piece of the default paging text (defaults to "First Page")
24757      * @type String
24758      */
24759     firstText : "First Page",
24760     /**
24761      * Customizable piece of the default paging text (defaults to "Previous Page")
24762      * @type String
24763      */
24764     prevText : "Previous Page",
24765     /**
24766      * Customizable piece of the default paging text (defaults to "Next Page")
24767      * @type String
24768      */
24769     nextText : "Next Page",
24770     /**
24771      * Customizable piece of the default paging text (defaults to "Last Page")
24772      * @type String
24773      */
24774     lastText : "Last Page",
24775     /**
24776      * Customizable piece of the default paging text (defaults to "Refresh")
24777      * @type String
24778      */
24779     refreshText : "Refresh",
24780
24781     buttons : false,
24782     // private
24783     onRender : function(ct, position) 
24784     {
24785         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24786         this.navgroup.parentId = this.id;
24787         this.navgroup.onRender(this.el, null);
24788         // add the buttons to the navgroup
24789         
24790         if(this.displayInfo){
24791             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24792             this.displayEl = this.el.select('.x-paging-info', true).first();
24793 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24794 //            this.displayEl = navel.el.select('span',true).first();
24795         }
24796         
24797         var _this = this;
24798         
24799         if(this.buttons){
24800             Roo.each(_this.buttons, function(e){ // this might need to use render????
24801                Roo.factory(e).render(_this.el);
24802             });
24803         }
24804             
24805         Roo.each(_this.toolbarItems, function(e) {
24806             _this.navgroup.addItem(e);
24807         });
24808         
24809         
24810         this.first = this.navgroup.addItem({
24811             tooltip: this.firstText,
24812             cls: "prev btn-outline-secondary",
24813             html : ' <i class="fa fa-step-backward"></i>',
24814             disabled: true,
24815             preventDefault: true,
24816             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24817         });
24818         
24819         this.prev =  this.navgroup.addItem({
24820             tooltip: this.prevText,
24821             cls: "prev btn-outline-secondary",
24822             html : ' <i class="fa fa-backward"></i>',
24823             disabled: true,
24824             preventDefault: true,
24825             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24826         });
24827     //this.addSeparator();
24828         
24829         
24830         var field = this.navgroup.addItem( {
24831             tagtype : 'span',
24832             cls : 'x-paging-position  btn-outline-secondary',
24833              disabled: true,
24834             html : this.beforePageText  +
24835                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24836                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24837          } ); //?? escaped?
24838         
24839         this.field = field.el.select('input', true).first();
24840         this.field.on("keydown", this.onPagingKeydown, this);
24841         this.field.on("focus", function(){this.dom.select();});
24842     
24843     
24844         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24845         //this.field.setHeight(18);
24846         //this.addSeparator();
24847         this.next = this.navgroup.addItem({
24848             tooltip: this.nextText,
24849             cls: "next btn-outline-secondary",
24850             html : ' <i class="fa fa-forward"></i>',
24851             disabled: true,
24852             preventDefault: true,
24853             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24854         });
24855         this.last = this.navgroup.addItem({
24856             tooltip: this.lastText,
24857             html : ' <i class="fa fa-step-forward"></i>',
24858             cls: "next btn-outline-secondary",
24859             disabled: true,
24860             preventDefault: true,
24861             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24862         });
24863     //this.addSeparator();
24864         this.loading = this.navgroup.addItem({
24865             tooltip: this.refreshText,
24866             cls: "btn-outline-secondary",
24867             html : ' <i class="fa fa-refresh"></i>',
24868             preventDefault: true,
24869             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24870         });
24871         
24872     },
24873
24874     // private
24875     updateInfo : function(){
24876         if(this.displayEl){
24877             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24878             var msg = count == 0 ?
24879                 this.emptyMsg :
24880                 String.format(
24881                     this.displayMsg,
24882                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24883                 );
24884             this.displayEl.update(msg);
24885         }
24886     },
24887
24888     // private
24889     onLoad : function(ds, r, o)
24890     {
24891         this.cursor = o.params.start ? o.params.start : 0;
24892         
24893         var d = this.getPageData(),
24894             ap = d.activePage,
24895             ps = d.pages;
24896         
24897         
24898         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24899         this.field.dom.value = ap;
24900         this.first.setDisabled(ap == 1);
24901         this.prev.setDisabled(ap == 1);
24902         this.next.setDisabled(ap == ps);
24903         this.last.setDisabled(ap == ps);
24904         this.loading.enable();
24905         this.updateInfo();
24906     },
24907
24908     // private
24909     getPageData : function(){
24910         var total = this.ds.getTotalCount();
24911         return {
24912             total : total,
24913             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24914             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24915         };
24916     },
24917
24918     // private
24919     onLoadError : function(){
24920         this.loading.enable();
24921     },
24922
24923     // private
24924     onPagingKeydown : function(e){
24925         var k = e.getKey();
24926         var d = this.getPageData();
24927         if(k == e.RETURN){
24928             var v = this.field.dom.value, pageNum;
24929             if(!v || isNaN(pageNum = parseInt(v, 10))){
24930                 this.field.dom.value = d.activePage;
24931                 return;
24932             }
24933             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24934             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24935             e.stopEvent();
24936         }
24937         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))
24938         {
24939           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24940           this.field.dom.value = pageNum;
24941           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24942           e.stopEvent();
24943         }
24944         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24945         {
24946           var v = this.field.dom.value, pageNum; 
24947           var increment = (e.shiftKey) ? 10 : 1;
24948           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24949                 increment *= -1;
24950           }
24951           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24952             this.field.dom.value = d.activePage;
24953             return;
24954           }
24955           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24956           {
24957             this.field.dom.value = parseInt(v, 10) + increment;
24958             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24959             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24960           }
24961           e.stopEvent();
24962         }
24963     },
24964
24965     // private
24966     beforeLoad : function(){
24967         if(this.loading){
24968             this.loading.disable();
24969         }
24970     },
24971
24972     // private
24973     onClick : function(which){
24974         
24975         var ds = this.ds;
24976         if (!ds) {
24977             return;
24978         }
24979         
24980         switch(which){
24981             case "first":
24982                 ds.load({params:{start: 0, limit: this.pageSize}});
24983             break;
24984             case "prev":
24985                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24986             break;
24987             case "next":
24988                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24989             break;
24990             case "last":
24991                 var total = ds.getTotalCount();
24992                 var extra = total % this.pageSize;
24993                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24994                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24995             break;
24996             case "refresh":
24997                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24998             break;
24999         }
25000     },
25001
25002     /**
25003      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25004      * @param {Roo.data.Store} store The data store to unbind
25005      */
25006     unbind : function(ds){
25007         ds.un("beforeload", this.beforeLoad, this);
25008         ds.un("load", this.onLoad, this);
25009         ds.un("loadexception", this.onLoadError, this);
25010         ds.un("remove", this.updateInfo, this);
25011         ds.un("add", this.updateInfo, this);
25012         this.ds = undefined;
25013     },
25014
25015     /**
25016      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25017      * @param {Roo.data.Store} store The data store to bind
25018      */
25019     bind : function(ds){
25020         ds.on("beforeload", this.beforeLoad, this);
25021         ds.on("load", this.onLoad, this);
25022         ds.on("loadexception", this.onLoadError, this);
25023         ds.on("remove", this.updateInfo, this);
25024         ds.on("add", this.updateInfo, this);
25025         this.ds = ds;
25026     }
25027 });/*
25028  * - LGPL
25029  *
25030  * element
25031  * 
25032  */
25033
25034 /**
25035  * @class Roo.bootstrap.MessageBar
25036  * @extends Roo.bootstrap.Component
25037  * Bootstrap MessageBar class
25038  * @cfg {String} html contents of the MessageBar
25039  * @cfg {String} weight (info | success | warning | danger) default info
25040  * @cfg {String} beforeClass insert the bar before the given class
25041  * @cfg {Boolean} closable (true | false) default false
25042  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25043  * 
25044  * @constructor
25045  * Create a new Element
25046  * @param {Object} config The config object
25047  */
25048
25049 Roo.bootstrap.MessageBar = function(config){
25050     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25051 };
25052
25053 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25054     
25055     html: '',
25056     weight: 'info',
25057     closable: false,
25058     fixed: false,
25059     beforeClass: 'bootstrap-sticky-wrap',
25060     
25061     getAutoCreate : function(){
25062         
25063         var cfg = {
25064             tag: 'div',
25065             cls: 'alert alert-dismissable alert-' + this.weight,
25066             cn: [
25067                 {
25068                     tag: 'span',
25069                     cls: 'message',
25070                     html: this.html || ''
25071                 }
25072             ]
25073         };
25074         
25075         if(this.fixed){
25076             cfg.cls += ' alert-messages-fixed';
25077         }
25078         
25079         if(this.closable){
25080             cfg.cn.push({
25081                 tag: 'button',
25082                 cls: 'close',
25083                 html: 'x'
25084             });
25085         }
25086         
25087         return cfg;
25088     },
25089     
25090     onRender : function(ct, position)
25091     {
25092         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25093         
25094         if(!this.el){
25095             var cfg = Roo.apply({},  this.getAutoCreate());
25096             cfg.id = Roo.id();
25097             
25098             if (this.cls) {
25099                 cfg.cls += ' ' + this.cls;
25100             }
25101             if (this.style) {
25102                 cfg.style = this.style;
25103             }
25104             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25105             
25106             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25107         }
25108         
25109         this.el.select('>button.close').on('click', this.hide, this);
25110         
25111     },
25112     
25113     show : function()
25114     {
25115         if (!this.rendered) {
25116             this.render();
25117         }
25118         
25119         this.el.show();
25120         
25121         this.fireEvent('show', this);
25122         
25123     },
25124     
25125     hide : function()
25126     {
25127         if (!this.rendered) {
25128             this.render();
25129         }
25130         
25131         this.el.hide();
25132         
25133         this.fireEvent('hide', this);
25134     },
25135     
25136     update : function()
25137     {
25138 //        var e = this.el.dom.firstChild;
25139 //        
25140 //        if(this.closable){
25141 //            e = e.nextSibling;
25142 //        }
25143 //        
25144 //        e.data = this.html || '';
25145
25146         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25147     }
25148    
25149 });
25150
25151  
25152
25153      /*
25154  * - LGPL
25155  *
25156  * Graph
25157  * 
25158  */
25159
25160
25161 /**
25162  * @class Roo.bootstrap.Graph
25163  * @extends Roo.bootstrap.Component
25164  * Bootstrap Graph class
25165 > Prameters
25166  -sm {number} sm 4
25167  -md {number} md 5
25168  @cfg {String} graphtype  bar | vbar | pie
25169  @cfg {number} g_x coodinator | centre x (pie)
25170  @cfg {number} g_y coodinator | centre y (pie)
25171  @cfg {number} g_r radius (pie)
25172  @cfg {number} g_height height of the chart (respected by all elements in the set)
25173  @cfg {number} g_width width of the chart (respected by all elements in the set)
25174  @cfg {Object} title The title of the chart
25175     
25176  -{Array}  values
25177  -opts (object) options for the chart 
25178      o {
25179      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25180      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25181      o vgutter (number)
25182      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.
25183      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25184      o to
25185      o stretch (boolean)
25186      o }
25187  -opts (object) options for the pie
25188      o{
25189      o cut
25190      o startAngle (number)
25191      o endAngle (number)
25192      } 
25193  *
25194  * @constructor
25195  * Create a new Input
25196  * @param {Object} config The config object
25197  */
25198
25199 Roo.bootstrap.Graph = function(config){
25200     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25201     
25202     this.addEvents({
25203         // img events
25204         /**
25205          * @event click
25206          * The img click event for the img.
25207          * @param {Roo.EventObject} e
25208          */
25209         "click" : true
25210     });
25211 };
25212
25213 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25214     
25215     sm: 4,
25216     md: 5,
25217     graphtype: 'bar',
25218     g_height: 250,
25219     g_width: 400,
25220     g_x: 50,
25221     g_y: 50,
25222     g_r: 30,
25223     opts:{
25224         //g_colors: this.colors,
25225         g_type: 'soft',
25226         g_gutter: '20%'
25227
25228     },
25229     title : false,
25230
25231     getAutoCreate : function(){
25232         
25233         var cfg = {
25234             tag: 'div',
25235             html : null
25236         };
25237         
25238         
25239         return  cfg;
25240     },
25241
25242     onRender : function(ct,position){
25243         
25244         
25245         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25246         
25247         if (typeof(Raphael) == 'undefined') {
25248             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25249             return;
25250         }
25251         
25252         this.raphael = Raphael(this.el.dom);
25253         
25254                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25255                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25256                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25257                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25258                 /*
25259                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25260                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25261                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25262                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25263                 
25264                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25265                 r.barchart(330, 10, 300, 220, data1);
25266                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25267                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25268                 */
25269                 
25270                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25271                 // r.barchart(30, 30, 560, 250,  xdata, {
25272                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25273                 //     axis : "0 0 1 1",
25274                 //     axisxlabels :  xdata
25275                 //     //yvalues : cols,
25276                    
25277                 // });
25278 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25279 //        
25280 //        this.load(null,xdata,{
25281 //                axis : "0 0 1 1",
25282 //                axisxlabels :  xdata
25283 //                });
25284
25285     },
25286
25287     load : function(graphtype,xdata,opts)
25288     {
25289         this.raphael.clear();
25290         if(!graphtype) {
25291             graphtype = this.graphtype;
25292         }
25293         if(!opts){
25294             opts = this.opts;
25295         }
25296         var r = this.raphael,
25297             fin = function () {
25298                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25299             },
25300             fout = function () {
25301                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25302             },
25303             pfin = function() {
25304                 this.sector.stop();
25305                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25306
25307                 if (this.label) {
25308                     this.label[0].stop();
25309                     this.label[0].attr({ r: 7.5 });
25310                     this.label[1].attr({ "font-weight": 800 });
25311                 }
25312             },
25313             pfout = function() {
25314                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25315
25316                 if (this.label) {
25317                     this.label[0].animate({ r: 5 }, 500, "bounce");
25318                     this.label[1].attr({ "font-weight": 400 });
25319                 }
25320             };
25321
25322         switch(graphtype){
25323             case 'bar':
25324                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25325                 break;
25326             case 'hbar':
25327                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25328                 break;
25329             case 'pie':
25330 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25331 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25332 //            
25333                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25334                 
25335                 break;
25336
25337         }
25338         
25339         if(this.title){
25340             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25341         }
25342         
25343     },
25344     
25345     setTitle: function(o)
25346     {
25347         this.title = o;
25348     },
25349     
25350     initEvents: function() {
25351         
25352         if(!this.href){
25353             this.el.on('click', this.onClick, this);
25354         }
25355     },
25356     
25357     onClick : function(e)
25358     {
25359         Roo.log('img onclick');
25360         this.fireEvent('click', this, e);
25361     }
25362    
25363 });
25364
25365  
25366 /*
25367  * - LGPL
25368  *
25369  * numberBox
25370  * 
25371  */
25372 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25373
25374 /**
25375  * @class Roo.bootstrap.dash.NumberBox
25376  * @extends Roo.bootstrap.Component
25377  * Bootstrap NumberBox class
25378  * @cfg {String} headline Box headline
25379  * @cfg {String} content Box content
25380  * @cfg {String} icon Box icon
25381  * @cfg {String} footer Footer text
25382  * @cfg {String} fhref Footer href
25383  * 
25384  * @constructor
25385  * Create a new NumberBox
25386  * @param {Object} config The config object
25387  */
25388
25389
25390 Roo.bootstrap.dash.NumberBox = function(config){
25391     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25392     
25393 };
25394
25395 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25396     
25397     headline : '',
25398     content : '',
25399     icon : '',
25400     footer : '',
25401     fhref : '',
25402     ficon : '',
25403     
25404     getAutoCreate : function(){
25405         
25406         var cfg = {
25407             tag : 'div',
25408             cls : 'small-box ',
25409             cn : [
25410                 {
25411                     tag : 'div',
25412                     cls : 'inner',
25413                     cn :[
25414                         {
25415                             tag : 'h3',
25416                             cls : 'roo-headline',
25417                             html : this.headline
25418                         },
25419                         {
25420                             tag : 'p',
25421                             cls : 'roo-content',
25422                             html : this.content
25423                         }
25424                     ]
25425                 }
25426             ]
25427         };
25428         
25429         if(this.icon){
25430             cfg.cn.push({
25431                 tag : 'div',
25432                 cls : 'icon',
25433                 cn :[
25434                     {
25435                         tag : 'i',
25436                         cls : 'ion ' + this.icon
25437                     }
25438                 ]
25439             });
25440         }
25441         
25442         if(this.footer){
25443             var footer = {
25444                 tag : 'a',
25445                 cls : 'small-box-footer',
25446                 href : this.fhref || '#',
25447                 html : this.footer
25448             };
25449             
25450             cfg.cn.push(footer);
25451             
25452         }
25453         
25454         return  cfg;
25455     },
25456
25457     onRender : function(ct,position){
25458         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25459
25460
25461        
25462                 
25463     },
25464
25465     setHeadline: function (value)
25466     {
25467         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25468     },
25469     
25470     setFooter: function (value, href)
25471     {
25472         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25473         
25474         if(href){
25475             this.el.select('a.small-box-footer',true).first().attr('href', href);
25476         }
25477         
25478     },
25479
25480     setContent: function (value)
25481     {
25482         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25483     },
25484
25485     initEvents: function() 
25486     {   
25487         
25488     }
25489     
25490 });
25491
25492  
25493 /*
25494  * - LGPL
25495  *
25496  * TabBox
25497  * 
25498  */
25499 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25500
25501 /**
25502  * @class Roo.bootstrap.dash.TabBox
25503  * @extends Roo.bootstrap.Component
25504  * Bootstrap TabBox class
25505  * @cfg {String} title Title of the TabBox
25506  * @cfg {String} icon Icon of the TabBox
25507  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25508  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25509  * 
25510  * @constructor
25511  * Create a new TabBox
25512  * @param {Object} config The config object
25513  */
25514
25515
25516 Roo.bootstrap.dash.TabBox = function(config){
25517     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25518     this.addEvents({
25519         // raw events
25520         /**
25521          * @event addpane
25522          * When a pane is added
25523          * @param {Roo.bootstrap.dash.TabPane} pane
25524          */
25525         "addpane" : true,
25526         /**
25527          * @event activatepane
25528          * When a pane is activated
25529          * @param {Roo.bootstrap.dash.TabPane} pane
25530          */
25531         "activatepane" : true
25532         
25533          
25534     });
25535     
25536     this.panes = [];
25537 };
25538
25539 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25540
25541     title : '',
25542     icon : false,
25543     showtabs : true,
25544     tabScrollable : false,
25545     
25546     getChildContainer : function()
25547     {
25548         return this.el.select('.tab-content', true).first();
25549     },
25550     
25551     getAutoCreate : function(){
25552         
25553         var header = {
25554             tag: 'li',
25555             cls: 'pull-left header',
25556             html: this.title,
25557             cn : []
25558         };
25559         
25560         if(this.icon){
25561             header.cn.push({
25562                 tag: 'i',
25563                 cls: 'fa ' + this.icon
25564             });
25565         }
25566         
25567         var h = {
25568             tag: 'ul',
25569             cls: 'nav nav-tabs pull-right',
25570             cn: [
25571                 header
25572             ]
25573         };
25574         
25575         if(this.tabScrollable){
25576             h = {
25577                 tag: 'div',
25578                 cls: 'tab-header',
25579                 cn: [
25580                     {
25581                         tag: 'ul',
25582                         cls: 'nav nav-tabs pull-right',
25583                         cn: [
25584                             header
25585                         ]
25586                     }
25587                 ]
25588             };
25589         }
25590         
25591         var cfg = {
25592             tag: 'div',
25593             cls: 'nav-tabs-custom',
25594             cn: [
25595                 h,
25596                 {
25597                     tag: 'div',
25598                     cls: 'tab-content no-padding',
25599                     cn: []
25600                 }
25601             ]
25602         };
25603
25604         return  cfg;
25605     },
25606     initEvents : function()
25607     {
25608         //Roo.log('add add pane handler');
25609         this.on('addpane', this.onAddPane, this);
25610     },
25611      /**
25612      * Updates the box title
25613      * @param {String} html to set the title to.
25614      */
25615     setTitle : function(value)
25616     {
25617         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25618     },
25619     onAddPane : function(pane)
25620     {
25621         this.panes.push(pane);
25622         //Roo.log('addpane');
25623         //Roo.log(pane);
25624         // tabs are rendere left to right..
25625         if(!this.showtabs){
25626             return;
25627         }
25628         
25629         var ctr = this.el.select('.nav-tabs', true).first();
25630          
25631          
25632         var existing = ctr.select('.nav-tab',true);
25633         var qty = existing.getCount();;
25634         
25635         
25636         var tab = ctr.createChild({
25637             tag : 'li',
25638             cls : 'nav-tab' + (qty ? '' : ' active'),
25639             cn : [
25640                 {
25641                     tag : 'a',
25642                     href:'#',
25643                     html : pane.title
25644                 }
25645             ]
25646         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25647         pane.tab = tab;
25648         
25649         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25650         if (!qty) {
25651             pane.el.addClass('active');
25652         }
25653         
25654                 
25655     },
25656     onTabClick : function(ev,un,ob,pane)
25657     {
25658         //Roo.log('tab - prev default');
25659         ev.preventDefault();
25660         
25661         
25662         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25663         pane.tab.addClass('active');
25664         //Roo.log(pane.title);
25665         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25666         // technically we should have a deactivate event.. but maybe add later.
25667         // and it should not de-activate the selected tab...
25668         this.fireEvent('activatepane', pane);
25669         pane.el.addClass('active');
25670         pane.fireEvent('activate');
25671         
25672         
25673     },
25674     
25675     getActivePane : function()
25676     {
25677         var r = false;
25678         Roo.each(this.panes, function(p) {
25679             if(p.el.hasClass('active')){
25680                 r = p;
25681                 return false;
25682             }
25683             
25684             return;
25685         });
25686         
25687         return r;
25688     }
25689     
25690     
25691 });
25692
25693  
25694 /*
25695  * - LGPL
25696  *
25697  * Tab pane
25698  * 
25699  */
25700 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25701 /**
25702  * @class Roo.bootstrap.TabPane
25703  * @extends Roo.bootstrap.Component
25704  * Bootstrap TabPane class
25705  * @cfg {Boolean} active (false | true) Default false
25706  * @cfg {String} title title of panel
25707
25708  * 
25709  * @constructor
25710  * Create a new TabPane
25711  * @param {Object} config The config object
25712  */
25713
25714 Roo.bootstrap.dash.TabPane = function(config){
25715     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25716     
25717     this.addEvents({
25718         // raw events
25719         /**
25720          * @event activate
25721          * When a pane is activated
25722          * @param {Roo.bootstrap.dash.TabPane} pane
25723          */
25724         "activate" : true
25725          
25726     });
25727 };
25728
25729 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25730     
25731     active : false,
25732     title : '',
25733     
25734     // the tabBox that this is attached to.
25735     tab : false,
25736      
25737     getAutoCreate : function() 
25738     {
25739         var cfg = {
25740             tag: 'div',
25741             cls: 'tab-pane'
25742         };
25743         
25744         if(this.active){
25745             cfg.cls += ' active';
25746         }
25747         
25748         return cfg;
25749     },
25750     initEvents  : function()
25751     {
25752         //Roo.log('trigger add pane handler');
25753         this.parent().fireEvent('addpane', this)
25754     },
25755     
25756      /**
25757      * Updates the tab title 
25758      * @param {String} html to set the title to.
25759      */
25760     setTitle: function(str)
25761     {
25762         if (!this.tab) {
25763             return;
25764         }
25765         this.title = str;
25766         this.tab.select('a', true).first().dom.innerHTML = str;
25767         
25768     }
25769     
25770     
25771     
25772 });
25773
25774  
25775
25776
25777  /*
25778  * - LGPL
25779  *
25780  * menu
25781  * 
25782  */
25783 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25784
25785 /**
25786  * @class Roo.bootstrap.menu.Menu
25787  * @extends Roo.bootstrap.Component
25788  * Bootstrap Menu class - container for Menu
25789  * @cfg {String} html Text of the menu
25790  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25791  * @cfg {String} icon Font awesome icon
25792  * @cfg {String} pos Menu align to (top | bottom) default bottom
25793  * 
25794  * 
25795  * @constructor
25796  * Create a new Menu
25797  * @param {Object} config The config object
25798  */
25799
25800
25801 Roo.bootstrap.menu.Menu = function(config){
25802     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25803     
25804     this.addEvents({
25805         /**
25806          * @event beforeshow
25807          * Fires before this menu is displayed
25808          * @param {Roo.bootstrap.menu.Menu} this
25809          */
25810         beforeshow : true,
25811         /**
25812          * @event beforehide
25813          * Fires before this menu is hidden
25814          * @param {Roo.bootstrap.menu.Menu} this
25815          */
25816         beforehide : true,
25817         /**
25818          * @event show
25819          * Fires after this menu is displayed
25820          * @param {Roo.bootstrap.menu.Menu} this
25821          */
25822         show : true,
25823         /**
25824          * @event hide
25825          * Fires after this menu is hidden
25826          * @param {Roo.bootstrap.menu.Menu} this
25827          */
25828         hide : true,
25829         /**
25830          * @event click
25831          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25832          * @param {Roo.bootstrap.menu.Menu} this
25833          * @param {Roo.EventObject} e
25834          */
25835         click : true
25836     });
25837     
25838 };
25839
25840 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25841     
25842     submenu : false,
25843     html : '',
25844     weight : 'default',
25845     icon : false,
25846     pos : 'bottom',
25847     
25848     
25849     getChildContainer : function() {
25850         if(this.isSubMenu){
25851             return this.el;
25852         }
25853         
25854         return this.el.select('ul.dropdown-menu', true).first();  
25855     },
25856     
25857     getAutoCreate : function()
25858     {
25859         var text = [
25860             {
25861                 tag : 'span',
25862                 cls : 'roo-menu-text',
25863                 html : this.html
25864             }
25865         ];
25866         
25867         if(this.icon){
25868             text.unshift({
25869                 tag : 'i',
25870                 cls : 'fa ' + this.icon
25871             })
25872         }
25873         
25874         
25875         var cfg = {
25876             tag : 'div',
25877             cls : 'btn-group',
25878             cn : [
25879                 {
25880                     tag : 'button',
25881                     cls : 'dropdown-button btn btn-' + this.weight,
25882                     cn : text
25883                 },
25884                 {
25885                     tag : 'button',
25886                     cls : 'dropdown-toggle btn btn-' + this.weight,
25887                     cn : [
25888                         {
25889                             tag : 'span',
25890                             cls : 'caret'
25891                         }
25892                     ]
25893                 },
25894                 {
25895                     tag : 'ul',
25896                     cls : 'dropdown-menu'
25897                 }
25898             ]
25899             
25900         };
25901         
25902         if(this.pos == 'top'){
25903             cfg.cls += ' dropup';
25904         }
25905         
25906         if(this.isSubMenu){
25907             cfg = {
25908                 tag : 'ul',
25909                 cls : 'dropdown-menu'
25910             }
25911         }
25912         
25913         return cfg;
25914     },
25915     
25916     onRender : function(ct, position)
25917     {
25918         this.isSubMenu = ct.hasClass('dropdown-submenu');
25919         
25920         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25921     },
25922     
25923     initEvents : function() 
25924     {
25925         if(this.isSubMenu){
25926             return;
25927         }
25928         
25929         this.hidden = true;
25930         
25931         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25932         this.triggerEl.on('click', this.onTriggerPress, this);
25933         
25934         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25935         this.buttonEl.on('click', this.onClick, this);
25936         
25937     },
25938     
25939     list : function()
25940     {
25941         if(this.isSubMenu){
25942             return this.el;
25943         }
25944         
25945         return this.el.select('ul.dropdown-menu', true).first();
25946     },
25947     
25948     onClick : function(e)
25949     {
25950         this.fireEvent("click", this, e);
25951     },
25952     
25953     onTriggerPress  : function(e)
25954     {   
25955         if (this.isVisible()) {
25956             this.hide();
25957         } else {
25958             this.show();
25959         }
25960     },
25961     
25962     isVisible : function(){
25963         return !this.hidden;
25964     },
25965     
25966     show : function()
25967     {
25968         this.fireEvent("beforeshow", this);
25969         
25970         this.hidden = false;
25971         this.el.addClass('open');
25972         
25973         Roo.get(document).on("mouseup", this.onMouseUp, this);
25974         
25975         this.fireEvent("show", this);
25976         
25977         
25978     },
25979     
25980     hide : function()
25981     {
25982         this.fireEvent("beforehide", this);
25983         
25984         this.hidden = true;
25985         this.el.removeClass('open');
25986         
25987         Roo.get(document).un("mouseup", this.onMouseUp);
25988         
25989         this.fireEvent("hide", this);
25990     },
25991     
25992     onMouseUp : function()
25993     {
25994         this.hide();
25995     }
25996     
25997 });
25998
25999  
26000  /*
26001  * - LGPL
26002  *
26003  * menu item
26004  * 
26005  */
26006 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26007
26008 /**
26009  * @class Roo.bootstrap.menu.Item
26010  * @extends Roo.bootstrap.Component
26011  * Bootstrap MenuItem class
26012  * @cfg {Boolean} submenu (true | false) default false
26013  * @cfg {String} html text of the item
26014  * @cfg {String} href the link
26015  * @cfg {Boolean} disable (true | false) default false
26016  * @cfg {Boolean} preventDefault (true | false) default true
26017  * @cfg {String} icon Font awesome icon
26018  * @cfg {String} pos Submenu align to (left | right) default right 
26019  * 
26020  * 
26021  * @constructor
26022  * Create a new Item
26023  * @param {Object} config The config object
26024  */
26025
26026
26027 Roo.bootstrap.menu.Item = function(config){
26028     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26029     this.addEvents({
26030         /**
26031          * @event mouseover
26032          * Fires when the mouse is hovering over this menu
26033          * @param {Roo.bootstrap.menu.Item} this
26034          * @param {Roo.EventObject} e
26035          */
26036         mouseover : true,
26037         /**
26038          * @event mouseout
26039          * Fires when the mouse exits this menu
26040          * @param {Roo.bootstrap.menu.Item} this
26041          * @param {Roo.EventObject} e
26042          */
26043         mouseout : true,
26044         // raw events
26045         /**
26046          * @event click
26047          * The raw click event for the entire grid.
26048          * @param {Roo.EventObject} e
26049          */
26050         click : true
26051     });
26052 };
26053
26054 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26055     
26056     submenu : false,
26057     href : '',
26058     html : '',
26059     preventDefault: true,
26060     disable : false,
26061     icon : false,
26062     pos : 'right',
26063     
26064     getAutoCreate : function()
26065     {
26066         var text = [
26067             {
26068                 tag : 'span',
26069                 cls : 'roo-menu-item-text',
26070                 html : this.html
26071             }
26072         ];
26073         
26074         if(this.icon){
26075             text.unshift({
26076                 tag : 'i',
26077                 cls : 'fa ' + this.icon
26078             })
26079         }
26080         
26081         var cfg = {
26082             tag : 'li',
26083             cn : [
26084                 {
26085                     tag : 'a',
26086                     href : this.href || '#',
26087                     cn : text
26088                 }
26089             ]
26090         };
26091         
26092         if(this.disable){
26093             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26094         }
26095         
26096         if(this.submenu){
26097             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26098             
26099             if(this.pos == 'left'){
26100                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26101             }
26102         }
26103         
26104         return cfg;
26105     },
26106     
26107     initEvents : function() 
26108     {
26109         this.el.on('mouseover', this.onMouseOver, this);
26110         this.el.on('mouseout', this.onMouseOut, this);
26111         
26112         this.el.select('a', true).first().on('click', this.onClick, this);
26113         
26114     },
26115     
26116     onClick : function(e)
26117     {
26118         if(this.preventDefault){
26119             e.preventDefault();
26120         }
26121         
26122         this.fireEvent("click", this, e);
26123     },
26124     
26125     onMouseOver : function(e)
26126     {
26127         if(this.submenu && this.pos == 'left'){
26128             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26129         }
26130         
26131         this.fireEvent("mouseover", this, e);
26132     },
26133     
26134     onMouseOut : function(e)
26135     {
26136         this.fireEvent("mouseout", this, e);
26137     }
26138 });
26139
26140  
26141
26142  /*
26143  * - LGPL
26144  *
26145  * menu separator
26146  * 
26147  */
26148 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26149
26150 /**
26151  * @class Roo.bootstrap.menu.Separator
26152  * @extends Roo.bootstrap.Component
26153  * Bootstrap Separator class
26154  * 
26155  * @constructor
26156  * Create a new Separator
26157  * @param {Object} config The config object
26158  */
26159
26160
26161 Roo.bootstrap.menu.Separator = function(config){
26162     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26163 };
26164
26165 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26166     
26167     getAutoCreate : function(){
26168         var cfg = {
26169             tag : 'li',
26170             cls: 'divider'
26171         };
26172         
26173         return cfg;
26174     }
26175    
26176 });
26177
26178  
26179
26180  /*
26181  * - LGPL
26182  *
26183  * Tooltip
26184  * 
26185  */
26186
26187 /**
26188  * @class Roo.bootstrap.Tooltip
26189  * Bootstrap Tooltip class
26190  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26191  * to determine which dom element triggers the tooltip.
26192  * 
26193  * It needs to add support for additional attributes like tooltip-position
26194  * 
26195  * @constructor
26196  * Create a new Toolti
26197  * @param {Object} config The config object
26198  */
26199
26200 Roo.bootstrap.Tooltip = function(config){
26201     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26202     
26203     this.alignment = Roo.bootstrap.Tooltip.alignment;
26204     
26205     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26206         this.alignment = config.alignment;
26207     }
26208     
26209 };
26210
26211 Roo.apply(Roo.bootstrap.Tooltip, {
26212     /**
26213      * @function init initialize tooltip monitoring.
26214      * @static
26215      */
26216     currentEl : false,
26217     currentTip : false,
26218     currentRegion : false,
26219     
26220     //  init : delay?
26221     
26222     init : function()
26223     {
26224         Roo.get(document).on('mouseover', this.enter ,this);
26225         Roo.get(document).on('mouseout', this.leave, this);
26226          
26227         
26228         this.currentTip = new Roo.bootstrap.Tooltip();
26229     },
26230     
26231     enter : function(ev)
26232     {
26233         var dom = ev.getTarget();
26234         
26235         //Roo.log(['enter',dom]);
26236         var el = Roo.fly(dom);
26237         if (this.currentEl) {
26238             //Roo.log(dom);
26239             //Roo.log(this.currentEl);
26240             //Roo.log(this.currentEl.contains(dom));
26241             if (this.currentEl == el) {
26242                 return;
26243             }
26244             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26245                 return;
26246             }
26247
26248         }
26249         
26250         if (this.currentTip.el) {
26251             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26252         }    
26253         //Roo.log(ev);
26254         
26255         if(!el || el.dom == document){
26256             return;
26257         }
26258         
26259         var bindEl = el;
26260         
26261         // you can not look for children, as if el is the body.. then everythign is the child..
26262         if (!el.attr('tooltip')) { //
26263             if (!el.select("[tooltip]").elements.length) {
26264                 return;
26265             }
26266             // is the mouse over this child...?
26267             bindEl = el.select("[tooltip]").first();
26268             var xy = ev.getXY();
26269             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26270                 //Roo.log("not in region.");
26271                 return;
26272             }
26273             //Roo.log("child element over..");
26274             
26275         }
26276         this.currentEl = bindEl;
26277         this.currentTip.bind(bindEl);
26278         this.currentRegion = Roo.lib.Region.getRegion(dom);
26279         this.currentTip.enter();
26280         
26281     },
26282     leave : function(ev)
26283     {
26284         var dom = ev.getTarget();
26285         //Roo.log(['leave',dom]);
26286         if (!this.currentEl) {
26287             return;
26288         }
26289         
26290         
26291         if (dom != this.currentEl.dom) {
26292             return;
26293         }
26294         var xy = ev.getXY();
26295         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26296             return;
26297         }
26298         // only activate leave if mouse cursor is outside... bounding box..
26299         
26300         
26301         
26302         
26303         if (this.currentTip) {
26304             this.currentTip.leave();
26305         }
26306         //Roo.log('clear currentEl');
26307         this.currentEl = false;
26308         
26309         
26310     },
26311     alignment : {
26312         'left' : ['r-l', [-2,0], 'right'],
26313         'right' : ['l-r', [2,0], 'left'],
26314         'bottom' : ['t-b', [0,2], 'top'],
26315         'top' : [ 'b-t', [0,-2], 'bottom']
26316     }
26317     
26318 });
26319
26320
26321 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26322     
26323     
26324     bindEl : false,
26325     
26326     delay : null, // can be { show : 300 , hide: 500}
26327     
26328     timeout : null,
26329     
26330     hoverState : null, //???
26331     
26332     placement : 'bottom', 
26333     
26334     alignment : false,
26335     
26336     getAutoCreate : function(){
26337     
26338         var cfg = {
26339            cls : 'tooltip',
26340            role : 'tooltip',
26341            cn : [
26342                 {
26343                     cls : 'tooltip-arrow'
26344                 },
26345                 {
26346                     cls : 'tooltip-inner'
26347                 }
26348            ]
26349         };
26350         
26351         return cfg;
26352     },
26353     bind : function(el)
26354     {
26355         this.bindEl = el;
26356     },
26357       
26358     
26359     enter : function () {
26360        
26361         if (this.timeout != null) {
26362             clearTimeout(this.timeout);
26363         }
26364         
26365         this.hoverState = 'in';
26366          //Roo.log("enter - show");
26367         if (!this.delay || !this.delay.show) {
26368             this.show();
26369             return;
26370         }
26371         var _t = this;
26372         this.timeout = setTimeout(function () {
26373             if (_t.hoverState == 'in') {
26374                 _t.show();
26375             }
26376         }, this.delay.show);
26377     },
26378     leave : function()
26379     {
26380         clearTimeout(this.timeout);
26381     
26382         this.hoverState = 'out';
26383          if (!this.delay || !this.delay.hide) {
26384             this.hide();
26385             return;
26386         }
26387        
26388         var _t = this;
26389         this.timeout = setTimeout(function () {
26390             //Roo.log("leave - timeout");
26391             
26392             if (_t.hoverState == 'out') {
26393                 _t.hide();
26394                 Roo.bootstrap.Tooltip.currentEl = false;
26395             }
26396         }, delay);
26397     },
26398     
26399     show : function (msg)
26400     {
26401         if (!this.el) {
26402             this.render(document.body);
26403         }
26404         // set content.
26405         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26406         
26407         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26408         
26409         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26410         
26411         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26412         
26413         var placement = typeof this.placement == 'function' ?
26414             this.placement.call(this, this.el, on_el) :
26415             this.placement;
26416             
26417         var autoToken = /\s?auto?\s?/i;
26418         var autoPlace = autoToken.test(placement);
26419         if (autoPlace) {
26420             placement = placement.replace(autoToken, '') || 'top';
26421         }
26422         
26423         //this.el.detach()
26424         //this.el.setXY([0,0]);
26425         this.el.show();
26426         //this.el.dom.style.display='block';
26427         
26428         //this.el.appendTo(on_el);
26429         
26430         var p = this.getPosition();
26431         var box = this.el.getBox();
26432         
26433         if (autoPlace) {
26434             // fixme..
26435         }
26436         
26437         var align = this.alignment[placement];
26438         
26439         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26440         
26441         if(placement == 'top' || placement == 'bottom'){
26442             if(xy[0] < 0){
26443                 placement = 'right';
26444             }
26445             
26446             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26447                 placement = 'left';
26448             }
26449             
26450             var scroll = Roo.select('body', true).first().getScroll();
26451             
26452             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26453                 placement = 'top';
26454             }
26455             
26456             align = this.alignment[placement];
26457         }
26458         
26459         this.el.alignTo(this.bindEl, align[0],align[1]);
26460         //var arrow = this.el.select('.arrow',true).first();
26461         //arrow.set(align[2], 
26462         
26463         this.el.addClass(placement);
26464         
26465         this.el.addClass('in fade');
26466         
26467         this.hoverState = null;
26468         
26469         if (this.el.hasClass('fade')) {
26470             // fade it?
26471         }
26472         
26473     },
26474     hide : function()
26475     {
26476          
26477         if (!this.el) {
26478             return;
26479         }
26480         //this.el.setXY([0,0]);
26481         this.el.removeClass('in');
26482         //this.el.hide();
26483         
26484     }
26485     
26486 });
26487  
26488
26489  /*
26490  * - LGPL
26491  *
26492  * Location Picker
26493  * 
26494  */
26495
26496 /**
26497  * @class Roo.bootstrap.LocationPicker
26498  * @extends Roo.bootstrap.Component
26499  * Bootstrap LocationPicker class
26500  * @cfg {Number} latitude Position when init default 0
26501  * @cfg {Number} longitude Position when init default 0
26502  * @cfg {Number} zoom default 15
26503  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26504  * @cfg {Boolean} mapTypeControl default false
26505  * @cfg {Boolean} disableDoubleClickZoom default false
26506  * @cfg {Boolean} scrollwheel default true
26507  * @cfg {Boolean} streetViewControl default false
26508  * @cfg {Number} radius default 0
26509  * @cfg {String} locationName
26510  * @cfg {Boolean} draggable default true
26511  * @cfg {Boolean} enableAutocomplete default false
26512  * @cfg {Boolean} enableReverseGeocode default true
26513  * @cfg {String} markerTitle
26514  * 
26515  * @constructor
26516  * Create a new LocationPicker
26517  * @param {Object} config The config object
26518  */
26519
26520
26521 Roo.bootstrap.LocationPicker = function(config){
26522     
26523     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26524     
26525     this.addEvents({
26526         /**
26527          * @event initial
26528          * Fires when the picker initialized.
26529          * @param {Roo.bootstrap.LocationPicker} this
26530          * @param {Google Location} location
26531          */
26532         initial : true,
26533         /**
26534          * @event positionchanged
26535          * Fires when the picker position changed.
26536          * @param {Roo.bootstrap.LocationPicker} this
26537          * @param {Google Location} location
26538          */
26539         positionchanged : true,
26540         /**
26541          * @event resize
26542          * Fires when the map resize.
26543          * @param {Roo.bootstrap.LocationPicker} this
26544          */
26545         resize : true,
26546         /**
26547          * @event show
26548          * Fires when the map show.
26549          * @param {Roo.bootstrap.LocationPicker} this
26550          */
26551         show : true,
26552         /**
26553          * @event hide
26554          * Fires when the map hide.
26555          * @param {Roo.bootstrap.LocationPicker} this
26556          */
26557         hide : true,
26558         /**
26559          * @event mapClick
26560          * Fires when click the map.
26561          * @param {Roo.bootstrap.LocationPicker} this
26562          * @param {Map event} e
26563          */
26564         mapClick : true,
26565         /**
26566          * @event mapRightClick
26567          * Fires when right click the map.
26568          * @param {Roo.bootstrap.LocationPicker} this
26569          * @param {Map event} e
26570          */
26571         mapRightClick : true,
26572         /**
26573          * @event markerClick
26574          * Fires when click the marker.
26575          * @param {Roo.bootstrap.LocationPicker} this
26576          * @param {Map event} e
26577          */
26578         markerClick : true,
26579         /**
26580          * @event markerRightClick
26581          * Fires when right click the marker.
26582          * @param {Roo.bootstrap.LocationPicker} this
26583          * @param {Map event} e
26584          */
26585         markerRightClick : true,
26586         /**
26587          * @event OverlayViewDraw
26588          * Fires when OverlayView Draw
26589          * @param {Roo.bootstrap.LocationPicker} this
26590          */
26591         OverlayViewDraw : true,
26592         /**
26593          * @event OverlayViewOnAdd
26594          * Fires when OverlayView Draw
26595          * @param {Roo.bootstrap.LocationPicker} this
26596          */
26597         OverlayViewOnAdd : true,
26598         /**
26599          * @event OverlayViewOnRemove
26600          * Fires when OverlayView Draw
26601          * @param {Roo.bootstrap.LocationPicker} this
26602          */
26603         OverlayViewOnRemove : true,
26604         /**
26605          * @event OverlayViewShow
26606          * Fires when OverlayView Draw
26607          * @param {Roo.bootstrap.LocationPicker} this
26608          * @param {Pixel} cpx
26609          */
26610         OverlayViewShow : true,
26611         /**
26612          * @event OverlayViewHide
26613          * Fires when OverlayView Draw
26614          * @param {Roo.bootstrap.LocationPicker} this
26615          */
26616         OverlayViewHide : true,
26617         /**
26618          * @event loadexception
26619          * Fires when load google lib failed.
26620          * @param {Roo.bootstrap.LocationPicker} this
26621          */
26622         loadexception : true
26623     });
26624         
26625 };
26626
26627 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26628     
26629     gMapContext: false,
26630     
26631     latitude: 0,
26632     longitude: 0,
26633     zoom: 15,
26634     mapTypeId: false,
26635     mapTypeControl: false,
26636     disableDoubleClickZoom: false,
26637     scrollwheel: true,
26638     streetViewControl: false,
26639     radius: 0,
26640     locationName: '',
26641     draggable: true,
26642     enableAutocomplete: false,
26643     enableReverseGeocode: true,
26644     markerTitle: '',
26645     
26646     getAutoCreate: function()
26647     {
26648
26649         var cfg = {
26650             tag: 'div',
26651             cls: 'roo-location-picker'
26652         };
26653         
26654         return cfg
26655     },
26656     
26657     initEvents: function(ct, position)
26658     {       
26659         if(!this.el.getWidth() || this.isApplied()){
26660             return;
26661         }
26662         
26663         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26664         
26665         this.initial();
26666     },
26667     
26668     initial: function()
26669     {
26670         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26671             this.fireEvent('loadexception', this);
26672             return;
26673         }
26674         
26675         if(!this.mapTypeId){
26676             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26677         }
26678         
26679         this.gMapContext = this.GMapContext();
26680         
26681         this.initOverlayView();
26682         
26683         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26684         
26685         var _this = this;
26686                 
26687         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26688             _this.setPosition(_this.gMapContext.marker.position);
26689         });
26690         
26691         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26692             _this.fireEvent('mapClick', this, event);
26693             
26694         });
26695
26696         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26697             _this.fireEvent('mapRightClick', this, event);
26698             
26699         });
26700         
26701         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26702             _this.fireEvent('markerClick', this, event);
26703             
26704         });
26705
26706         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26707             _this.fireEvent('markerRightClick', this, event);
26708             
26709         });
26710         
26711         this.setPosition(this.gMapContext.location);
26712         
26713         this.fireEvent('initial', this, this.gMapContext.location);
26714     },
26715     
26716     initOverlayView: function()
26717     {
26718         var _this = this;
26719         
26720         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26721             
26722             draw: function()
26723             {
26724                 _this.fireEvent('OverlayViewDraw', _this);
26725             },
26726             
26727             onAdd: function()
26728             {
26729                 _this.fireEvent('OverlayViewOnAdd', _this);
26730             },
26731             
26732             onRemove: function()
26733             {
26734                 _this.fireEvent('OverlayViewOnRemove', _this);
26735             },
26736             
26737             show: function(cpx)
26738             {
26739                 _this.fireEvent('OverlayViewShow', _this, cpx);
26740             },
26741             
26742             hide: function()
26743             {
26744                 _this.fireEvent('OverlayViewHide', _this);
26745             }
26746             
26747         });
26748     },
26749     
26750     fromLatLngToContainerPixel: function(event)
26751     {
26752         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26753     },
26754     
26755     isApplied: function() 
26756     {
26757         return this.getGmapContext() == false ? false : true;
26758     },
26759     
26760     getGmapContext: function() 
26761     {
26762         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26763     },
26764     
26765     GMapContext: function() 
26766     {
26767         var position = new google.maps.LatLng(this.latitude, this.longitude);
26768         
26769         var _map = new google.maps.Map(this.el.dom, {
26770             center: position,
26771             zoom: this.zoom,
26772             mapTypeId: this.mapTypeId,
26773             mapTypeControl: this.mapTypeControl,
26774             disableDoubleClickZoom: this.disableDoubleClickZoom,
26775             scrollwheel: this.scrollwheel,
26776             streetViewControl: this.streetViewControl,
26777             locationName: this.locationName,
26778             draggable: this.draggable,
26779             enableAutocomplete: this.enableAutocomplete,
26780             enableReverseGeocode: this.enableReverseGeocode
26781         });
26782         
26783         var _marker = new google.maps.Marker({
26784             position: position,
26785             map: _map,
26786             title: this.markerTitle,
26787             draggable: this.draggable
26788         });
26789         
26790         return {
26791             map: _map,
26792             marker: _marker,
26793             circle: null,
26794             location: position,
26795             radius: this.radius,
26796             locationName: this.locationName,
26797             addressComponents: {
26798                 formatted_address: null,
26799                 addressLine1: null,
26800                 addressLine2: null,
26801                 streetName: null,
26802                 streetNumber: null,
26803                 city: null,
26804                 district: null,
26805                 state: null,
26806                 stateOrProvince: null
26807             },
26808             settings: this,
26809             domContainer: this.el.dom,
26810             geodecoder: new google.maps.Geocoder()
26811         };
26812     },
26813     
26814     drawCircle: function(center, radius, options) 
26815     {
26816         if (this.gMapContext.circle != null) {
26817             this.gMapContext.circle.setMap(null);
26818         }
26819         if (radius > 0) {
26820             radius *= 1;
26821             options = Roo.apply({}, options, {
26822                 strokeColor: "#0000FF",
26823                 strokeOpacity: .35,
26824                 strokeWeight: 2,
26825                 fillColor: "#0000FF",
26826                 fillOpacity: .2
26827             });
26828             
26829             options.map = this.gMapContext.map;
26830             options.radius = radius;
26831             options.center = center;
26832             this.gMapContext.circle = new google.maps.Circle(options);
26833             return this.gMapContext.circle;
26834         }
26835         
26836         return null;
26837     },
26838     
26839     setPosition: function(location) 
26840     {
26841         this.gMapContext.location = location;
26842         this.gMapContext.marker.setPosition(location);
26843         this.gMapContext.map.panTo(location);
26844         this.drawCircle(location, this.gMapContext.radius, {});
26845         
26846         var _this = this;
26847         
26848         if (this.gMapContext.settings.enableReverseGeocode) {
26849             this.gMapContext.geodecoder.geocode({
26850                 latLng: this.gMapContext.location
26851             }, function(results, status) {
26852                 
26853                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26854                     _this.gMapContext.locationName = results[0].formatted_address;
26855                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26856                     
26857                     _this.fireEvent('positionchanged', this, location);
26858                 }
26859             });
26860             
26861             return;
26862         }
26863         
26864         this.fireEvent('positionchanged', this, location);
26865     },
26866     
26867     resize: function()
26868     {
26869         google.maps.event.trigger(this.gMapContext.map, "resize");
26870         
26871         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26872         
26873         this.fireEvent('resize', this);
26874     },
26875     
26876     setPositionByLatLng: function(latitude, longitude)
26877     {
26878         this.setPosition(new google.maps.LatLng(latitude, longitude));
26879     },
26880     
26881     getCurrentPosition: function() 
26882     {
26883         return {
26884             latitude: this.gMapContext.location.lat(),
26885             longitude: this.gMapContext.location.lng()
26886         };
26887     },
26888     
26889     getAddressName: function() 
26890     {
26891         return this.gMapContext.locationName;
26892     },
26893     
26894     getAddressComponents: function() 
26895     {
26896         return this.gMapContext.addressComponents;
26897     },
26898     
26899     address_component_from_google_geocode: function(address_components) 
26900     {
26901         var result = {};
26902         
26903         for (var i = 0; i < address_components.length; i++) {
26904             var component = address_components[i];
26905             if (component.types.indexOf("postal_code") >= 0) {
26906                 result.postalCode = component.short_name;
26907             } else if (component.types.indexOf("street_number") >= 0) {
26908                 result.streetNumber = component.short_name;
26909             } else if (component.types.indexOf("route") >= 0) {
26910                 result.streetName = component.short_name;
26911             } else if (component.types.indexOf("neighborhood") >= 0) {
26912                 result.city = component.short_name;
26913             } else if (component.types.indexOf("locality") >= 0) {
26914                 result.city = component.short_name;
26915             } else if (component.types.indexOf("sublocality") >= 0) {
26916                 result.district = component.short_name;
26917             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26918                 result.stateOrProvince = component.short_name;
26919             } else if (component.types.indexOf("country") >= 0) {
26920                 result.country = component.short_name;
26921             }
26922         }
26923         
26924         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26925         result.addressLine2 = "";
26926         return result;
26927     },
26928     
26929     setZoomLevel: function(zoom)
26930     {
26931         this.gMapContext.map.setZoom(zoom);
26932     },
26933     
26934     show: function()
26935     {
26936         if(!this.el){
26937             return;
26938         }
26939         
26940         this.el.show();
26941         
26942         this.resize();
26943         
26944         this.fireEvent('show', this);
26945     },
26946     
26947     hide: function()
26948     {
26949         if(!this.el){
26950             return;
26951         }
26952         
26953         this.el.hide();
26954         
26955         this.fireEvent('hide', this);
26956     }
26957     
26958 });
26959
26960 Roo.apply(Roo.bootstrap.LocationPicker, {
26961     
26962     OverlayView : function(map, options)
26963     {
26964         options = options || {};
26965         
26966         this.setMap(map);
26967     }
26968     
26969     
26970 });/*
26971  * - LGPL
26972  *
26973  * Alert
26974  * 
26975  */
26976
26977 /**
26978  * @class Roo.bootstrap.Alert
26979  * @extends Roo.bootstrap.Component
26980  * Bootstrap Alert class
26981  * @cfg {String} title The title of alert
26982  * @cfg {String} html The content of alert
26983  * @cfg {String} weight (  success | info | warning | danger )
26984  * @cfg {String} faicon font-awesomeicon
26985  * 
26986  * @constructor
26987  * Create a new alert
26988  * @param {Object} config The config object
26989  */
26990
26991
26992 Roo.bootstrap.Alert = function(config){
26993     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26994     
26995 };
26996
26997 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26998     
26999     title: '',
27000     html: '',
27001     weight: false,
27002     faicon: false,
27003     
27004     getAutoCreate : function()
27005     {
27006         
27007         var cfg = {
27008             tag : 'div',
27009             cls : 'alert',
27010             cn : [
27011                 {
27012                     tag : 'i',
27013                     cls : 'roo-alert-icon'
27014                     
27015                 },
27016                 {
27017                     tag : 'b',
27018                     cls : 'roo-alert-title',
27019                     html : this.title
27020                 },
27021                 {
27022                     tag : 'span',
27023                     cls : 'roo-alert-text',
27024                     html : this.html
27025                 }
27026             ]
27027         };
27028         
27029         if(this.faicon){
27030             cfg.cn[0].cls += ' fa ' + this.faicon;
27031         }
27032         
27033         if(this.weight){
27034             cfg.cls += ' alert-' + this.weight;
27035         }
27036         
27037         return cfg;
27038     },
27039     
27040     initEvents: function() 
27041     {
27042         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27043     },
27044     
27045     setTitle : function(str)
27046     {
27047         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27048     },
27049     
27050     setText : function(str)
27051     {
27052         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27053     },
27054     
27055     setWeight : function(weight)
27056     {
27057         if(this.weight){
27058             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27059         }
27060         
27061         this.weight = weight;
27062         
27063         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27064     },
27065     
27066     setIcon : function(icon)
27067     {
27068         if(this.faicon){
27069             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27070         }
27071         
27072         this.faicon = icon;
27073         
27074         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27075     },
27076     
27077     hide: function() 
27078     {
27079         this.el.hide();   
27080     },
27081     
27082     show: function() 
27083     {  
27084         this.el.show();   
27085     }
27086     
27087 });
27088
27089  
27090 /*
27091 * Licence: LGPL
27092 */
27093
27094 /**
27095  * @class Roo.bootstrap.UploadCropbox
27096  * @extends Roo.bootstrap.Component
27097  * Bootstrap UploadCropbox class
27098  * @cfg {String} emptyText show when image has been loaded
27099  * @cfg {String} rotateNotify show when image too small to rotate
27100  * @cfg {Number} errorTimeout default 3000
27101  * @cfg {Number} minWidth default 300
27102  * @cfg {Number} minHeight default 300
27103  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27104  * @cfg {Boolean} isDocument (true|false) default false
27105  * @cfg {String} url action url
27106  * @cfg {String} paramName default 'imageUpload'
27107  * @cfg {String} method default POST
27108  * @cfg {Boolean} loadMask (true|false) default true
27109  * @cfg {Boolean} loadingText default 'Loading...'
27110  * 
27111  * @constructor
27112  * Create a new UploadCropbox
27113  * @param {Object} config The config object
27114  */
27115
27116 Roo.bootstrap.UploadCropbox = function(config){
27117     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27118     
27119     this.addEvents({
27120         /**
27121          * @event beforeselectfile
27122          * Fire before select file
27123          * @param {Roo.bootstrap.UploadCropbox} this
27124          */
27125         "beforeselectfile" : true,
27126         /**
27127          * @event initial
27128          * Fire after initEvent
27129          * @param {Roo.bootstrap.UploadCropbox} this
27130          */
27131         "initial" : true,
27132         /**
27133          * @event crop
27134          * Fire after initEvent
27135          * @param {Roo.bootstrap.UploadCropbox} this
27136          * @param {String} data
27137          */
27138         "crop" : true,
27139         /**
27140          * @event prepare
27141          * Fire when preparing the file data
27142          * @param {Roo.bootstrap.UploadCropbox} this
27143          * @param {Object} file
27144          */
27145         "prepare" : true,
27146         /**
27147          * @event exception
27148          * Fire when get exception
27149          * @param {Roo.bootstrap.UploadCropbox} this
27150          * @param {XMLHttpRequest} xhr
27151          */
27152         "exception" : true,
27153         /**
27154          * @event beforeloadcanvas
27155          * Fire before load the canvas
27156          * @param {Roo.bootstrap.UploadCropbox} this
27157          * @param {String} src
27158          */
27159         "beforeloadcanvas" : true,
27160         /**
27161          * @event trash
27162          * Fire when trash image
27163          * @param {Roo.bootstrap.UploadCropbox} this
27164          */
27165         "trash" : true,
27166         /**
27167          * @event download
27168          * Fire when download the image
27169          * @param {Roo.bootstrap.UploadCropbox} this
27170          */
27171         "download" : true,
27172         /**
27173          * @event footerbuttonclick
27174          * Fire when footerbuttonclick
27175          * @param {Roo.bootstrap.UploadCropbox} this
27176          * @param {String} type
27177          */
27178         "footerbuttonclick" : true,
27179         /**
27180          * @event resize
27181          * Fire when resize
27182          * @param {Roo.bootstrap.UploadCropbox} this
27183          */
27184         "resize" : true,
27185         /**
27186          * @event rotate
27187          * Fire when rotate the image
27188          * @param {Roo.bootstrap.UploadCropbox} this
27189          * @param {String} pos
27190          */
27191         "rotate" : true,
27192         /**
27193          * @event inspect
27194          * Fire when inspect the file
27195          * @param {Roo.bootstrap.UploadCropbox} this
27196          * @param {Object} file
27197          */
27198         "inspect" : true,
27199         /**
27200          * @event upload
27201          * Fire when xhr upload the file
27202          * @param {Roo.bootstrap.UploadCropbox} this
27203          * @param {Object} data
27204          */
27205         "upload" : true,
27206         /**
27207          * @event arrange
27208          * Fire when arrange the file data
27209          * @param {Roo.bootstrap.UploadCropbox} this
27210          * @param {Object} formData
27211          */
27212         "arrange" : true
27213     });
27214     
27215     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27216 };
27217
27218 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27219     
27220     emptyText : 'Click to upload image',
27221     rotateNotify : 'Image is too small to rotate',
27222     errorTimeout : 3000,
27223     scale : 0,
27224     baseScale : 1,
27225     rotate : 0,
27226     dragable : false,
27227     pinching : false,
27228     mouseX : 0,
27229     mouseY : 0,
27230     cropData : false,
27231     minWidth : 300,
27232     minHeight : 300,
27233     file : false,
27234     exif : {},
27235     baseRotate : 1,
27236     cropType : 'image/jpeg',
27237     buttons : false,
27238     canvasLoaded : false,
27239     isDocument : false,
27240     method : 'POST',
27241     paramName : 'imageUpload',
27242     loadMask : true,
27243     loadingText : 'Loading...',
27244     maskEl : false,
27245     
27246     getAutoCreate : function()
27247     {
27248         var cfg = {
27249             tag : 'div',
27250             cls : 'roo-upload-cropbox',
27251             cn : [
27252                 {
27253                     tag : 'input',
27254                     cls : 'roo-upload-cropbox-selector',
27255                     type : 'file'
27256                 },
27257                 {
27258                     tag : 'div',
27259                     cls : 'roo-upload-cropbox-body',
27260                     style : 'cursor:pointer',
27261                     cn : [
27262                         {
27263                             tag : 'div',
27264                             cls : 'roo-upload-cropbox-preview'
27265                         },
27266                         {
27267                             tag : 'div',
27268                             cls : 'roo-upload-cropbox-thumb'
27269                         },
27270                         {
27271                             tag : 'div',
27272                             cls : 'roo-upload-cropbox-empty-notify',
27273                             html : this.emptyText
27274                         },
27275                         {
27276                             tag : 'div',
27277                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27278                             html : this.rotateNotify
27279                         }
27280                     ]
27281                 },
27282                 {
27283                     tag : 'div',
27284                     cls : 'roo-upload-cropbox-footer',
27285                     cn : {
27286                         tag : 'div',
27287                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27288                         cn : []
27289                     }
27290                 }
27291             ]
27292         };
27293         
27294         return cfg;
27295     },
27296     
27297     onRender : function(ct, position)
27298     {
27299         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27300         
27301         if (this.buttons.length) {
27302             
27303             Roo.each(this.buttons, function(bb) {
27304                 
27305                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27306                 
27307                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27308                 
27309             }, this);
27310         }
27311         
27312         if(this.loadMask){
27313             this.maskEl = this.el;
27314         }
27315     },
27316     
27317     initEvents : function()
27318     {
27319         this.urlAPI = (window.createObjectURL && window) || 
27320                                 (window.URL && URL.revokeObjectURL && URL) || 
27321                                 (window.webkitURL && webkitURL);
27322                         
27323         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27324         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27325         
27326         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27327         this.selectorEl.hide();
27328         
27329         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27330         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27331         
27332         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27333         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27334         this.thumbEl.hide();
27335         
27336         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27337         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27338         
27339         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27340         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27341         this.errorEl.hide();
27342         
27343         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27344         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27345         this.footerEl.hide();
27346         
27347         this.setThumbBoxSize();
27348         
27349         this.bind();
27350         
27351         this.resize();
27352         
27353         this.fireEvent('initial', this);
27354     },
27355
27356     bind : function()
27357     {
27358         var _this = this;
27359         
27360         window.addEventListener("resize", function() { _this.resize(); } );
27361         
27362         this.bodyEl.on('click', this.beforeSelectFile, this);
27363         
27364         if(Roo.isTouch){
27365             this.bodyEl.on('touchstart', this.onTouchStart, this);
27366             this.bodyEl.on('touchmove', this.onTouchMove, this);
27367             this.bodyEl.on('touchend', this.onTouchEnd, this);
27368         }
27369         
27370         if(!Roo.isTouch){
27371             this.bodyEl.on('mousedown', this.onMouseDown, this);
27372             this.bodyEl.on('mousemove', this.onMouseMove, this);
27373             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27374             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27375             Roo.get(document).on('mouseup', this.onMouseUp, this);
27376         }
27377         
27378         this.selectorEl.on('change', this.onFileSelected, this);
27379     },
27380     
27381     reset : function()
27382     {    
27383         this.scale = 0;
27384         this.baseScale = 1;
27385         this.rotate = 0;
27386         this.baseRotate = 1;
27387         this.dragable = false;
27388         this.pinching = false;
27389         this.mouseX = 0;
27390         this.mouseY = 0;
27391         this.cropData = false;
27392         this.notifyEl.dom.innerHTML = this.emptyText;
27393         
27394         this.selectorEl.dom.value = '';
27395         
27396     },
27397     
27398     resize : function()
27399     {
27400         if(this.fireEvent('resize', this) != false){
27401             this.setThumbBoxPosition();
27402             this.setCanvasPosition();
27403         }
27404     },
27405     
27406     onFooterButtonClick : function(e, el, o, type)
27407     {
27408         switch (type) {
27409             case 'rotate-left' :
27410                 this.onRotateLeft(e);
27411                 break;
27412             case 'rotate-right' :
27413                 this.onRotateRight(e);
27414                 break;
27415             case 'picture' :
27416                 this.beforeSelectFile(e);
27417                 break;
27418             case 'trash' :
27419                 this.trash(e);
27420                 break;
27421             case 'crop' :
27422                 this.crop(e);
27423                 break;
27424             case 'download' :
27425                 this.download(e);
27426                 break;
27427             default :
27428                 break;
27429         }
27430         
27431         this.fireEvent('footerbuttonclick', this, type);
27432     },
27433     
27434     beforeSelectFile : function(e)
27435     {
27436         e.preventDefault();
27437         
27438         if(this.fireEvent('beforeselectfile', this) != false){
27439             this.selectorEl.dom.click();
27440         }
27441     },
27442     
27443     onFileSelected : function(e)
27444     {
27445         e.preventDefault();
27446         
27447         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27448             return;
27449         }
27450         
27451         var file = this.selectorEl.dom.files[0];
27452         
27453         if(this.fireEvent('inspect', this, file) != false){
27454             this.prepare(file);
27455         }
27456         
27457     },
27458     
27459     trash : function(e)
27460     {
27461         this.fireEvent('trash', this);
27462     },
27463     
27464     download : function(e)
27465     {
27466         this.fireEvent('download', this);
27467     },
27468     
27469     loadCanvas : function(src)
27470     {   
27471         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27472             
27473             this.reset();
27474             
27475             this.imageEl = document.createElement('img');
27476             
27477             var _this = this;
27478             
27479             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27480             
27481             this.imageEl.src = src;
27482         }
27483     },
27484     
27485     onLoadCanvas : function()
27486     {   
27487         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27488         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27489         
27490         this.bodyEl.un('click', this.beforeSelectFile, this);
27491         
27492         this.notifyEl.hide();
27493         this.thumbEl.show();
27494         this.footerEl.show();
27495         
27496         this.baseRotateLevel();
27497         
27498         if(this.isDocument){
27499             this.setThumbBoxSize();
27500         }
27501         
27502         this.setThumbBoxPosition();
27503         
27504         this.baseScaleLevel();
27505         
27506         this.draw();
27507         
27508         this.resize();
27509         
27510         this.canvasLoaded = true;
27511         
27512         if(this.loadMask){
27513             this.maskEl.unmask();
27514         }
27515         
27516     },
27517     
27518     setCanvasPosition : function()
27519     {   
27520         if(!this.canvasEl){
27521             return;
27522         }
27523         
27524         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27525         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27526         
27527         this.previewEl.setLeft(pw);
27528         this.previewEl.setTop(ph);
27529         
27530     },
27531     
27532     onMouseDown : function(e)
27533     {   
27534         e.stopEvent();
27535         
27536         this.dragable = true;
27537         this.pinching = false;
27538         
27539         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27540             this.dragable = false;
27541             return;
27542         }
27543         
27544         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27545         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27546         
27547     },
27548     
27549     onMouseMove : function(e)
27550     {   
27551         e.stopEvent();
27552         
27553         if(!this.canvasLoaded){
27554             return;
27555         }
27556         
27557         if (!this.dragable){
27558             return;
27559         }
27560         
27561         var minX = Math.ceil(this.thumbEl.getLeft(true));
27562         var minY = Math.ceil(this.thumbEl.getTop(true));
27563         
27564         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27565         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27566         
27567         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27568         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27569         
27570         x = x - this.mouseX;
27571         y = y - this.mouseY;
27572         
27573         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27574         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27575         
27576         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27577         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27578         
27579         this.previewEl.setLeft(bgX);
27580         this.previewEl.setTop(bgY);
27581         
27582         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27583         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27584     },
27585     
27586     onMouseUp : function(e)
27587     {   
27588         e.stopEvent();
27589         
27590         this.dragable = false;
27591     },
27592     
27593     onMouseWheel : function(e)
27594     {   
27595         e.stopEvent();
27596         
27597         this.startScale = this.scale;
27598         
27599         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27600         
27601         if(!this.zoomable()){
27602             this.scale = this.startScale;
27603             return;
27604         }
27605         
27606         this.draw();
27607         
27608         return;
27609     },
27610     
27611     zoomable : function()
27612     {
27613         var minScale = this.thumbEl.getWidth() / this.minWidth;
27614         
27615         if(this.minWidth < this.minHeight){
27616             minScale = this.thumbEl.getHeight() / this.minHeight;
27617         }
27618         
27619         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27620         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27621         
27622         if(
27623                 this.isDocument &&
27624                 (this.rotate == 0 || this.rotate == 180) && 
27625                 (
27626                     width > this.imageEl.OriginWidth || 
27627                     height > this.imageEl.OriginHeight ||
27628                     (width < this.minWidth && height < this.minHeight)
27629                 )
27630         ){
27631             return false;
27632         }
27633         
27634         if(
27635                 this.isDocument &&
27636                 (this.rotate == 90 || this.rotate == 270) && 
27637                 (
27638                     width > this.imageEl.OriginWidth || 
27639                     height > this.imageEl.OriginHeight ||
27640                     (width < this.minHeight && height < this.minWidth)
27641                 )
27642         ){
27643             return false;
27644         }
27645         
27646         if(
27647                 !this.isDocument &&
27648                 (this.rotate == 0 || this.rotate == 180) && 
27649                 (
27650                     width < this.minWidth || 
27651                     width > this.imageEl.OriginWidth || 
27652                     height < this.minHeight || 
27653                     height > this.imageEl.OriginHeight
27654                 )
27655         ){
27656             return false;
27657         }
27658         
27659         if(
27660                 !this.isDocument &&
27661                 (this.rotate == 90 || this.rotate == 270) && 
27662                 (
27663                     width < this.minHeight || 
27664                     width > this.imageEl.OriginWidth || 
27665                     height < this.minWidth || 
27666                     height > this.imageEl.OriginHeight
27667                 )
27668         ){
27669             return false;
27670         }
27671         
27672         return true;
27673         
27674     },
27675     
27676     onRotateLeft : function(e)
27677     {   
27678         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27679             
27680             var minScale = this.thumbEl.getWidth() / this.minWidth;
27681             
27682             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27683             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27684             
27685             this.startScale = this.scale;
27686             
27687             while (this.getScaleLevel() < minScale){
27688             
27689                 this.scale = this.scale + 1;
27690                 
27691                 if(!this.zoomable()){
27692                     break;
27693                 }
27694                 
27695                 if(
27696                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27697                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27698                 ){
27699                     continue;
27700                 }
27701                 
27702                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27703
27704                 this.draw();
27705                 
27706                 return;
27707             }
27708             
27709             this.scale = this.startScale;
27710             
27711             this.onRotateFail();
27712             
27713             return false;
27714         }
27715         
27716         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27717
27718         if(this.isDocument){
27719             this.setThumbBoxSize();
27720             this.setThumbBoxPosition();
27721             this.setCanvasPosition();
27722         }
27723         
27724         this.draw();
27725         
27726         this.fireEvent('rotate', this, 'left');
27727         
27728     },
27729     
27730     onRotateRight : function(e)
27731     {
27732         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27733             
27734             var minScale = this.thumbEl.getWidth() / this.minWidth;
27735         
27736             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27737             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27738             
27739             this.startScale = this.scale;
27740             
27741             while (this.getScaleLevel() < minScale){
27742             
27743                 this.scale = this.scale + 1;
27744                 
27745                 if(!this.zoomable()){
27746                     break;
27747                 }
27748                 
27749                 if(
27750                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27751                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27752                 ){
27753                     continue;
27754                 }
27755                 
27756                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27757
27758                 this.draw();
27759                 
27760                 return;
27761             }
27762             
27763             this.scale = this.startScale;
27764             
27765             this.onRotateFail();
27766             
27767             return false;
27768         }
27769         
27770         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27771
27772         if(this.isDocument){
27773             this.setThumbBoxSize();
27774             this.setThumbBoxPosition();
27775             this.setCanvasPosition();
27776         }
27777         
27778         this.draw();
27779         
27780         this.fireEvent('rotate', this, 'right');
27781     },
27782     
27783     onRotateFail : function()
27784     {
27785         this.errorEl.show(true);
27786         
27787         var _this = this;
27788         
27789         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27790     },
27791     
27792     draw : function()
27793     {
27794         this.previewEl.dom.innerHTML = '';
27795         
27796         var canvasEl = document.createElement("canvas");
27797         
27798         var contextEl = canvasEl.getContext("2d");
27799         
27800         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27801         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27802         var center = this.imageEl.OriginWidth / 2;
27803         
27804         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27805             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27806             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27807             center = this.imageEl.OriginHeight / 2;
27808         }
27809         
27810         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27811         
27812         contextEl.translate(center, center);
27813         contextEl.rotate(this.rotate * Math.PI / 180);
27814
27815         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27816         
27817         this.canvasEl = document.createElement("canvas");
27818         
27819         this.contextEl = this.canvasEl.getContext("2d");
27820         
27821         switch (this.rotate) {
27822             case 0 :
27823                 
27824                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27825                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27826                 
27827                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27828                 
27829                 break;
27830             case 90 : 
27831                 
27832                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27833                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27834                 
27835                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27836                     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);
27837                     break;
27838                 }
27839                 
27840                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27841                 
27842                 break;
27843             case 180 :
27844                 
27845                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27846                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27847                 
27848                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27849                     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);
27850                     break;
27851                 }
27852                 
27853                 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);
27854                 
27855                 break;
27856             case 270 :
27857                 
27858                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27859                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27860         
27861                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27862                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27863                     break;
27864                 }
27865                 
27866                 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);
27867                 
27868                 break;
27869             default : 
27870                 break;
27871         }
27872         
27873         this.previewEl.appendChild(this.canvasEl);
27874         
27875         this.setCanvasPosition();
27876     },
27877     
27878     crop : function()
27879     {
27880         if(!this.canvasLoaded){
27881             return;
27882         }
27883         
27884         var imageCanvas = document.createElement("canvas");
27885         
27886         var imageContext = imageCanvas.getContext("2d");
27887         
27888         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27889         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27890         
27891         var center = imageCanvas.width / 2;
27892         
27893         imageContext.translate(center, center);
27894         
27895         imageContext.rotate(this.rotate * Math.PI / 180);
27896         
27897         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27898         
27899         var canvas = document.createElement("canvas");
27900         
27901         var context = canvas.getContext("2d");
27902                 
27903         canvas.width = this.minWidth;
27904         canvas.height = this.minHeight;
27905
27906         switch (this.rotate) {
27907             case 0 :
27908                 
27909                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27910                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27911                 
27912                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27913                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27914                 
27915                 var targetWidth = this.minWidth - 2 * x;
27916                 var targetHeight = this.minHeight - 2 * y;
27917                 
27918                 var scale = 1;
27919                 
27920                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27921                     scale = targetWidth / width;
27922                 }
27923                 
27924                 if(x > 0 && y == 0){
27925                     scale = targetHeight / height;
27926                 }
27927                 
27928                 if(x > 0 && y > 0){
27929                     scale = targetWidth / width;
27930                     
27931                     if(width < height){
27932                         scale = targetHeight / height;
27933                     }
27934                 }
27935                 
27936                 context.scale(scale, scale);
27937                 
27938                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27939                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27940
27941                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27942                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27943
27944                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27945                 
27946                 break;
27947             case 90 : 
27948                 
27949                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27950                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27951                 
27952                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27953                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27954                 
27955                 var targetWidth = this.minWidth - 2 * x;
27956                 var targetHeight = this.minHeight - 2 * y;
27957                 
27958                 var scale = 1;
27959                 
27960                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27961                     scale = targetWidth / width;
27962                 }
27963                 
27964                 if(x > 0 && y == 0){
27965                     scale = targetHeight / height;
27966                 }
27967                 
27968                 if(x > 0 && y > 0){
27969                     scale = targetWidth / width;
27970                     
27971                     if(width < height){
27972                         scale = targetHeight / height;
27973                     }
27974                 }
27975                 
27976                 context.scale(scale, scale);
27977                 
27978                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27979                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27980
27981                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27982                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27983                 
27984                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27985                 
27986                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27987                 
27988                 break;
27989             case 180 :
27990                 
27991                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27992                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27993                 
27994                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27995                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27996                 
27997                 var targetWidth = this.minWidth - 2 * x;
27998                 var targetHeight = this.minHeight - 2 * y;
27999                 
28000                 var scale = 1;
28001                 
28002                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28003                     scale = targetWidth / width;
28004                 }
28005                 
28006                 if(x > 0 && y == 0){
28007                     scale = targetHeight / height;
28008                 }
28009                 
28010                 if(x > 0 && y > 0){
28011                     scale = targetWidth / width;
28012                     
28013                     if(width < height){
28014                         scale = targetHeight / height;
28015                     }
28016                 }
28017                 
28018                 context.scale(scale, scale);
28019                 
28020                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28021                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28022
28023                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28024                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28025
28026                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28027                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28028                 
28029                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28030                 
28031                 break;
28032             case 270 :
28033                 
28034                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28035                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28036                 
28037                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28038                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28039                 
28040                 var targetWidth = this.minWidth - 2 * x;
28041                 var targetHeight = this.minHeight - 2 * y;
28042                 
28043                 var scale = 1;
28044                 
28045                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28046                     scale = targetWidth / width;
28047                 }
28048                 
28049                 if(x > 0 && y == 0){
28050                     scale = targetHeight / height;
28051                 }
28052                 
28053                 if(x > 0 && y > 0){
28054                     scale = targetWidth / width;
28055                     
28056                     if(width < height){
28057                         scale = targetHeight / height;
28058                     }
28059                 }
28060                 
28061                 context.scale(scale, scale);
28062                 
28063                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28064                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28065
28066                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28067                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28068                 
28069                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28070                 
28071                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28072                 
28073                 break;
28074             default : 
28075                 break;
28076         }
28077         
28078         this.cropData = canvas.toDataURL(this.cropType);
28079         
28080         if(this.fireEvent('crop', this, this.cropData) !== false){
28081             this.process(this.file, this.cropData);
28082         }
28083         
28084         return;
28085         
28086     },
28087     
28088     setThumbBoxSize : function()
28089     {
28090         var width, height;
28091         
28092         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28093             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28094             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28095             
28096             this.minWidth = width;
28097             this.minHeight = height;
28098             
28099             if(this.rotate == 90 || this.rotate == 270){
28100                 this.minWidth = height;
28101                 this.minHeight = width;
28102             }
28103         }
28104         
28105         height = 300;
28106         width = Math.ceil(this.minWidth * height / this.minHeight);
28107         
28108         if(this.minWidth > this.minHeight){
28109             width = 300;
28110             height = Math.ceil(this.minHeight * width / this.minWidth);
28111         }
28112         
28113         this.thumbEl.setStyle({
28114             width : width + 'px',
28115             height : height + 'px'
28116         });
28117
28118         return;
28119             
28120     },
28121     
28122     setThumbBoxPosition : function()
28123     {
28124         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28125         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28126         
28127         this.thumbEl.setLeft(x);
28128         this.thumbEl.setTop(y);
28129         
28130     },
28131     
28132     baseRotateLevel : function()
28133     {
28134         this.baseRotate = 1;
28135         
28136         if(
28137                 typeof(this.exif) != 'undefined' &&
28138                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28139                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28140         ){
28141             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28142         }
28143         
28144         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28145         
28146     },
28147     
28148     baseScaleLevel : function()
28149     {
28150         var width, height;
28151         
28152         if(this.isDocument){
28153             
28154             if(this.baseRotate == 6 || this.baseRotate == 8){
28155             
28156                 height = this.thumbEl.getHeight();
28157                 this.baseScale = height / this.imageEl.OriginWidth;
28158
28159                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28160                     width = this.thumbEl.getWidth();
28161                     this.baseScale = width / this.imageEl.OriginHeight;
28162                 }
28163
28164                 return;
28165             }
28166
28167             height = this.thumbEl.getHeight();
28168             this.baseScale = height / this.imageEl.OriginHeight;
28169
28170             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28171                 width = this.thumbEl.getWidth();
28172                 this.baseScale = width / this.imageEl.OriginWidth;
28173             }
28174
28175             return;
28176         }
28177         
28178         if(this.baseRotate == 6 || this.baseRotate == 8){
28179             
28180             width = this.thumbEl.getHeight();
28181             this.baseScale = width / this.imageEl.OriginHeight;
28182             
28183             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28184                 height = this.thumbEl.getWidth();
28185                 this.baseScale = height / this.imageEl.OriginHeight;
28186             }
28187             
28188             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28189                 height = this.thumbEl.getWidth();
28190                 this.baseScale = height / this.imageEl.OriginHeight;
28191                 
28192                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28193                     width = this.thumbEl.getHeight();
28194                     this.baseScale = width / this.imageEl.OriginWidth;
28195                 }
28196             }
28197             
28198             return;
28199         }
28200         
28201         width = this.thumbEl.getWidth();
28202         this.baseScale = width / this.imageEl.OriginWidth;
28203         
28204         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28205             height = this.thumbEl.getHeight();
28206             this.baseScale = height / this.imageEl.OriginHeight;
28207         }
28208         
28209         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28210             
28211             height = this.thumbEl.getHeight();
28212             this.baseScale = height / this.imageEl.OriginHeight;
28213             
28214             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28215                 width = this.thumbEl.getWidth();
28216                 this.baseScale = width / this.imageEl.OriginWidth;
28217             }
28218             
28219         }
28220         
28221         return;
28222     },
28223     
28224     getScaleLevel : function()
28225     {
28226         return this.baseScale * Math.pow(1.1, this.scale);
28227     },
28228     
28229     onTouchStart : function(e)
28230     {
28231         if(!this.canvasLoaded){
28232             this.beforeSelectFile(e);
28233             return;
28234         }
28235         
28236         var touches = e.browserEvent.touches;
28237         
28238         if(!touches){
28239             return;
28240         }
28241         
28242         if(touches.length == 1){
28243             this.onMouseDown(e);
28244             return;
28245         }
28246         
28247         if(touches.length != 2){
28248             return;
28249         }
28250         
28251         var coords = [];
28252         
28253         for(var i = 0, finger; finger = touches[i]; i++){
28254             coords.push(finger.pageX, finger.pageY);
28255         }
28256         
28257         var x = Math.pow(coords[0] - coords[2], 2);
28258         var y = Math.pow(coords[1] - coords[3], 2);
28259         
28260         this.startDistance = Math.sqrt(x + y);
28261         
28262         this.startScale = this.scale;
28263         
28264         this.pinching = true;
28265         this.dragable = false;
28266         
28267     },
28268     
28269     onTouchMove : function(e)
28270     {
28271         if(!this.pinching && !this.dragable){
28272             return;
28273         }
28274         
28275         var touches = e.browserEvent.touches;
28276         
28277         if(!touches){
28278             return;
28279         }
28280         
28281         if(this.dragable){
28282             this.onMouseMove(e);
28283             return;
28284         }
28285         
28286         var coords = [];
28287         
28288         for(var i = 0, finger; finger = touches[i]; i++){
28289             coords.push(finger.pageX, finger.pageY);
28290         }
28291         
28292         var x = Math.pow(coords[0] - coords[2], 2);
28293         var y = Math.pow(coords[1] - coords[3], 2);
28294         
28295         this.endDistance = Math.sqrt(x + y);
28296         
28297         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28298         
28299         if(!this.zoomable()){
28300             this.scale = this.startScale;
28301             return;
28302         }
28303         
28304         this.draw();
28305         
28306     },
28307     
28308     onTouchEnd : function(e)
28309     {
28310         this.pinching = false;
28311         this.dragable = false;
28312         
28313     },
28314     
28315     process : function(file, crop)
28316     {
28317         if(this.loadMask){
28318             this.maskEl.mask(this.loadingText);
28319         }
28320         
28321         this.xhr = new XMLHttpRequest();
28322         
28323         file.xhr = this.xhr;
28324
28325         this.xhr.open(this.method, this.url, true);
28326         
28327         var headers = {
28328             "Accept": "application/json",
28329             "Cache-Control": "no-cache",
28330             "X-Requested-With": "XMLHttpRequest"
28331         };
28332         
28333         for (var headerName in headers) {
28334             var headerValue = headers[headerName];
28335             if (headerValue) {
28336                 this.xhr.setRequestHeader(headerName, headerValue);
28337             }
28338         }
28339         
28340         var _this = this;
28341         
28342         this.xhr.onload = function()
28343         {
28344             _this.xhrOnLoad(_this.xhr);
28345         }
28346         
28347         this.xhr.onerror = function()
28348         {
28349             _this.xhrOnError(_this.xhr);
28350         }
28351         
28352         var formData = new FormData();
28353
28354         formData.append('returnHTML', 'NO');
28355         
28356         if(crop){
28357             formData.append('crop', crop);
28358         }
28359         
28360         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28361             formData.append(this.paramName, file, file.name);
28362         }
28363         
28364         if(typeof(file.filename) != 'undefined'){
28365             formData.append('filename', file.filename);
28366         }
28367         
28368         if(typeof(file.mimetype) != 'undefined'){
28369             formData.append('mimetype', file.mimetype);
28370         }
28371         
28372         if(this.fireEvent('arrange', this, formData) != false){
28373             this.xhr.send(formData);
28374         };
28375     },
28376     
28377     xhrOnLoad : function(xhr)
28378     {
28379         if(this.loadMask){
28380             this.maskEl.unmask();
28381         }
28382         
28383         if (xhr.readyState !== 4) {
28384             this.fireEvent('exception', this, xhr);
28385             return;
28386         }
28387
28388         var response = Roo.decode(xhr.responseText);
28389         
28390         if(!response.success){
28391             this.fireEvent('exception', this, xhr);
28392             return;
28393         }
28394         
28395         var response = Roo.decode(xhr.responseText);
28396         
28397         this.fireEvent('upload', this, response);
28398         
28399     },
28400     
28401     xhrOnError : function()
28402     {
28403         if(this.loadMask){
28404             this.maskEl.unmask();
28405         }
28406         
28407         Roo.log('xhr on error');
28408         
28409         var response = Roo.decode(xhr.responseText);
28410           
28411         Roo.log(response);
28412         
28413     },
28414     
28415     prepare : function(file)
28416     {   
28417         if(this.loadMask){
28418             this.maskEl.mask(this.loadingText);
28419         }
28420         
28421         this.file = false;
28422         this.exif = {};
28423         
28424         if(typeof(file) === 'string'){
28425             this.loadCanvas(file);
28426             return;
28427         }
28428         
28429         if(!file || !this.urlAPI){
28430             return;
28431         }
28432         
28433         this.file = file;
28434         this.cropType = file.type;
28435         
28436         var _this = this;
28437         
28438         if(this.fireEvent('prepare', this, this.file) != false){
28439             
28440             var reader = new FileReader();
28441             
28442             reader.onload = function (e) {
28443                 if (e.target.error) {
28444                     Roo.log(e.target.error);
28445                     return;
28446                 }
28447                 
28448                 var buffer = e.target.result,
28449                     dataView = new DataView(buffer),
28450                     offset = 2,
28451                     maxOffset = dataView.byteLength - 4,
28452                     markerBytes,
28453                     markerLength;
28454                 
28455                 if (dataView.getUint16(0) === 0xffd8) {
28456                     while (offset < maxOffset) {
28457                         markerBytes = dataView.getUint16(offset);
28458                         
28459                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28460                             markerLength = dataView.getUint16(offset + 2) + 2;
28461                             if (offset + markerLength > dataView.byteLength) {
28462                                 Roo.log('Invalid meta data: Invalid segment size.');
28463                                 break;
28464                             }
28465                             
28466                             if(markerBytes == 0xffe1){
28467                                 _this.parseExifData(
28468                                     dataView,
28469                                     offset,
28470                                     markerLength
28471                                 );
28472                             }
28473                             
28474                             offset += markerLength;
28475                             
28476                             continue;
28477                         }
28478                         
28479                         break;
28480                     }
28481                     
28482                 }
28483                 
28484                 var url = _this.urlAPI.createObjectURL(_this.file);
28485                 
28486                 _this.loadCanvas(url);
28487                 
28488                 return;
28489             }
28490             
28491             reader.readAsArrayBuffer(this.file);
28492             
28493         }
28494         
28495     },
28496     
28497     parseExifData : function(dataView, offset, length)
28498     {
28499         var tiffOffset = offset + 10,
28500             littleEndian,
28501             dirOffset;
28502     
28503         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28504             // No Exif data, might be XMP data instead
28505             return;
28506         }
28507         
28508         // Check for the ASCII code for "Exif" (0x45786966):
28509         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28510             // No Exif data, might be XMP data instead
28511             return;
28512         }
28513         if (tiffOffset + 8 > dataView.byteLength) {
28514             Roo.log('Invalid Exif data: Invalid segment size.');
28515             return;
28516         }
28517         // Check for the two null bytes:
28518         if (dataView.getUint16(offset + 8) !== 0x0000) {
28519             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28520             return;
28521         }
28522         // Check the byte alignment:
28523         switch (dataView.getUint16(tiffOffset)) {
28524         case 0x4949:
28525             littleEndian = true;
28526             break;
28527         case 0x4D4D:
28528             littleEndian = false;
28529             break;
28530         default:
28531             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28532             return;
28533         }
28534         // Check for the TIFF tag marker (0x002A):
28535         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28536             Roo.log('Invalid Exif data: Missing TIFF marker.');
28537             return;
28538         }
28539         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28540         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28541         
28542         this.parseExifTags(
28543             dataView,
28544             tiffOffset,
28545             tiffOffset + dirOffset,
28546             littleEndian
28547         );
28548     },
28549     
28550     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28551     {
28552         var tagsNumber,
28553             dirEndOffset,
28554             i;
28555         if (dirOffset + 6 > dataView.byteLength) {
28556             Roo.log('Invalid Exif data: Invalid directory offset.');
28557             return;
28558         }
28559         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28560         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28561         if (dirEndOffset + 4 > dataView.byteLength) {
28562             Roo.log('Invalid Exif data: Invalid directory size.');
28563             return;
28564         }
28565         for (i = 0; i < tagsNumber; i += 1) {
28566             this.parseExifTag(
28567                 dataView,
28568                 tiffOffset,
28569                 dirOffset + 2 + 12 * i, // tag offset
28570                 littleEndian
28571             );
28572         }
28573         // Return the offset to the next directory:
28574         return dataView.getUint32(dirEndOffset, littleEndian);
28575     },
28576     
28577     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28578     {
28579         var tag = dataView.getUint16(offset, littleEndian);
28580         
28581         this.exif[tag] = this.getExifValue(
28582             dataView,
28583             tiffOffset,
28584             offset,
28585             dataView.getUint16(offset + 2, littleEndian), // tag type
28586             dataView.getUint32(offset + 4, littleEndian), // tag length
28587             littleEndian
28588         );
28589     },
28590     
28591     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28592     {
28593         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28594             tagSize,
28595             dataOffset,
28596             values,
28597             i,
28598             str,
28599             c;
28600     
28601         if (!tagType) {
28602             Roo.log('Invalid Exif data: Invalid tag type.');
28603             return;
28604         }
28605         
28606         tagSize = tagType.size * length;
28607         // Determine if the value is contained in the dataOffset bytes,
28608         // or if the value at the dataOffset is a pointer to the actual data:
28609         dataOffset = tagSize > 4 ?
28610                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28611         if (dataOffset + tagSize > dataView.byteLength) {
28612             Roo.log('Invalid Exif data: Invalid data offset.');
28613             return;
28614         }
28615         if (length === 1) {
28616             return tagType.getValue(dataView, dataOffset, littleEndian);
28617         }
28618         values = [];
28619         for (i = 0; i < length; i += 1) {
28620             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28621         }
28622         
28623         if (tagType.ascii) {
28624             str = '';
28625             // Concatenate the chars:
28626             for (i = 0; i < values.length; i += 1) {
28627                 c = values[i];
28628                 // Ignore the terminating NULL byte(s):
28629                 if (c === '\u0000') {
28630                     break;
28631                 }
28632                 str += c;
28633             }
28634             return str;
28635         }
28636         return values;
28637     }
28638     
28639 });
28640
28641 Roo.apply(Roo.bootstrap.UploadCropbox, {
28642     tags : {
28643         'Orientation': 0x0112
28644     },
28645     
28646     Orientation: {
28647             1: 0, //'top-left',
28648 //            2: 'top-right',
28649             3: 180, //'bottom-right',
28650 //            4: 'bottom-left',
28651 //            5: 'left-top',
28652             6: 90, //'right-top',
28653 //            7: 'right-bottom',
28654             8: 270 //'left-bottom'
28655     },
28656     
28657     exifTagTypes : {
28658         // byte, 8-bit unsigned int:
28659         1: {
28660             getValue: function (dataView, dataOffset) {
28661                 return dataView.getUint8(dataOffset);
28662             },
28663             size: 1
28664         },
28665         // ascii, 8-bit byte:
28666         2: {
28667             getValue: function (dataView, dataOffset) {
28668                 return String.fromCharCode(dataView.getUint8(dataOffset));
28669             },
28670             size: 1,
28671             ascii: true
28672         },
28673         // short, 16 bit int:
28674         3: {
28675             getValue: function (dataView, dataOffset, littleEndian) {
28676                 return dataView.getUint16(dataOffset, littleEndian);
28677             },
28678             size: 2
28679         },
28680         // long, 32 bit int:
28681         4: {
28682             getValue: function (dataView, dataOffset, littleEndian) {
28683                 return dataView.getUint32(dataOffset, littleEndian);
28684             },
28685             size: 4
28686         },
28687         // rational = two long values, first is numerator, second is denominator:
28688         5: {
28689             getValue: function (dataView, dataOffset, littleEndian) {
28690                 return dataView.getUint32(dataOffset, littleEndian) /
28691                     dataView.getUint32(dataOffset + 4, littleEndian);
28692             },
28693             size: 8
28694         },
28695         // slong, 32 bit signed int:
28696         9: {
28697             getValue: function (dataView, dataOffset, littleEndian) {
28698                 return dataView.getInt32(dataOffset, littleEndian);
28699             },
28700             size: 4
28701         },
28702         // srational, two slongs, first is numerator, second is denominator:
28703         10: {
28704             getValue: function (dataView, dataOffset, littleEndian) {
28705                 return dataView.getInt32(dataOffset, littleEndian) /
28706                     dataView.getInt32(dataOffset + 4, littleEndian);
28707             },
28708             size: 8
28709         }
28710     },
28711     
28712     footer : {
28713         STANDARD : [
28714             {
28715                 tag : 'div',
28716                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28717                 action : 'rotate-left',
28718                 cn : [
28719                     {
28720                         tag : 'button',
28721                         cls : 'btn btn-default',
28722                         html : '<i class="fa fa-undo"></i>'
28723                     }
28724                 ]
28725             },
28726             {
28727                 tag : 'div',
28728                 cls : 'btn-group roo-upload-cropbox-picture',
28729                 action : 'picture',
28730                 cn : [
28731                     {
28732                         tag : 'button',
28733                         cls : 'btn btn-default',
28734                         html : '<i class="fa fa-picture-o"></i>'
28735                     }
28736                 ]
28737             },
28738             {
28739                 tag : 'div',
28740                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28741                 action : 'rotate-right',
28742                 cn : [
28743                     {
28744                         tag : 'button',
28745                         cls : 'btn btn-default',
28746                         html : '<i class="fa fa-repeat"></i>'
28747                     }
28748                 ]
28749             }
28750         ],
28751         DOCUMENT : [
28752             {
28753                 tag : 'div',
28754                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28755                 action : 'rotate-left',
28756                 cn : [
28757                     {
28758                         tag : 'button',
28759                         cls : 'btn btn-default',
28760                         html : '<i class="fa fa-undo"></i>'
28761                     }
28762                 ]
28763             },
28764             {
28765                 tag : 'div',
28766                 cls : 'btn-group roo-upload-cropbox-download',
28767                 action : 'download',
28768                 cn : [
28769                     {
28770                         tag : 'button',
28771                         cls : 'btn btn-default',
28772                         html : '<i class="fa fa-download"></i>'
28773                     }
28774                 ]
28775             },
28776             {
28777                 tag : 'div',
28778                 cls : 'btn-group roo-upload-cropbox-crop',
28779                 action : 'crop',
28780                 cn : [
28781                     {
28782                         tag : 'button',
28783                         cls : 'btn btn-default',
28784                         html : '<i class="fa fa-crop"></i>'
28785                     }
28786                 ]
28787             },
28788             {
28789                 tag : 'div',
28790                 cls : 'btn-group roo-upload-cropbox-trash',
28791                 action : 'trash',
28792                 cn : [
28793                     {
28794                         tag : 'button',
28795                         cls : 'btn btn-default',
28796                         html : '<i class="fa fa-trash"></i>'
28797                     }
28798                 ]
28799             },
28800             {
28801                 tag : 'div',
28802                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28803                 action : 'rotate-right',
28804                 cn : [
28805                     {
28806                         tag : 'button',
28807                         cls : 'btn btn-default',
28808                         html : '<i class="fa fa-repeat"></i>'
28809                     }
28810                 ]
28811             }
28812         ],
28813         ROTATOR : [
28814             {
28815                 tag : 'div',
28816                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28817                 action : 'rotate-left',
28818                 cn : [
28819                     {
28820                         tag : 'button',
28821                         cls : 'btn btn-default',
28822                         html : '<i class="fa fa-undo"></i>'
28823                     }
28824                 ]
28825             },
28826             {
28827                 tag : 'div',
28828                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28829                 action : 'rotate-right',
28830                 cn : [
28831                     {
28832                         tag : 'button',
28833                         cls : 'btn btn-default',
28834                         html : '<i class="fa fa-repeat"></i>'
28835                     }
28836                 ]
28837             }
28838         ]
28839     }
28840 });
28841
28842 /*
28843 * Licence: LGPL
28844 */
28845
28846 /**
28847  * @class Roo.bootstrap.DocumentManager
28848  * @extends Roo.bootstrap.Component
28849  * Bootstrap DocumentManager class
28850  * @cfg {String} paramName default 'imageUpload'
28851  * @cfg {String} toolTipName default 'filename'
28852  * @cfg {String} method default POST
28853  * @cfg {String} url action url
28854  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28855  * @cfg {Boolean} multiple multiple upload default true
28856  * @cfg {Number} thumbSize default 300
28857  * @cfg {String} fieldLabel
28858  * @cfg {Number} labelWidth default 4
28859  * @cfg {String} labelAlign (left|top) default left
28860  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28861 * @cfg {Number} labellg set the width of label (1-12)
28862  * @cfg {Number} labelmd set the width of label (1-12)
28863  * @cfg {Number} labelsm set the width of label (1-12)
28864  * @cfg {Number} labelxs set the width of label (1-12)
28865  * 
28866  * @constructor
28867  * Create a new DocumentManager
28868  * @param {Object} config The config object
28869  */
28870
28871 Roo.bootstrap.DocumentManager = function(config){
28872     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28873     
28874     this.files = [];
28875     this.delegates = [];
28876     
28877     this.addEvents({
28878         /**
28879          * @event initial
28880          * Fire when initial the DocumentManager
28881          * @param {Roo.bootstrap.DocumentManager} this
28882          */
28883         "initial" : true,
28884         /**
28885          * @event inspect
28886          * inspect selected file
28887          * @param {Roo.bootstrap.DocumentManager} this
28888          * @param {File} file
28889          */
28890         "inspect" : true,
28891         /**
28892          * @event exception
28893          * Fire when xhr load exception
28894          * @param {Roo.bootstrap.DocumentManager} this
28895          * @param {XMLHttpRequest} xhr
28896          */
28897         "exception" : true,
28898         /**
28899          * @event afterupload
28900          * Fire when xhr load exception
28901          * @param {Roo.bootstrap.DocumentManager} this
28902          * @param {XMLHttpRequest} xhr
28903          */
28904         "afterupload" : true,
28905         /**
28906          * @event prepare
28907          * prepare the form data
28908          * @param {Roo.bootstrap.DocumentManager} this
28909          * @param {Object} formData
28910          */
28911         "prepare" : true,
28912         /**
28913          * @event remove
28914          * Fire when remove the file
28915          * @param {Roo.bootstrap.DocumentManager} this
28916          * @param {Object} file
28917          */
28918         "remove" : true,
28919         /**
28920          * @event refresh
28921          * Fire after refresh the file
28922          * @param {Roo.bootstrap.DocumentManager} this
28923          */
28924         "refresh" : true,
28925         /**
28926          * @event click
28927          * Fire after click the image
28928          * @param {Roo.bootstrap.DocumentManager} this
28929          * @param {Object} file
28930          */
28931         "click" : true,
28932         /**
28933          * @event edit
28934          * Fire when upload a image and editable set to true
28935          * @param {Roo.bootstrap.DocumentManager} this
28936          * @param {Object} file
28937          */
28938         "edit" : true,
28939         /**
28940          * @event beforeselectfile
28941          * Fire before select file
28942          * @param {Roo.bootstrap.DocumentManager} this
28943          */
28944         "beforeselectfile" : true,
28945         /**
28946          * @event process
28947          * Fire before process file
28948          * @param {Roo.bootstrap.DocumentManager} this
28949          * @param {Object} file
28950          */
28951         "process" : true,
28952         /**
28953          * @event previewrendered
28954          * Fire when preview rendered
28955          * @param {Roo.bootstrap.DocumentManager} this
28956          * @param {Object} file
28957          */
28958         "previewrendered" : true,
28959         /**
28960          */
28961         "previewResize" : true
28962         
28963     });
28964 };
28965
28966 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28967     
28968     boxes : 0,
28969     inputName : '',
28970     thumbSize : 300,
28971     multiple : true,
28972     files : false,
28973     method : 'POST',
28974     url : '',
28975     paramName : 'imageUpload',
28976     toolTipName : 'filename',
28977     fieldLabel : '',
28978     labelWidth : 4,
28979     labelAlign : 'left',
28980     editable : true,
28981     delegates : false,
28982     xhr : false, 
28983     
28984     labellg : 0,
28985     labelmd : 0,
28986     labelsm : 0,
28987     labelxs : 0,
28988     
28989     getAutoCreate : function()
28990     {   
28991         var managerWidget = {
28992             tag : 'div',
28993             cls : 'roo-document-manager',
28994             cn : [
28995                 {
28996                     tag : 'input',
28997                     cls : 'roo-document-manager-selector',
28998                     type : 'file'
28999                 },
29000                 {
29001                     tag : 'div',
29002                     cls : 'roo-document-manager-uploader',
29003                     cn : [
29004                         {
29005                             tag : 'div',
29006                             cls : 'roo-document-manager-upload-btn',
29007                             html : '<i class="fa fa-plus"></i>'
29008                         }
29009                     ]
29010                     
29011                 }
29012             ]
29013         };
29014         
29015         var content = [
29016             {
29017                 tag : 'div',
29018                 cls : 'column col-md-12',
29019                 cn : managerWidget
29020             }
29021         ];
29022         
29023         if(this.fieldLabel.length){
29024             
29025             content = [
29026                 {
29027                     tag : 'div',
29028                     cls : 'column col-md-12',
29029                     html : this.fieldLabel
29030                 },
29031                 {
29032                     tag : 'div',
29033                     cls : 'column col-md-12',
29034                     cn : managerWidget
29035                 }
29036             ];
29037
29038             if(this.labelAlign == 'left'){
29039                 content = [
29040                     {
29041                         tag : 'div',
29042                         cls : 'column',
29043                         html : this.fieldLabel
29044                     },
29045                     {
29046                         tag : 'div',
29047                         cls : 'column',
29048                         cn : managerWidget
29049                     }
29050                 ];
29051                 
29052                 if(this.labelWidth > 12){
29053                     content[0].style = "width: " + this.labelWidth + 'px';
29054                 }
29055
29056                 if(this.labelWidth < 13 && this.labelmd == 0){
29057                     this.labelmd = this.labelWidth;
29058                 }
29059
29060                 if(this.labellg > 0){
29061                     content[0].cls += ' col-lg-' + this.labellg;
29062                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29063                 }
29064
29065                 if(this.labelmd > 0){
29066                     content[0].cls += ' col-md-' + this.labelmd;
29067                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29068                 }
29069
29070                 if(this.labelsm > 0){
29071                     content[0].cls += ' col-sm-' + this.labelsm;
29072                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29073                 }
29074
29075                 if(this.labelxs > 0){
29076                     content[0].cls += ' col-xs-' + this.labelxs;
29077                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29078                 }
29079                 
29080             }
29081         }
29082         
29083         var cfg = {
29084             tag : 'div',
29085             cls : 'row clearfix',
29086             cn : content
29087         };
29088         
29089         return cfg;
29090         
29091     },
29092     
29093     initEvents : function()
29094     {
29095         this.managerEl = this.el.select('.roo-document-manager', true).first();
29096         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29097         
29098         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29099         this.selectorEl.hide();
29100         
29101         if(this.multiple){
29102             this.selectorEl.attr('multiple', 'multiple');
29103         }
29104         
29105         this.selectorEl.on('change', this.onFileSelected, this);
29106         
29107         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29108         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29109         
29110         this.uploader.on('click', this.onUploaderClick, this);
29111         
29112         this.renderProgressDialog();
29113         
29114         var _this = this;
29115         
29116         window.addEventListener("resize", function() { _this.refresh(); } );
29117         
29118         this.fireEvent('initial', this);
29119     },
29120     
29121     renderProgressDialog : function()
29122     {
29123         var _this = this;
29124         
29125         this.progressDialog = new Roo.bootstrap.Modal({
29126             cls : 'roo-document-manager-progress-dialog',
29127             allow_close : false,
29128             title : '',
29129             buttons : [
29130                 {
29131                     name  :'cancel',
29132                     weight : 'danger',
29133                     html : 'Cancel'
29134                 }
29135             ], 
29136             listeners : { 
29137                 btnclick : function() {
29138                     _this.uploadCancel();
29139                     this.hide();
29140                 }
29141             }
29142         });
29143          
29144         this.progressDialog.render(Roo.get(document.body));
29145          
29146         this.progress = new Roo.bootstrap.Progress({
29147             cls : 'roo-document-manager-progress',
29148             active : true,
29149             striped : true
29150         });
29151         
29152         this.progress.render(this.progressDialog.getChildContainer());
29153         
29154         this.progressBar = new Roo.bootstrap.ProgressBar({
29155             cls : 'roo-document-manager-progress-bar',
29156             aria_valuenow : 0,
29157             aria_valuemin : 0,
29158             aria_valuemax : 12,
29159             panel : 'success'
29160         });
29161         
29162         this.progressBar.render(this.progress.getChildContainer());
29163     },
29164     
29165     onUploaderClick : function(e)
29166     {
29167         e.preventDefault();
29168      
29169         if(this.fireEvent('beforeselectfile', this) != false){
29170             this.selectorEl.dom.click();
29171         }
29172         
29173     },
29174     
29175     onFileSelected : function(e)
29176     {
29177         e.preventDefault();
29178         
29179         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29180             return;
29181         }
29182         
29183         Roo.each(this.selectorEl.dom.files, function(file){
29184             if(this.fireEvent('inspect', this, file) != false){
29185                 this.files.push(file);
29186             }
29187         }, this);
29188         
29189         this.queue();
29190         
29191     },
29192     
29193     queue : function()
29194     {
29195         this.selectorEl.dom.value = '';
29196         
29197         if(!this.files || !this.files.length){
29198             return;
29199         }
29200         
29201         if(this.boxes > 0 && this.files.length > this.boxes){
29202             this.files = this.files.slice(0, this.boxes);
29203         }
29204         
29205         this.uploader.show();
29206         
29207         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29208             this.uploader.hide();
29209         }
29210         
29211         var _this = this;
29212         
29213         var files = [];
29214         
29215         var docs = [];
29216         
29217         Roo.each(this.files, function(file){
29218             
29219             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29220                 var f = this.renderPreview(file);
29221                 files.push(f);
29222                 return;
29223             }
29224             
29225             if(file.type.indexOf('image') != -1){
29226                 this.delegates.push(
29227                     (function(){
29228                         _this.process(file);
29229                     }).createDelegate(this)
29230                 );
29231         
29232                 return;
29233             }
29234             
29235             docs.push(
29236                 (function(){
29237                     _this.process(file);
29238                 }).createDelegate(this)
29239             );
29240             
29241         }, this);
29242         
29243         this.files = files;
29244         
29245         this.delegates = this.delegates.concat(docs);
29246         
29247         if(!this.delegates.length){
29248             this.refresh();
29249             return;
29250         }
29251         
29252         this.progressBar.aria_valuemax = this.delegates.length;
29253         
29254         this.arrange();
29255         
29256         return;
29257     },
29258     
29259     arrange : function()
29260     {
29261         if(!this.delegates.length){
29262             this.progressDialog.hide();
29263             this.refresh();
29264             return;
29265         }
29266         
29267         var delegate = this.delegates.shift();
29268         
29269         this.progressDialog.show();
29270         
29271         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29272         
29273         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29274         
29275         delegate();
29276     },
29277     
29278     refresh : function()
29279     {
29280         this.uploader.show();
29281         
29282         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29283             this.uploader.hide();
29284         }
29285         
29286         Roo.isTouch ? this.closable(false) : this.closable(true);
29287         
29288         this.fireEvent('refresh', this);
29289     },
29290     
29291     onRemove : function(e, el, o)
29292     {
29293         e.preventDefault();
29294         
29295         this.fireEvent('remove', this, o);
29296         
29297     },
29298     
29299     remove : function(o)
29300     {
29301         var files = [];
29302         
29303         Roo.each(this.files, function(file){
29304             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29305                 files.push(file);
29306                 return;
29307             }
29308
29309             o.target.remove();
29310
29311         }, this);
29312         
29313         this.files = files;
29314         
29315         this.refresh();
29316     },
29317     
29318     clear : function()
29319     {
29320         Roo.each(this.files, function(file){
29321             if(!file.target){
29322                 return;
29323             }
29324             
29325             file.target.remove();
29326
29327         }, this);
29328         
29329         this.files = [];
29330         
29331         this.refresh();
29332     },
29333     
29334     onClick : function(e, el, o)
29335     {
29336         e.preventDefault();
29337         
29338         this.fireEvent('click', this, o);
29339         
29340     },
29341     
29342     closable : function(closable)
29343     {
29344         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29345             
29346             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29347             
29348             if(closable){
29349                 el.show();
29350                 return;
29351             }
29352             
29353             el.hide();
29354             
29355         }, this);
29356     },
29357     
29358     xhrOnLoad : function(xhr)
29359     {
29360         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29361             el.remove();
29362         }, this);
29363         
29364         if (xhr.readyState !== 4) {
29365             this.arrange();
29366             this.fireEvent('exception', this, xhr);
29367             return;
29368         }
29369
29370         var response = Roo.decode(xhr.responseText);
29371         
29372         if(!response.success){
29373             this.arrange();
29374             this.fireEvent('exception', this, xhr);
29375             return;
29376         }
29377         
29378         var file = this.renderPreview(response.data);
29379         
29380         this.files.push(file);
29381         
29382         this.arrange();
29383         
29384         this.fireEvent('afterupload', this, xhr);
29385         
29386     },
29387     
29388     xhrOnError : function(xhr)
29389     {
29390         Roo.log('xhr on error');
29391         
29392         var response = Roo.decode(xhr.responseText);
29393           
29394         Roo.log(response);
29395         
29396         this.arrange();
29397     },
29398     
29399     process : function(file)
29400     {
29401         if(this.fireEvent('process', this, file) !== false){
29402             if(this.editable && file.type.indexOf('image') != -1){
29403                 this.fireEvent('edit', this, file);
29404                 return;
29405             }
29406
29407             this.uploadStart(file, false);
29408
29409             return;
29410         }
29411         
29412     },
29413     
29414     uploadStart : function(file, crop)
29415     {
29416         this.xhr = new XMLHttpRequest();
29417         
29418         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29419             this.arrange();
29420             return;
29421         }
29422         
29423         file.xhr = this.xhr;
29424             
29425         this.managerEl.createChild({
29426             tag : 'div',
29427             cls : 'roo-document-manager-loading',
29428             cn : [
29429                 {
29430                     tag : 'div',
29431                     tooltip : file.name,
29432                     cls : 'roo-document-manager-thumb',
29433                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29434                 }
29435             ]
29436
29437         });
29438
29439         this.xhr.open(this.method, this.url, true);
29440         
29441         var headers = {
29442             "Accept": "application/json",
29443             "Cache-Control": "no-cache",
29444             "X-Requested-With": "XMLHttpRequest"
29445         };
29446         
29447         for (var headerName in headers) {
29448             var headerValue = headers[headerName];
29449             if (headerValue) {
29450                 this.xhr.setRequestHeader(headerName, headerValue);
29451             }
29452         }
29453         
29454         var _this = this;
29455         
29456         this.xhr.onload = function()
29457         {
29458             _this.xhrOnLoad(_this.xhr);
29459         }
29460         
29461         this.xhr.onerror = function()
29462         {
29463             _this.xhrOnError(_this.xhr);
29464         }
29465         
29466         var formData = new FormData();
29467
29468         formData.append('returnHTML', 'NO');
29469         
29470         if(crop){
29471             formData.append('crop', crop);
29472         }
29473         
29474         formData.append(this.paramName, file, file.name);
29475         
29476         var options = {
29477             file : file, 
29478             manually : false
29479         };
29480         
29481         if(this.fireEvent('prepare', this, formData, options) != false){
29482             
29483             if(options.manually){
29484                 return;
29485             }
29486             
29487             this.xhr.send(formData);
29488             return;
29489         };
29490         
29491         this.uploadCancel();
29492     },
29493     
29494     uploadCancel : function()
29495     {
29496         if (this.xhr) {
29497             this.xhr.abort();
29498         }
29499         
29500         this.delegates = [];
29501         
29502         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29503             el.remove();
29504         }, this);
29505         
29506         this.arrange();
29507     },
29508     
29509     renderPreview : function(file)
29510     {
29511         if(typeof(file.target) != 'undefined' && file.target){
29512             return file;
29513         }
29514         
29515         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29516         
29517         var previewEl = this.managerEl.createChild({
29518             tag : 'div',
29519             cls : 'roo-document-manager-preview',
29520             cn : [
29521                 {
29522                     tag : 'div',
29523                     tooltip : file[this.toolTipName],
29524                     cls : 'roo-document-manager-thumb',
29525                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29526                 },
29527                 {
29528                     tag : 'button',
29529                     cls : 'close',
29530                     html : '<i class="fa fa-times-circle"></i>'
29531                 }
29532             ]
29533         });
29534
29535         var close = previewEl.select('button.close', true).first();
29536
29537         close.on('click', this.onRemove, this, file);
29538
29539         file.target = previewEl;
29540
29541         var image = previewEl.select('img', true).first();
29542         
29543         var _this = this;
29544         
29545         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29546         
29547         image.on('click', this.onClick, this, file);
29548         
29549         this.fireEvent('previewrendered', this, file);
29550         
29551         return file;
29552         
29553     },
29554     
29555     onPreviewLoad : function(file, image)
29556     {
29557         if(typeof(file.target) == 'undefined' || !file.target){
29558             return;
29559         }
29560         
29561         var width = image.dom.naturalWidth || image.dom.width;
29562         var height = image.dom.naturalHeight || image.dom.height;
29563         
29564         if(!this.previewResize) {
29565             return;
29566         }
29567         
29568         if(width > height){
29569             file.target.addClass('wide');
29570             return;
29571         }
29572         
29573         file.target.addClass('tall');
29574         return;
29575         
29576     },
29577     
29578     uploadFromSource : function(file, crop)
29579     {
29580         this.xhr = new XMLHttpRequest();
29581         
29582         this.managerEl.createChild({
29583             tag : 'div',
29584             cls : 'roo-document-manager-loading',
29585             cn : [
29586                 {
29587                     tag : 'div',
29588                     tooltip : file.name,
29589                     cls : 'roo-document-manager-thumb',
29590                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29591                 }
29592             ]
29593
29594         });
29595
29596         this.xhr.open(this.method, this.url, true);
29597         
29598         var headers = {
29599             "Accept": "application/json",
29600             "Cache-Control": "no-cache",
29601             "X-Requested-With": "XMLHttpRequest"
29602         };
29603         
29604         for (var headerName in headers) {
29605             var headerValue = headers[headerName];
29606             if (headerValue) {
29607                 this.xhr.setRequestHeader(headerName, headerValue);
29608             }
29609         }
29610         
29611         var _this = this;
29612         
29613         this.xhr.onload = function()
29614         {
29615             _this.xhrOnLoad(_this.xhr);
29616         }
29617         
29618         this.xhr.onerror = function()
29619         {
29620             _this.xhrOnError(_this.xhr);
29621         }
29622         
29623         var formData = new FormData();
29624
29625         formData.append('returnHTML', 'NO');
29626         
29627         formData.append('crop', crop);
29628         
29629         if(typeof(file.filename) != 'undefined'){
29630             formData.append('filename', file.filename);
29631         }
29632         
29633         if(typeof(file.mimetype) != 'undefined'){
29634             formData.append('mimetype', file.mimetype);
29635         }
29636         
29637         Roo.log(formData);
29638         
29639         if(this.fireEvent('prepare', this, formData) != false){
29640             this.xhr.send(formData);
29641         };
29642     }
29643 });
29644
29645 /*
29646 * Licence: LGPL
29647 */
29648
29649 /**
29650  * @class Roo.bootstrap.DocumentViewer
29651  * @extends Roo.bootstrap.Component
29652  * Bootstrap DocumentViewer class
29653  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29654  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29655  * 
29656  * @constructor
29657  * Create a new DocumentViewer
29658  * @param {Object} config The config object
29659  */
29660
29661 Roo.bootstrap.DocumentViewer = function(config){
29662     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29663     
29664     this.addEvents({
29665         /**
29666          * @event initial
29667          * Fire after initEvent
29668          * @param {Roo.bootstrap.DocumentViewer} this
29669          */
29670         "initial" : true,
29671         /**
29672          * @event click
29673          * Fire after click
29674          * @param {Roo.bootstrap.DocumentViewer} this
29675          */
29676         "click" : true,
29677         /**
29678          * @event download
29679          * Fire after download button
29680          * @param {Roo.bootstrap.DocumentViewer} this
29681          */
29682         "download" : true,
29683         /**
29684          * @event trash
29685          * Fire after trash button
29686          * @param {Roo.bootstrap.DocumentViewer} this
29687          */
29688         "trash" : true
29689         
29690     });
29691 };
29692
29693 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29694     
29695     showDownload : true,
29696     
29697     showTrash : true,
29698     
29699     getAutoCreate : function()
29700     {
29701         var cfg = {
29702             tag : 'div',
29703             cls : 'roo-document-viewer',
29704             cn : [
29705                 {
29706                     tag : 'div',
29707                     cls : 'roo-document-viewer-body',
29708                     cn : [
29709                         {
29710                             tag : 'div',
29711                             cls : 'roo-document-viewer-thumb',
29712                             cn : [
29713                                 {
29714                                     tag : 'img',
29715                                     cls : 'roo-document-viewer-image'
29716                                 }
29717                             ]
29718                         }
29719                     ]
29720                 },
29721                 {
29722                     tag : 'div',
29723                     cls : 'roo-document-viewer-footer',
29724                     cn : {
29725                         tag : 'div',
29726                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29727                         cn : [
29728                             {
29729                                 tag : 'div',
29730                                 cls : 'btn-group roo-document-viewer-download',
29731                                 cn : [
29732                                     {
29733                                         tag : 'button',
29734                                         cls : 'btn btn-default',
29735                                         html : '<i class="fa fa-download"></i>'
29736                                     }
29737                                 ]
29738                             },
29739                             {
29740                                 tag : 'div',
29741                                 cls : 'btn-group roo-document-viewer-trash',
29742                                 cn : [
29743                                     {
29744                                         tag : 'button',
29745                                         cls : 'btn btn-default',
29746                                         html : '<i class="fa fa-trash"></i>'
29747                                     }
29748                                 ]
29749                             }
29750                         ]
29751                     }
29752                 }
29753             ]
29754         };
29755         
29756         return cfg;
29757     },
29758     
29759     initEvents : function()
29760     {
29761         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29762         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29763         
29764         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29765         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29766         
29767         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29768         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29769         
29770         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29771         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29772         
29773         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29774         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29775         
29776         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29777         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29778         
29779         this.bodyEl.on('click', this.onClick, this);
29780         this.downloadBtn.on('click', this.onDownload, this);
29781         this.trashBtn.on('click', this.onTrash, this);
29782         
29783         this.downloadBtn.hide();
29784         this.trashBtn.hide();
29785         
29786         if(this.showDownload){
29787             this.downloadBtn.show();
29788         }
29789         
29790         if(this.showTrash){
29791             this.trashBtn.show();
29792         }
29793         
29794         if(!this.showDownload && !this.showTrash) {
29795             this.footerEl.hide();
29796         }
29797         
29798     },
29799     
29800     initial : function()
29801     {
29802         this.fireEvent('initial', this);
29803         
29804     },
29805     
29806     onClick : function(e)
29807     {
29808         e.preventDefault();
29809         
29810         this.fireEvent('click', this);
29811     },
29812     
29813     onDownload : function(e)
29814     {
29815         e.preventDefault();
29816         
29817         this.fireEvent('download', this);
29818     },
29819     
29820     onTrash : function(e)
29821     {
29822         e.preventDefault();
29823         
29824         this.fireEvent('trash', this);
29825     }
29826     
29827 });
29828 /*
29829  * - LGPL
29830  *
29831  * nav progress bar
29832  * 
29833  */
29834
29835 /**
29836  * @class Roo.bootstrap.NavProgressBar
29837  * @extends Roo.bootstrap.Component
29838  * Bootstrap NavProgressBar class
29839  * 
29840  * @constructor
29841  * Create a new nav progress bar
29842  * @param {Object} config The config object
29843  */
29844
29845 Roo.bootstrap.NavProgressBar = function(config){
29846     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29847
29848     this.bullets = this.bullets || [];
29849    
29850 //    Roo.bootstrap.NavProgressBar.register(this);
29851      this.addEvents({
29852         /**
29853              * @event changed
29854              * Fires when the active item changes
29855              * @param {Roo.bootstrap.NavProgressBar} this
29856              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29857              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29858          */
29859         'changed': true
29860      });
29861     
29862 };
29863
29864 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29865     
29866     bullets : [],
29867     barItems : [],
29868     
29869     getAutoCreate : function()
29870     {
29871         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29872         
29873         cfg = {
29874             tag : 'div',
29875             cls : 'roo-navigation-bar-group',
29876             cn : [
29877                 {
29878                     tag : 'div',
29879                     cls : 'roo-navigation-top-bar'
29880                 },
29881                 {
29882                     tag : 'div',
29883                     cls : 'roo-navigation-bullets-bar',
29884                     cn : [
29885                         {
29886                             tag : 'ul',
29887                             cls : 'roo-navigation-bar'
29888                         }
29889                     ]
29890                 },
29891                 
29892                 {
29893                     tag : 'div',
29894                     cls : 'roo-navigation-bottom-bar'
29895                 }
29896             ]
29897             
29898         };
29899         
29900         return cfg;
29901         
29902     },
29903     
29904     initEvents: function() 
29905     {
29906         
29907     },
29908     
29909     onRender : function(ct, position) 
29910     {
29911         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29912         
29913         if(this.bullets.length){
29914             Roo.each(this.bullets, function(b){
29915                this.addItem(b);
29916             }, this);
29917         }
29918         
29919         this.format();
29920         
29921     },
29922     
29923     addItem : function(cfg)
29924     {
29925         var item = new Roo.bootstrap.NavProgressItem(cfg);
29926         
29927         item.parentId = this.id;
29928         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29929         
29930         if(cfg.html){
29931             var top = new Roo.bootstrap.Element({
29932                 tag : 'div',
29933                 cls : 'roo-navigation-bar-text'
29934             });
29935             
29936             var bottom = new Roo.bootstrap.Element({
29937                 tag : 'div',
29938                 cls : 'roo-navigation-bar-text'
29939             });
29940             
29941             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29942             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29943             
29944             var topText = new Roo.bootstrap.Element({
29945                 tag : 'span',
29946                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29947             });
29948             
29949             var bottomText = new Roo.bootstrap.Element({
29950                 tag : 'span',
29951                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29952             });
29953             
29954             topText.onRender(top.el, null);
29955             bottomText.onRender(bottom.el, null);
29956             
29957             item.topEl = top;
29958             item.bottomEl = bottom;
29959         }
29960         
29961         this.barItems.push(item);
29962         
29963         return item;
29964     },
29965     
29966     getActive : function()
29967     {
29968         var active = false;
29969         
29970         Roo.each(this.barItems, function(v){
29971             
29972             if (!v.isActive()) {
29973                 return;
29974             }
29975             
29976             active = v;
29977             return false;
29978             
29979         });
29980         
29981         return active;
29982     },
29983     
29984     setActiveItem : function(item)
29985     {
29986         var prev = false;
29987         
29988         Roo.each(this.barItems, function(v){
29989             if (v.rid == item.rid) {
29990                 return ;
29991             }
29992             
29993             if (v.isActive()) {
29994                 v.setActive(false);
29995                 prev = v;
29996             }
29997         });
29998
29999         item.setActive(true);
30000         
30001         this.fireEvent('changed', this, item, prev);
30002     },
30003     
30004     getBarItem: function(rid)
30005     {
30006         var ret = false;
30007         
30008         Roo.each(this.barItems, function(e) {
30009             if (e.rid != rid) {
30010                 return;
30011             }
30012             
30013             ret =  e;
30014             return false;
30015         });
30016         
30017         return ret;
30018     },
30019     
30020     indexOfItem : function(item)
30021     {
30022         var index = false;
30023         
30024         Roo.each(this.barItems, function(v, i){
30025             
30026             if (v.rid != item.rid) {
30027                 return;
30028             }
30029             
30030             index = i;
30031             return false
30032         });
30033         
30034         return index;
30035     },
30036     
30037     setActiveNext : function()
30038     {
30039         var i = this.indexOfItem(this.getActive());
30040         
30041         if (i > this.barItems.length) {
30042             return;
30043         }
30044         
30045         this.setActiveItem(this.barItems[i+1]);
30046     },
30047     
30048     setActivePrev : function()
30049     {
30050         var i = this.indexOfItem(this.getActive());
30051         
30052         if (i  < 1) {
30053             return;
30054         }
30055         
30056         this.setActiveItem(this.barItems[i-1]);
30057     },
30058     
30059     format : function()
30060     {
30061         if(!this.barItems.length){
30062             return;
30063         }
30064      
30065         var width = 100 / this.barItems.length;
30066         
30067         Roo.each(this.barItems, function(i){
30068             i.el.setStyle('width', width + '%');
30069             i.topEl.el.setStyle('width', width + '%');
30070             i.bottomEl.el.setStyle('width', width + '%');
30071         }, this);
30072         
30073     }
30074     
30075 });
30076 /*
30077  * - LGPL
30078  *
30079  * Nav Progress Item
30080  * 
30081  */
30082
30083 /**
30084  * @class Roo.bootstrap.NavProgressItem
30085  * @extends Roo.bootstrap.Component
30086  * Bootstrap NavProgressItem class
30087  * @cfg {String} rid the reference id
30088  * @cfg {Boolean} active (true|false) Is item active default false
30089  * @cfg {Boolean} disabled (true|false) Is item active default false
30090  * @cfg {String} html
30091  * @cfg {String} position (top|bottom) text position default bottom
30092  * @cfg {String} icon show icon instead of number
30093  * 
30094  * @constructor
30095  * Create a new NavProgressItem
30096  * @param {Object} config The config object
30097  */
30098 Roo.bootstrap.NavProgressItem = function(config){
30099     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30100     this.addEvents({
30101         // raw events
30102         /**
30103          * @event click
30104          * The raw click event for the entire grid.
30105          * @param {Roo.bootstrap.NavProgressItem} this
30106          * @param {Roo.EventObject} e
30107          */
30108         "click" : true
30109     });
30110    
30111 };
30112
30113 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30114     
30115     rid : '',
30116     active : false,
30117     disabled : false,
30118     html : '',
30119     position : 'bottom',
30120     icon : false,
30121     
30122     getAutoCreate : function()
30123     {
30124         var iconCls = 'roo-navigation-bar-item-icon';
30125         
30126         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30127         
30128         var cfg = {
30129             tag: 'li',
30130             cls: 'roo-navigation-bar-item',
30131             cn : [
30132                 {
30133                     tag : 'i',
30134                     cls : iconCls
30135                 }
30136             ]
30137         };
30138         
30139         if(this.active){
30140             cfg.cls += ' active';
30141         }
30142         if(this.disabled){
30143             cfg.cls += ' disabled';
30144         }
30145         
30146         return cfg;
30147     },
30148     
30149     disable : function()
30150     {
30151         this.setDisabled(true);
30152     },
30153     
30154     enable : function()
30155     {
30156         this.setDisabled(false);
30157     },
30158     
30159     initEvents: function() 
30160     {
30161         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30162         
30163         this.iconEl.on('click', this.onClick, this);
30164     },
30165     
30166     onClick : function(e)
30167     {
30168         e.preventDefault();
30169         
30170         if(this.disabled){
30171             return;
30172         }
30173         
30174         if(this.fireEvent('click', this, e) === false){
30175             return;
30176         };
30177         
30178         this.parent().setActiveItem(this);
30179     },
30180     
30181     isActive: function () 
30182     {
30183         return this.active;
30184     },
30185     
30186     setActive : function(state)
30187     {
30188         if(this.active == state){
30189             return;
30190         }
30191         
30192         this.active = state;
30193         
30194         if (state) {
30195             this.el.addClass('active');
30196             return;
30197         }
30198         
30199         this.el.removeClass('active');
30200         
30201         return;
30202     },
30203     
30204     setDisabled : function(state)
30205     {
30206         if(this.disabled == state){
30207             return;
30208         }
30209         
30210         this.disabled = state;
30211         
30212         if (state) {
30213             this.el.addClass('disabled');
30214             return;
30215         }
30216         
30217         this.el.removeClass('disabled');
30218     },
30219     
30220     tooltipEl : function()
30221     {
30222         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30223     }
30224 });
30225  
30226
30227  /*
30228  * - LGPL
30229  *
30230  * FieldLabel
30231  * 
30232  */
30233
30234 /**
30235  * @class Roo.bootstrap.FieldLabel
30236  * @extends Roo.bootstrap.Component
30237  * Bootstrap FieldLabel class
30238  * @cfg {String} html contents of the element
30239  * @cfg {String} tag tag of the element default label
30240  * @cfg {String} cls class of the element
30241  * @cfg {String} target label target 
30242  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30243  * @cfg {String} invalidClass default "text-warning"
30244  * @cfg {String} validClass default "text-success"
30245  * @cfg {String} iconTooltip default "This field is required"
30246  * @cfg {String} indicatorpos (left|right) default left
30247  * 
30248  * @constructor
30249  * Create a new FieldLabel
30250  * @param {Object} config The config object
30251  */
30252
30253 Roo.bootstrap.FieldLabel = function(config){
30254     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30255     
30256     this.addEvents({
30257             /**
30258              * @event invalid
30259              * Fires after the field has been marked as invalid.
30260              * @param {Roo.form.FieldLabel} this
30261              * @param {String} msg The validation message
30262              */
30263             invalid : true,
30264             /**
30265              * @event valid
30266              * Fires after the field has been validated with no errors.
30267              * @param {Roo.form.FieldLabel} this
30268              */
30269             valid : true
30270         });
30271 };
30272
30273 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30274     
30275     tag: 'label',
30276     cls: '',
30277     html: '',
30278     target: '',
30279     allowBlank : true,
30280     invalidClass : 'has-warning',
30281     validClass : 'has-success',
30282     iconTooltip : 'This field is required',
30283     indicatorpos : 'left',
30284     
30285     getAutoCreate : function(){
30286         
30287         var cls = "";
30288         if (!this.allowBlank) {
30289             cls  = "visible";
30290         }
30291         
30292         var cfg = {
30293             tag : this.tag,
30294             cls : 'roo-bootstrap-field-label ' + this.cls,
30295             for : this.target,
30296             cn : [
30297                 {
30298                     tag : 'i',
30299                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30300                     tooltip : this.iconTooltip
30301                 },
30302                 {
30303                     tag : 'span',
30304                     html : this.html
30305                 }
30306             ] 
30307         };
30308         
30309         if(this.indicatorpos == 'right'){
30310             var cfg = {
30311                 tag : this.tag,
30312                 cls : 'roo-bootstrap-field-label ' + this.cls,
30313                 for : this.target,
30314                 cn : [
30315                     {
30316                         tag : 'span',
30317                         html : this.html
30318                     },
30319                     {
30320                         tag : 'i',
30321                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30322                         tooltip : this.iconTooltip
30323                     }
30324                 ] 
30325             };
30326         }
30327         
30328         return cfg;
30329     },
30330     
30331     initEvents: function() 
30332     {
30333         Roo.bootstrap.Element.superclass.initEvents.call(this);
30334         
30335         this.indicator = this.indicatorEl();
30336         
30337         if(this.indicator){
30338             this.indicator.removeClass('visible');
30339             this.indicator.addClass('invisible');
30340         }
30341         
30342         Roo.bootstrap.FieldLabel.register(this);
30343     },
30344     
30345     indicatorEl : function()
30346     {
30347         var indicator = this.el.select('i.roo-required-indicator',true).first();
30348         
30349         if(!indicator){
30350             return false;
30351         }
30352         
30353         return indicator;
30354         
30355     },
30356     
30357     /**
30358      * Mark this field as valid
30359      */
30360     markValid : function()
30361     {
30362         if(this.indicator){
30363             this.indicator.removeClass('visible');
30364             this.indicator.addClass('invisible');
30365         }
30366         
30367         this.el.removeClass(this.invalidClass);
30368         
30369         this.el.addClass(this.validClass);
30370         
30371         this.fireEvent('valid', this);
30372     },
30373     
30374     /**
30375      * Mark this field as invalid
30376      * @param {String} msg The validation message
30377      */
30378     markInvalid : function(msg)
30379     {
30380         if(this.indicator){
30381             this.indicator.removeClass('invisible');
30382             this.indicator.addClass('visible');
30383         }
30384         
30385         this.el.removeClass(this.validClass);
30386         
30387         this.el.addClass(this.invalidClass);
30388         
30389         this.fireEvent('invalid', this, msg);
30390     }
30391     
30392    
30393 });
30394
30395 Roo.apply(Roo.bootstrap.FieldLabel, {
30396     
30397     groups: {},
30398     
30399      /**
30400     * register a FieldLabel Group
30401     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30402     */
30403     register : function(label)
30404     {
30405         if(this.groups.hasOwnProperty(label.target)){
30406             return;
30407         }
30408      
30409         this.groups[label.target] = label;
30410         
30411     },
30412     /**
30413     * fetch a FieldLabel Group based on the target
30414     * @param {string} target
30415     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30416     */
30417     get: function(target) {
30418         if (typeof(this.groups[target]) == 'undefined') {
30419             return false;
30420         }
30421         
30422         return this.groups[target] ;
30423     }
30424 });
30425
30426  
30427
30428  /*
30429  * - LGPL
30430  *
30431  * page DateSplitField.
30432  * 
30433  */
30434
30435
30436 /**
30437  * @class Roo.bootstrap.DateSplitField
30438  * @extends Roo.bootstrap.Component
30439  * Bootstrap DateSplitField class
30440  * @cfg {string} fieldLabel - the label associated
30441  * @cfg {Number} labelWidth set the width of label (0-12)
30442  * @cfg {String} labelAlign (top|left)
30443  * @cfg {Boolean} dayAllowBlank (true|false) default false
30444  * @cfg {Boolean} monthAllowBlank (true|false) default false
30445  * @cfg {Boolean} yearAllowBlank (true|false) default false
30446  * @cfg {string} dayPlaceholder 
30447  * @cfg {string} monthPlaceholder
30448  * @cfg {string} yearPlaceholder
30449  * @cfg {string} dayFormat default 'd'
30450  * @cfg {string} monthFormat default 'm'
30451  * @cfg {string} yearFormat default 'Y'
30452  * @cfg {Number} labellg set the width of label (1-12)
30453  * @cfg {Number} labelmd set the width of label (1-12)
30454  * @cfg {Number} labelsm set the width of label (1-12)
30455  * @cfg {Number} labelxs set the width of label (1-12)
30456
30457  *     
30458  * @constructor
30459  * Create a new DateSplitField
30460  * @param {Object} config The config object
30461  */
30462
30463 Roo.bootstrap.DateSplitField = function(config){
30464     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30465     
30466     this.addEvents({
30467         // raw events
30468          /**
30469          * @event years
30470          * getting the data of years
30471          * @param {Roo.bootstrap.DateSplitField} this
30472          * @param {Object} years
30473          */
30474         "years" : true,
30475         /**
30476          * @event days
30477          * getting the data of days
30478          * @param {Roo.bootstrap.DateSplitField} this
30479          * @param {Object} days
30480          */
30481         "days" : true,
30482         /**
30483          * @event invalid
30484          * Fires after the field has been marked as invalid.
30485          * @param {Roo.form.Field} this
30486          * @param {String} msg The validation message
30487          */
30488         invalid : true,
30489        /**
30490          * @event valid
30491          * Fires after the field has been validated with no errors.
30492          * @param {Roo.form.Field} this
30493          */
30494         valid : true
30495     });
30496 };
30497
30498 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30499     
30500     fieldLabel : '',
30501     labelAlign : 'top',
30502     labelWidth : 3,
30503     dayAllowBlank : false,
30504     monthAllowBlank : false,
30505     yearAllowBlank : false,
30506     dayPlaceholder : '',
30507     monthPlaceholder : '',
30508     yearPlaceholder : '',
30509     dayFormat : 'd',
30510     monthFormat : 'm',
30511     yearFormat : 'Y',
30512     isFormField : true,
30513     labellg : 0,
30514     labelmd : 0,
30515     labelsm : 0,
30516     labelxs : 0,
30517     
30518     getAutoCreate : function()
30519     {
30520         var cfg = {
30521             tag : 'div',
30522             cls : 'row roo-date-split-field-group',
30523             cn : [
30524                 {
30525                     tag : 'input',
30526                     type : 'hidden',
30527                     cls : 'form-hidden-field roo-date-split-field-group-value',
30528                     name : this.name
30529                 }
30530             ]
30531         };
30532         
30533         var labelCls = 'col-md-12';
30534         var contentCls = 'col-md-4';
30535         
30536         if(this.fieldLabel){
30537             
30538             var label = {
30539                 tag : 'div',
30540                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30541                 cn : [
30542                     {
30543                         tag : 'label',
30544                         html : this.fieldLabel
30545                     }
30546                 ]
30547             };
30548             
30549             if(this.labelAlign == 'left'){
30550             
30551                 if(this.labelWidth > 12){
30552                     label.style = "width: " + this.labelWidth + 'px';
30553                 }
30554
30555                 if(this.labelWidth < 13 && this.labelmd == 0){
30556                     this.labelmd = this.labelWidth;
30557                 }
30558
30559                 if(this.labellg > 0){
30560                     labelCls = ' col-lg-' + this.labellg;
30561                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30562                 }
30563
30564                 if(this.labelmd > 0){
30565                     labelCls = ' col-md-' + this.labelmd;
30566                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30567                 }
30568
30569                 if(this.labelsm > 0){
30570                     labelCls = ' col-sm-' + this.labelsm;
30571                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30572                 }
30573
30574                 if(this.labelxs > 0){
30575                     labelCls = ' col-xs-' + this.labelxs;
30576                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30577                 }
30578             }
30579             
30580             label.cls += ' ' + labelCls;
30581             
30582             cfg.cn.push(label);
30583         }
30584         
30585         Roo.each(['day', 'month', 'year'], function(t){
30586             cfg.cn.push({
30587                 tag : 'div',
30588                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30589             });
30590         }, this);
30591         
30592         return cfg;
30593     },
30594     
30595     inputEl: function ()
30596     {
30597         return this.el.select('.roo-date-split-field-group-value', true).first();
30598     },
30599     
30600     onRender : function(ct, position) 
30601     {
30602         var _this = this;
30603         
30604         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30605         
30606         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30607         
30608         this.dayField = new Roo.bootstrap.ComboBox({
30609             allowBlank : this.dayAllowBlank,
30610             alwaysQuery : true,
30611             displayField : 'value',
30612             editable : false,
30613             fieldLabel : '',
30614             forceSelection : true,
30615             mode : 'local',
30616             placeholder : this.dayPlaceholder,
30617             selectOnFocus : true,
30618             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30619             triggerAction : 'all',
30620             typeAhead : true,
30621             valueField : 'value',
30622             store : new Roo.data.SimpleStore({
30623                 data : (function() {    
30624                     var days = [];
30625                     _this.fireEvent('days', _this, days);
30626                     return days;
30627                 })(),
30628                 fields : [ 'value' ]
30629             }),
30630             listeners : {
30631                 select : function (_self, record, index)
30632                 {
30633                     _this.setValue(_this.getValue());
30634                 }
30635             }
30636         });
30637
30638         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30639         
30640         this.monthField = new Roo.bootstrap.MonthField({
30641             after : '<i class=\"fa fa-calendar\"></i>',
30642             allowBlank : this.monthAllowBlank,
30643             placeholder : this.monthPlaceholder,
30644             readOnly : true,
30645             listeners : {
30646                 render : function (_self)
30647                 {
30648                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30649                         e.preventDefault();
30650                         _self.focus();
30651                     });
30652                 },
30653                 select : function (_self, oldvalue, newvalue)
30654                 {
30655                     _this.setValue(_this.getValue());
30656                 }
30657             }
30658         });
30659         
30660         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30661         
30662         this.yearField = new Roo.bootstrap.ComboBox({
30663             allowBlank : this.yearAllowBlank,
30664             alwaysQuery : true,
30665             displayField : 'value',
30666             editable : false,
30667             fieldLabel : '',
30668             forceSelection : true,
30669             mode : 'local',
30670             placeholder : this.yearPlaceholder,
30671             selectOnFocus : true,
30672             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30673             triggerAction : 'all',
30674             typeAhead : true,
30675             valueField : 'value',
30676             store : new Roo.data.SimpleStore({
30677                 data : (function() {
30678                     var years = [];
30679                     _this.fireEvent('years', _this, years);
30680                     return years;
30681                 })(),
30682                 fields : [ 'value' ]
30683             }),
30684             listeners : {
30685                 select : function (_self, record, index)
30686                 {
30687                     _this.setValue(_this.getValue());
30688                 }
30689             }
30690         });
30691
30692         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30693     },
30694     
30695     setValue : function(v, format)
30696     {
30697         this.inputEl.dom.value = v;
30698         
30699         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30700         
30701         var d = Date.parseDate(v, f);
30702         
30703         if(!d){
30704             this.validate();
30705             return;
30706         }
30707         
30708         this.setDay(d.format(this.dayFormat));
30709         this.setMonth(d.format(this.monthFormat));
30710         this.setYear(d.format(this.yearFormat));
30711         
30712         this.validate();
30713         
30714         return;
30715     },
30716     
30717     setDay : function(v)
30718     {
30719         this.dayField.setValue(v);
30720         this.inputEl.dom.value = this.getValue();
30721         this.validate();
30722         return;
30723     },
30724     
30725     setMonth : function(v)
30726     {
30727         this.monthField.setValue(v, true);
30728         this.inputEl.dom.value = this.getValue();
30729         this.validate();
30730         return;
30731     },
30732     
30733     setYear : function(v)
30734     {
30735         this.yearField.setValue(v);
30736         this.inputEl.dom.value = this.getValue();
30737         this.validate();
30738         return;
30739     },
30740     
30741     getDay : function()
30742     {
30743         return this.dayField.getValue();
30744     },
30745     
30746     getMonth : function()
30747     {
30748         return this.monthField.getValue();
30749     },
30750     
30751     getYear : function()
30752     {
30753         return this.yearField.getValue();
30754     },
30755     
30756     getValue : function()
30757     {
30758         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30759         
30760         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30761         
30762         return date;
30763     },
30764     
30765     reset : function()
30766     {
30767         this.setDay('');
30768         this.setMonth('');
30769         this.setYear('');
30770         this.inputEl.dom.value = '';
30771         this.validate();
30772         return;
30773     },
30774     
30775     validate : function()
30776     {
30777         var d = this.dayField.validate();
30778         var m = this.monthField.validate();
30779         var y = this.yearField.validate();
30780         
30781         var valid = true;
30782         
30783         if(
30784                 (!this.dayAllowBlank && !d) ||
30785                 (!this.monthAllowBlank && !m) ||
30786                 (!this.yearAllowBlank && !y)
30787         ){
30788             valid = false;
30789         }
30790         
30791         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30792             return valid;
30793         }
30794         
30795         if(valid){
30796             this.markValid();
30797             return valid;
30798         }
30799         
30800         this.markInvalid();
30801         
30802         return valid;
30803     },
30804     
30805     markValid : function()
30806     {
30807         
30808         var label = this.el.select('label', true).first();
30809         var icon = this.el.select('i.fa-star', true).first();
30810
30811         if(label && icon){
30812             icon.remove();
30813         }
30814         
30815         this.fireEvent('valid', this);
30816     },
30817     
30818      /**
30819      * Mark this field as invalid
30820      * @param {String} msg The validation message
30821      */
30822     markInvalid : function(msg)
30823     {
30824         
30825         var label = this.el.select('label', true).first();
30826         var icon = this.el.select('i.fa-star', true).first();
30827
30828         if(label && !icon){
30829             this.el.select('.roo-date-split-field-label', true).createChild({
30830                 tag : 'i',
30831                 cls : 'text-danger fa fa-lg fa-star',
30832                 tooltip : 'This field is required',
30833                 style : 'margin-right:5px;'
30834             }, label, true);
30835         }
30836         
30837         this.fireEvent('invalid', this, msg);
30838     },
30839     
30840     clearInvalid : function()
30841     {
30842         var label = this.el.select('label', true).first();
30843         var icon = this.el.select('i.fa-star', true).first();
30844
30845         if(label && icon){
30846             icon.remove();
30847         }
30848         
30849         this.fireEvent('valid', this);
30850     },
30851     
30852     getName: function()
30853     {
30854         return this.name;
30855     }
30856     
30857 });
30858
30859  /**
30860  *
30861  * This is based on 
30862  * http://masonry.desandro.com
30863  *
30864  * The idea is to render all the bricks based on vertical width...
30865  *
30866  * The original code extends 'outlayer' - we might need to use that....
30867  * 
30868  */
30869
30870
30871 /**
30872  * @class Roo.bootstrap.LayoutMasonry
30873  * @extends Roo.bootstrap.Component
30874  * Bootstrap Layout Masonry class
30875  * 
30876  * @constructor
30877  * Create a new Element
30878  * @param {Object} config The config object
30879  */
30880
30881 Roo.bootstrap.LayoutMasonry = function(config){
30882     
30883     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30884     
30885     this.bricks = [];
30886     
30887     Roo.bootstrap.LayoutMasonry.register(this);
30888     
30889     this.addEvents({
30890         // raw events
30891         /**
30892          * @event layout
30893          * Fire after layout the items
30894          * @param {Roo.bootstrap.LayoutMasonry} this
30895          * @param {Roo.EventObject} e
30896          */
30897         "layout" : true
30898     });
30899     
30900 };
30901
30902 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30903     
30904     /**
30905      * @cfg {Boolean} isLayoutInstant = no animation?
30906      */   
30907     isLayoutInstant : false, // needed?
30908    
30909     /**
30910      * @cfg {Number} boxWidth  width of the columns
30911      */   
30912     boxWidth : 450,
30913     
30914       /**
30915      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30916      */   
30917     boxHeight : 0,
30918     
30919     /**
30920      * @cfg {Number} padWidth padding below box..
30921      */   
30922     padWidth : 10, 
30923     
30924     /**
30925      * @cfg {Number} gutter gutter width..
30926      */   
30927     gutter : 10,
30928     
30929      /**
30930      * @cfg {Number} maxCols maximum number of columns
30931      */   
30932     
30933     maxCols: 0,
30934     
30935     /**
30936      * @cfg {Boolean} isAutoInitial defalut true
30937      */   
30938     isAutoInitial : true, 
30939     
30940     containerWidth: 0,
30941     
30942     /**
30943      * @cfg {Boolean} isHorizontal defalut false
30944      */   
30945     isHorizontal : false, 
30946
30947     currentSize : null,
30948     
30949     tag: 'div',
30950     
30951     cls: '',
30952     
30953     bricks: null, //CompositeElement
30954     
30955     cols : 1,
30956     
30957     _isLayoutInited : false,
30958     
30959 //    isAlternative : false, // only use for vertical layout...
30960     
30961     /**
30962      * @cfg {Number} alternativePadWidth padding below box..
30963      */   
30964     alternativePadWidth : 50,
30965     
30966     selectedBrick : [],
30967     
30968     getAutoCreate : function(){
30969         
30970         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30971         
30972         var cfg = {
30973             tag: this.tag,
30974             cls: 'blog-masonary-wrapper ' + this.cls,
30975             cn : {
30976                 cls : 'mas-boxes masonary'
30977             }
30978         };
30979         
30980         return cfg;
30981     },
30982     
30983     getChildContainer: function( )
30984     {
30985         if (this.boxesEl) {
30986             return this.boxesEl;
30987         }
30988         
30989         this.boxesEl = this.el.select('.mas-boxes').first();
30990         
30991         return this.boxesEl;
30992     },
30993     
30994     
30995     initEvents : function()
30996     {
30997         var _this = this;
30998         
30999         if(this.isAutoInitial){
31000             Roo.log('hook children rendered');
31001             this.on('childrenrendered', function() {
31002                 Roo.log('children rendered');
31003                 _this.initial();
31004             } ,this);
31005         }
31006     },
31007     
31008     initial : function()
31009     {
31010         this.selectedBrick = [];
31011         
31012         this.currentSize = this.el.getBox(true);
31013         
31014         Roo.EventManager.onWindowResize(this.resize, this); 
31015
31016         if(!this.isAutoInitial){
31017             this.layout();
31018             return;
31019         }
31020         
31021         this.layout();
31022         
31023         return;
31024         //this.layout.defer(500,this);
31025         
31026     },
31027     
31028     resize : function()
31029     {
31030         var cs = this.el.getBox(true);
31031         
31032         if (
31033                 this.currentSize.width == cs.width && 
31034                 this.currentSize.x == cs.x && 
31035                 this.currentSize.height == cs.height && 
31036                 this.currentSize.y == cs.y 
31037         ) {
31038             Roo.log("no change in with or X or Y");
31039             return;
31040         }
31041         
31042         this.currentSize = cs;
31043         
31044         this.layout();
31045         
31046     },
31047     
31048     layout : function()
31049     {   
31050         this._resetLayout();
31051         
31052         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31053         
31054         this.layoutItems( isInstant );
31055       
31056         this._isLayoutInited = true;
31057         
31058         this.fireEvent('layout', this);
31059         
31060     },
31061     
31062     _resetLayout : function()
31063     {
31064         if(this.isHorizontal){
31065             this.horizontalMeasureColumns();
31066             return;
31067         }
31068         
31069         this.verticalMeasureColumns();
31070         
31071     },
31072     
31073     verticalMeasureColumns : function()
31074     {
31075         this.getContainerWidth();
31076         
31077 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31078 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31079 //            return;
31080 //        }
31081         
31082         var boxWidth = this.boxWidth + this.padWidth;
31083         
31084         if(this.containerWidth < this.boxWidth){
31085             boxWidth = this.containerWidth
31086         }
31087         
31088         var containerWidth = this.containerWidth;
31089         
31090         var cols = Math.floor(containerWidth / boxWidth);
31091         
31092         this.cols = Math.max( cols, 1 );
31093         
31094         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31095         
31096         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31097         
31098         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31099         
31100         this.colWidth = boxWidth + avail - this.padWidth;
31101         
31102         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31103         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31104     },
31105     
31106     horizontalMeasureColumns : function()
31107     {
31108         this.getContainerWidth();
31109         
31110         var boxWidth = this.boxWidth;
31111         
31112         if(this.containerWidth < boxWidth){
31113             boxWidth = this.containerWidth;
31114         }
31115         
31116         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31117         
31118         this.el.setHeight(boxWidth);
31119         
31120     },
31121     
31122     getContainerWidth : function()
31123     {
31124         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31125     },
31126     
31127     layoutItems : function( isInstant )
31128     {
31129         Roo.log(this.bricks);
31130         
31131         var items = Roo.apply([], this.bricks);
31132         
31133         if(this.isHorizontal){
31134             this._horizontalLayoutItems( items , isInstant );
31135             return;
31136         }
31137         
31138 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31139 //            this._verticalAlternativeLayoutItems( items , isInstant );
31140 //            return;
31141 //        }
31142         
31143         this._verticalLayoutItems( items , isInstant );
31144         
31145     },
31146     
31147     _verticalLayoutItems : function ( items , isInstant)
31148     {
31149         if ( !items || !items.length ) {
31150             return;
31151         }
31152         
31153         var standard = [
31154             ['xs', 'xs', 'xs', 'tall'],
31155             ['xs', 'xs', 'tall'],
31156             ['xs', 'xs', 'sm'],
31157             ['xs', 'xs', 'xs'],
31158             ['xs', 'tall'],
31159             ['xs', 'sm'],
31160             ['xs', 'xs'],
31161             ['xs'],
31162             
31163             ['sm', 'xs', 'xs'],
31164             ['sm', 'xs'],
31165             ['sm'],
31166             
31167             ['tall', 'xs', 'xs', 'xs'],
31168             ['tall', 'xs', 'xs'],
31169             ['tall', 'xs'],
31170             ['tall']
31171             
31172         ];
31173         
31174         var queue = [];
31175         
31176         var boxes = [];
31177         
31178         var box = [];
31179         
31180         Roo.each(items, function(item, k){
31181             
31182             switch (item.size) {
31183                 // these layouts take up a full box,
31184                 case 'md' :
31185                 case 'md-left' :
31186                 case 'md-right' :
31187                 case 'wide' :
31188                     
31189                     if(box.length){
31190                         boxes.push(box);
31191                         box = [];
31192                     }
31193                     
31194                     boxes.push([item]);
31195                     
31196                     break;
31197                     
31198                 case 'xs' :
31199                 case 'sm' :
31200                 case 'tall' :
31201                     
31202                     box.push(item);
31203                     
31204                     break;
31205                 default :
31206                     break;
31207                     
31208             }
31209             
31210         }, this);
31211         
31212         if(box.length){
31213             boxes.push(box);
31214             box = [];
31215         }
31216         
31217         var filterPattern = function(box, length)
31218         {
31219             if(!box.length){
31220                 return;
31221             }
31222             
31223             var match = false;
31224             
31225             var pattern = box.slice(0, length);
31226             
31227             var format = [];
31228             
31229             Roo.each(pattern, function(i){
31230                 format.push(i.size);
31231             }, this);
31232             
31233             Roo.each(standard, function(s){
31234                 
31235                 if(String(s) != String(format)){
31236                     return;
31237                 }
31238                 
31239                 match = true;
31240                 return false;
31241                 
31242             }, this);
31243             
31244             if(!match && length == 1){
31245                 return;
31246             }
31247             
31248             if(!match){
31249                 filterPattern(box, length - 1);
31250                 return;
31251             }
31252                 
31253             queue.push(pattern);
31254
31255             box = box.slice(length, box.length);
31256
31257             filterPattern(box, 4);
31258
31259             return;
31260             
31261         }
31262         
31263         Roo.each(boxes, function(box, k){
31264             
31265             if(!box.length){
31266                 return;
31267             }
31268             
31269             if(box.length == 1){
31270                 queue.push(box);
31271                 return;
31272             }
31273             
31274             filterPattern(box, 4);
31275             
31276         }, this);
31277         
31278         this._processVerticalLayoutQueue( queue, isInstant );
31279         
31280     },
31281     
31282 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31283 //    {
31284 //        if ( !items || !items.length ) {
31285 //            return;
31286 //        }
31287 //
31288 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31289 //        
31290 //    },
31291     
31292     _horizontalLayoutItems : function ( items , isInstant)
31293     {
31294         if ( !items || !items.length || items.length < 3) {
31295             return;
31296         }
31297         
31298         items.reverse();
31299         
31300         var eItems = items.slice(0, 3);
31301         
31302         items = items.slice(3, items.length);
31303         
31304         var standard = [
31305             ['xs', 'xs', 'xs', 'wide'],
31306             ['xs', 'xs', 'wide'],
31307             ['xs', 'xs', 'sm'],
31308             ['xs', 'xs', 'xs'],
31309             ['xs', 'wide'],
31310             ['xs', 'sm'],
31311             ['xs', 'xs'],
31312             ['xs'],
31313             
31314             ['sm', 'xs', 'xs'],
31315             ['sm', 'xs'],
31316             ['sm'],
31317             
31318             ['wide', 'xs', 'xs', 'xs'],
31319             ['wide', 'xs', 'xs'],
31320             ['wide', 'xs'],
31321             ['wide'],
31322             
31323             ['wide-thin']
31324         ];
31325         
31326         var queue = [];
31327         
31328         var boxes = [];
31329         
31330         var box = [];
31331         
31332         Roo.each(items, function(item, k){
31333             
31334             switch (item.size) {
31335                 case 'md' :
31336                 case 'md-left' :
31337                 case 'md-right' :
31338                 case 'tall' :
31339                     
31340                     if(box.length){
31341                         boxes.push(box);
31342                         box = [];
31343                     }
31344                     
31345                     boxes.push([item]);
31346                     
31347                     break;
31348                     
31349                 case 'xs' :
31350                 case 'sm' :
31351                 case 'wide' :
31352                 case 'wide-thin' :
31353                     
31354                     box.push(item);
31355                     
31356                     break;
31357                 default :
31358                     break;
31359                     
31360             }
31361             
31362         }, this);
31363         
31364         if(box.length){
31365             boxes.push(box);
31366             box = [];
31367         }
31368         
31369         var filterPattern = function(box, length)
31370         {
31371             if(!box.length){
31372                 return;
31373             }
31374             
31375             var match = false;
31376             
31377             var pattern = box.slice(0, length);
31378             
31379             var format = [];
31380             
31381             Roo.each(pattern, function(i){
31382                 format.push(i.size);
31383             }, this);
31384             
31385             Roo.each(standard, function(s){
31386                 
31387                 if(String(s) != String(format)){
31388                     return;
31389                 }
31390                 
31391                 match = true;
31392                 return false;
31393                 
31394             }, this);
31395             
31396             if(!match && length == 1){
31397                 return;
31398             }
31399             
31400             if(!match){
31401                 filterPattern(box, length - 1);
31402                 return;
31403             }
31404                 
31405             queue.push(pattern);
31406
31407             box = box.slice(length, box.length);
31408
31409             filterPattern(box, 4);
31410
31411             return;
31412             
31413         }
31414         
31415         Roo.each(boxes, function(box, k){
31416             
31417             if(!box.length){
31418                 return;
31419             }
31420             
31421             if(box.length == 1){
31422                 queue.push(box);
31423                 return;
31424             }
31425             
31426             filterPattern(box, 4);
31427             
31428         }, this);
31429         
31430         
31431         var prune = [];
31432         
31433         var pos = this.el.getBox(true);
31434         
31435         var minX = pos.x;
31436         
31437         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31438         
31439         var hit_end = false;
31440         
31441         Roo.each(queue, function(box){
31442             
31443             if(hit_end){
31444                 
31445                 Roo.each(box, function(b){
31446                 
31447                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31448                     b.el.hide();
31449
31450                 }, this);
31451
31452                 return;
31453             }
31454             
31455             var mx = 0;
31456             
31457             Roo.each(box, function(b){
31458                 
31459                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31460                 b.el.show();
31461
31462                 mx = Math.max(mx, b.x);
31463                 
31464             }, this);
31465             
31466             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31467             
31468             if(maxX < minX){
31469                 
31470                 Roo.each(box, function(b){
31471                 
31472                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31473                     b.el.hide();
31474                     
31475                 }, this);
31476                 
31477                 hit_end = true;
31478                 
31479                 return;
31480             }
31481             
31482             prune.push(box);
31483             
31484         }, this);
31485         
31486         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31487     },
31488     
31489     /** Sets position of item in DOM
31490     * @param {Element} item
31491     * @param {Number} x - horizontal position
31492     * @param {Number} y - vertical position
31493     * @param {Boolean} isInstant - disables transitions
31494     */
31495     _processVerticalLayoutQueue : function( queue, isInstant )
31496     {
31497         var pos = this.el.getBox(true);
31498         var x = pos.x;
31499         var y = pos.y;
31500         var maxY = [];
31501         
31502         for (var i = 0; i < this.cols; i++){
31503             maxY[i] = pos.y;
31504         }
31505         
31506         Roo.each(queue, function(box, k){
31507             
31508             var col = k % this.cols;
31509             
31510             Roo.each(box, function(b,kk){
31511                 
31512                 b.el.position('absolute');
31513                 
31514                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31515                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31516                 
31517                 if(b.size == 'md-left' || b.size == 'md-right'){
31518                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31519                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31520                 }
31521                 
31522                 b.el.setWidth(width);
31523                 b.el.setHeight(height);
31524                 // iframe?
31525                 b.el.select('iframe',true).setSize(width,height);
31526                 
31527             }, this);
31528             
31529             for (var i = 0; i < this.cols; i++){
31530                 
31531                 if(maxY[i] < maxY[col]){
31532                     col = i;
31533                     continue;
31534                 }
31535                 
31536                 col = Math.min(col, i);
31537                 
31538             }
31539             
31540             x = pos.x + col * (this.colWidth + this.padWidth);
31541             
31542             y = maxY[col];
31543             
31544             var positions = [];
31545             
31546             switch (box.length){
31547                 case 1 :
31548                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31549                     break;
31550                 case 2 :
31551                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31552                     break;
31553                 case 3 :
31554                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31555                     break;
31556                 case 4 :
31557                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31558                     break;
31559                 default :
31560                     break;
31561             }
31562             
31563             Roo.each(box, function(b,kk){
31564                 
31565                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31566                 
31567                 var sz = b.el.getSize();
31568                 
31569                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31570                 
31571             }, this);
31572             
31573         }, this);
31574         
31575         var mY = 0;
31576         
31577         for (var i = 0; i < this.cols; i++){
31578             mY = Math.max(mY, maxY[i]);
31579         }
31580         
31581         this.el.setHeight(mY - pos.y);
31582         
31583     },
31584     
31585 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31586 //    {
31587 //        var pos = this.el.getBox(true);
31588 //        var x = pos.x;
31589 //        var y = pos.y;
31590 //        var maxX = pos.right;
31591 //        
31592 //        var maxHeight = 0;
31593 //        
31594 //        Roo.each(items, function(item, k){
31595 //            
31596 //            var c = k % 2;
31597 //            
31598 //            item.el.position('absolute');
31599 //                
31600 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31601 //
31602 //            item.el.setWidth(width);
31603 //
31604 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31605 //
31606 //            item.el.setHeight(height);
31607 //            
31608 //            if(c == 0){
31609 //                item.el.setXY([x, y], isInstant ? false : true);
31610 //            } else {
31611 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31612 //            }
31613 //            
31614 //            y = y + height + this.alternativePadWidth;
31615 //            
31616 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31617 //            
31618 //        }, this);
31619 //        
31620 //        this.el.setHeight(maxHeight);
31621 //        
31622 //    },
31623     
31624     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31625     {
31626         var pos = this.el.getBox(true);
31627         
31628         var minX = pos.x;
31629         var minY = pos.y;
31630         
31631         var maxX = pos.right;
31632         
31633         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31634         
31635         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31636         
31637         Roo.each(queue, function(box, k){
31638             
31639             Roo.each(box, function(b, kk){
31640                 
31641                 b.el.position('absolute');
31642                 
31643                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31644                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31645                 
31646                 if(b.size == 'md-left' || b.size == 'md-right'){
31647                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31648                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31649                 }
31650                 
31651                 b.el.setWidth(width);
31652                 b.el.setHeight(height);
31653                 
31654             }, this);
31655             
31656             if(!box.length){
31657                 return;
31658             }
31659             
31660             var positions = [];
31661             
31662             switch (box.length){
31663                 case 1 :
31664                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31665                     break;
31666                 case 2 :
31667                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31668                     break;
31669                 case 3 :
31670                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31671                     break;
31672                 case 4 :
31673                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31674                     break;
31675                 default :
31676                     break;
31677             }
31678             
31679             Roo.each(box, function(b,kk){
31680                 
31681                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31682                 
31683                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31684                 
31685             }, this);
31686             
31687         }, this);
31688         
31689     },
31690     
31691     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31692     {
31693         Roo.each(eItems, function(b,k){
31694             
31695             b.size = (k == 0) ? 'sm' : 'xs';
31696             b.x = (k == 0) ? 2 : 1;
31697             b.y = (k == 0) ? 2 : 1;
31698             
31699             b.el.position('absolute');
31700             
31701             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31702                 
31703             b.el.setWidth(width);
31704             
31705             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31706             
31707             b.el.setHeight(height);
31708             
31709         }, this);
31710
31711         var positions = [];
31712         
31713         positions.push({
31714             x : maxX - this.unitWidth * 2 - this.gutter,
31715             y : minY
31716         });
31717         
31718         positions.push({
31719             x : maxX - this.unitWidth,
31720             y : minY + (this.unitWidth + this.gutter) * 2
31721         });
31722         
31723         positions.push({
31724             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31725             y : minY
31726         });
31727         
31728         Roo.each(eItems, function(b,k){
31729             
31730             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31731
31732         }, this);
31733         
31734     },
31735     
31736     getVerticalOneBoxColPositions : function(x, y, box)
31737     {
31738         var pos = [];
31739         
31740         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31741         
31742         if(box[0].size == 'md-left'){
31743             rand = 0;
31744         }
31745         
31746         if(box[0].size == 'md-right'){
31747             rand = 1;
31748         }
31749         
31750         pos.push({
31751             x : x + (this.unitWidth + this.gutter) * rand,
31752             y : y
31753         });
31754         
31755         return pos;
31756     },
31757     
31758     getVerticalTwoBoxColPositions : function(x, y, box)
31759     {
31760         var pos = [];
31761         
31762         if(box[0].size == 'xs'){
31763             
31764             pos.push({
31765                 x : x,
31766                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31767             });
31768
31769             pos.push({
31770                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31771                 y : y
31772             });
31773             
31774             return pos;
31775             
31776         }
31777         
31778         pos.push({
31779             x : x,
31780             y : y
31781         });
31782
31783         pos.push({
31784             x : x + (this.unitWidth + this.gutter) * 2,
31785             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31786         });
31787         
31788         return pos;
31789         
31790     },
31791     
31792     getVerticalThreeBoxColPositions : function(x, y, box)
31793     {
31794         var pos = [];
31795         
31796         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31797             
31798             pos.push({
31799                 x : x,
31800                 y : y
31801             });
31802
31803             pos.push({
31804                 x : x + (this.unitWidth + this.gutter) * 1,
31805                 y : y
31806             });
31807             
31808             pos.push({
31809                 x : x + (this.unitWidth + this.gutter) * 2,
31810                 y : y
31811             });
31812             
31813             return pos;
31814             
31815         }
31816         
31817         if(box[0].size == 'xs' && box[1].size == 'xs'){
31818             
31819             pos.push({
31820                 x : x,
31821                 y : y
31822             });
31823
31824             pos.push({
31825                 x : x,
31826                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31827             });
31828             
31829             pos.push({
31830                 x : x + (this.unitWidth + this.gutter) * 1,
31831                 y : y
31832             });
31833             
31834             return pos;
31835             
31836         }
31837         
31838         pos.push({
31839             x : x,
31840             y : y
31841         });
31842
31843         pos.push({
31844             x : x + (this.unitWidth + this.gutter) * 2,
31845             y : y
31846         });
31847
31848         pos.push({
31849             x : x + (this.unitWidth + this.gutter) * 2,
31850             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31851         });
31852             
31853         return pos;
31854         
31855     },
31856     
31857     getVerticalFourBoxColPositions : function(x, y, box)
31858     {
31859         var pos = [];
31860         
31861         if(box[0].size == 'xs'){
31862             
31863             pos.push({
31864                 x : x,
31865                 y : y
31866             });
31867
31868             pos.push({
31869                 x : x,
31870                 y : y + (this.unitHeight + this.gutter) * 1
31871             });
31872             
31873             pos.push({
31874                 x : x,
31875                 y : y + (this.unitHeight + this.gutter) * 2
31876             });
31877             
31878             pos.push({
31879                 x : x + (this.unitWidth + this.gutter) * 1,
31880                 y : y
31881             });
31882             
31883             return pos;
31884             
31885         }
31886         
31887         pos.push({
31888             x : x,
31889             y : y
31890         });
31891
31892         pos.push({
31893             x : x + (this.unitWidth + this.gutter) * 2,
31894             y : y
31895         });
31896
31897         pos.push({
31898             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31899             y : y + (this.unitHeight + this.gutter) * 1
31900         });
31901
31902         pos.push({
31903             x : x + (this.unitWidth + this.gutter) * 2,
31904             y : y + (this.unitWidth + this.gutter) * 2
31905         });
31906
31907         return pos;
31908         
31909     },
31910     
31911     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31912     {
31913         var pos = [];
31914         
31915         if(box[0].size == 'md-left'){
31916             pos.push({
31917                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31918                 y : minY
31919             });
31920             
31921             return pos;
31922         }
31923         
31924         if(box[0].size == 'md-right'){
31925             pos.push({
31926                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31927                 y : minY + (this.unitWidth + this.gutter) * 1
31928             });
31929             
31930             return pos;
31931         }
31932         
31933         var rand = Math.floor(Math.random() * (4 - box[0].y));
31934         
31935         pos.push({
31936             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31937             y : minY + (this.unitWidth + this.gutter) * rand
31938         });
31939         
31940         return pos;
31941         
31942     },
31943     
31944     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31945     {
31946         var pos = [];
31947         
31948         if(box[0].size == 'xs'){
31949             
31950             pos.push({
31951                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31952                 y : minY
31953             });
31954
31955             pos.push({
31956                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31957                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31958             });
31959             
31960             return pos;
31961             
31962         }
31963         
31964         pos.push({
31965             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31966             y : minY
31967         });
31968
31969         pos.push({
31970             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31971             y : minY + (this.unitWidth + this.gutter) * 2
31972         });
31973         
31974         return pos;
31975         
31976     },
31977     
31978     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31979     {
31980         var pos = [];
31981         
31982         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31983             
31984             pos.push({
31985                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31986                 y : minY
31987             });
31988
31989             pos.push({
31990                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31991                 y : minY + (this.unitWidth + this.gutter) * 1
31992             });
31993             
31994             pos.push({
31995                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31996                 y : minY + (this.unitWidth + this.gutter) * 2
31997             });
31998             
31999             return pos;
32000             
32001         }
32002         
32003         if(box[0].size == 'xs' && box[1].size == 'xs'){
32004             
32005             pos.push({
32006                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32007                 y : minY
32008             });
32009
32010             pos.push({
32011                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32012                 y : minY
32013             });
32014             
32015             pos.push({
32016                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32017                 y : minY + (this.unitWidth + this.gutter) * 1
32018             });
32019             
32020             return pos;
32021             
32022         }
32023         
32024         pos.push({
32025             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32026             y : minY
32027         });
32028
32029         pos.push({
32030             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32031             y : minY + (this.unitWidth + this.gutter) * 2
32032         });
32033
32034         pos.push({
32035             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32036             y : minY + (this.unitWidth + this.gutter) * 2
32037         });
32038             
32039         return pos;
32040         
32041     },
32042     
32043     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32044     {
32045         var pos = [];
32046         
32047         if(box[0].size == 'xs'){
32048             
32049             pos.push({
32050                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32051                 y : minY
32052             });
32053
32054             pos.push({
32055                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32056                 y : minY
32057             });
32058             
32059             pos.push({
32060                 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),
32061                 y : minY
32062             });
32063             
32064             pos.push({
32065                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32066                 y : minY + (this.unitWidth + this.gutter) * 1
32067             });
32068             
32069             return pos;
32070             
32071         }
32072         
32073         pos.push({
32074             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32075             y : minY
32076         });
32077         
32078         pos.push({
32079             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32080             y : minY + (this.unitWidth + this.gutter) * 2
32081         });
32082         
32083         pos.push({
32084             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32085             y : minY + (this.unitWidth + this.gutter) * 2
32086         });
32087         
32088         pos.push({
32089             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),
32090             y : minY + (this.unitWidth + this.gutter) * 2
32091         });
32092
32093         return pos;
32094         
32095     },
32096     
32097     /**
32098     * remove a Masonry Brick
32099     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32100     */
32101     removeBrick : function(brick_id)
32102     {
32103         if (!brick_id) {
32104             return;
32105         }
32106         
32107         for (var i = 0; i<this.bricks.length; i++) {
32108             if (this.bricks[i].id == brick_id) {
32109                 this.bricks.splice(i,1);
32110                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32111                 this.initial();
32112             }
32113         }
32114     },
32115     
32116     /**
32117     * adds a Masonry Brick
32118     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32119     */
32120     addBrick : function(cfg)
32121     {
32122         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32123         //this.register(cn);
32124         cn.parentId = this.id;
32125         cn.render(this.el);
32126         return cn;
32127     },
32128     
32129     /**
32130     * register a Masonry Brick
32131     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32132     */
32133     
32134     register : function(brick)
32135     {
32136         this.bricks.push(brick);
32137         brick.masonryId = this.id;
32138     },
32139     
32140     /**
32141     * clear all the Masonry Brick
32142     */
32143     clearAll : function()
32144     {
32145         this.bricks = [];
32146         //this.getChildContainer().dom.innerHTML = "";
32147         this.el.dom.innerHTML = '';
32148     },
32149     
32150     getSelected : function()
32151     {
32152         if (!this.selectedBrick) {
32153             return false;
32154         }
32155         
32156         return this.selectedBrick;
32157     }
32158 });
32159
32160 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32161     
32162     groups: {},
32163      /**
32164     * register a Masonry Layout
32165     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32166     */
32167     
32168     register : function(layout)
32169     {
32170         this.groups[layout.id] = layout;
32171     },
32172     /**
32173     * fetch a  Masonry Layout based on the masonry layout ID
32174     * @param {string} the masonry layout to add
32175     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32176     */
32177     
32178     get: function(layout_id) {
32179         if (typeof(this.groups[layout_id]) == 'undefined') {
32180             return false;
32181         }
32182         return this.groups[layout_id] ;
32183     }
32184     
32185     
32186     
32187 });
32188
32189  
32190
32191  /**
32192  *
32193  * This is based on 
32194  * http://masonry.desandro.com
32195  *
32196  * The idea is to render all the bricks based on vertical width...
32197  *
32198  * The original code extends 'outlayer' - we might need to use that....
32199  * 
32200  */
32201
32202
32203 /**
32204  * @class Roo.bootstrap.LayoutMasonryAuto
32205  * @extends Roo.bootstrap.Component
32206  * Bootstrap Layout Masonry class
32207  * 
32208  * @constructor
32209  * Create a new Element
32210  * @param {Object} config The config object
32211  */
32212
32213 Roo.bootstrap.LayoutMasonryAuto = function(config){
32214     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32215 };
32216
32217 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32218     
32219       /**
32220      * @cfg {Boolean} isFitWidth  - resize the width..
32221      */   
32222     isFitWidth : false,  // options..
32223     /**
32224      * @cfg {Boolean} isOriginLeft = left align?
32225      */   
32226     isOriginLeft : true,
32227     /**
32228      * @cfg {Boolean} isOriginTop = top align?
32229      */   
32230     isOriginTop : false,
32231     /**
32232      * @cfg {Boolean} isLayoutInstant = no animation?
32233      */   
32234     isLayoutInstant : false, // needed?
32235     /**
32236      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32237      */   
32238     isResizingContainer : true,
32239     /**
32240      * @cfg {Number} columnWidth  width of the columns 
32241      */   
32242     
32243     columnWidth : 0,
32244     
32245     /**
32246      * @cfg {Number} maxCols maximum number of columns
32247      */   
32248     
32249     maxCols: 0,
32250     /**
32251      * @cfg {Number} padHeight padding below box..
32252      */   
32253     
32254     padHeight : 10, 
32255     
32256     /**
32257      * @cfg {Boolean} isAutoInitial defalut true
32258      */   
32259     
32260     isAutoInitial : true, 
32261     
32262     // private?
32263     gutter : 0,
32264     
32265     containerWidth: 0,
32266     initialColumnWidth : 0,
32267     currentSize : null,
32268     
32269     colYs : null, // array.
32270     maxY : 0,
32271     padWidth: 10,
32272     
32273     
32274     tag: 'div',
32275     cls: '',
32276     bricks: null, //CompositeElement
32277     cols : 0, // array?
32278     // element : null, // wrapped now this.el
32279     _isLayoutInited : null, 
32280     
32281     
32282     getAutoCreate : function(){
32283         
32284         var cfg = {
32285             tag: this.tag,
32286             cls: 'blog-masonary-wrapper ' + this.cls,
32287             cn : {
32288                 cls : 'mas-boxes masonary'
32289             }
32290         };
32291         
32292         return cfg;
32293     },
32294     
32295     getChildContainer: function( )
32296     {
32297         if (this.boxesEl) {
32298             return this.boxesEl;
32299         }
32300         
32301         this.boxesEl = this.el.select('.mas-boxes').first();
32302         
32303         return this.boxesEl;
32304     },
32305     
32306     
32307     initEvents : function()
32308     {
32309         var _this = this;
32310         
32311         if(this.isAutoInitial){
32312             Roo.log('hook children rendered');
32313             this.on('childrenrendered', function() {
32314                 Roo.log('children rendered');
32315                 _this.initial();
32316             } ,this);
32317         }
32318         
32319     },
32320     
32321     initial : function()
32322     {
32323         this.reloadItems();
32324
32325         this.currentSize = this.el.getBox(true);
32326
32327         /// was window resize... - let's see if this works..
32328         Roo.EventManager.onWindowResize(this.resize, this); 
32329
32330         if(!this.isAutoInitial){
32331             this.layout();
32332             return;
32333         }
32334         
32335         this.layout.defer(500,this);
32336     },
32337     
32338     reloadItems: function()
32339     {
32340         this.bricks = this.el.select('.masonry-brick', true);
32341         
32342         this.bricks.each(function(b) {
32343             //Roo.log(b.getSize());
32344             if (!b.attr('originalwidth')) {
32345                 b.attr('originalwidth',  b.getSize().width);
32346             }
32347             
32348         });
32349         
32350         Roo.log(this.bricks.elements.length);
32351     },
32352     
32353     resize : function()
32354     {
32355         Roo.log('resize');
32356         var cs = this.el.getBox(true);
32357         
32358         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32359             Roo.log("no change in with or X");
32360             return;
32361         }
32362         this.currentSize = cs;
32363         this.layout();
32364     },
32365     
32366     layout : function()
32367     {
32368          Roo.log('layout');
32369         this._resetLayout();
32370         //this._manageStamps();
32371       
32372         // don't animate first layout
32373         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32374         this.layoutItems( isInstant );
32375       
32376         // flag for initalized
32377         this._isLayoutInited = true;
32378     },
32379     
32380     layoutItems : function( isInstant )
32381     {
32382         //var items = this._getItemsForLayout( this.items );
32383         // original code supports filtering layout items.. we just ignore it..
32384         
32385         this._layoutItems( this.bricks , isInstant );
32386       
32387         this._postLayout();
32388     },
32389     _layoutItems : function ( items , isInstant)
32390     {
32391        //this.fireEvent( 'layout', this, items );
32392     
32393
32394         if ( !items || !items.elements.length ) {
32395           // no items, emit event with empty array
32396             return;
32397         }
32398
32399         var queue = [];
32400         items.each(function(item) {
32401             Roo.log("layout item");
32402             Roo.log(item);
32403             // get x/y object from method
32404             var position = this._getItemLayoutPosition( item );
32405             // enqueue
32406             position.item = item;
32407             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32408             queue.push( position );
32409         }, this);
32410       
32411         this._processLayoutQueue( queue );
32412     },
32413     /** Sets position of item in DOM
32414     * @param {Element} item
32415     * @param {Number} x - horizontal position
32416     * @param {Number} y - vertical position
32417     * @param {Boolean} isInstant - disables transitions
32418     */
32419     _processLayoutQueue : function( queue )
32420     {
32421         for ( var i=0, len = queue.length; i < len; i++ ) {
32422             var obj = queue[i];
32423             obj.item.position('absolute');
32424             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32425         }
32426     },
32427       
32428     
32429     /**
32430     * Any logic you want to do after each layout,
32431     * i.e. size the container
32432     */
32433     _postLayout : function()
32434     {
32435         this.resizeContainer();
32436     },
32437     
32438     resizeContainer : function()
32439     {
32440         if ( !this.isResizingContainer ) {
32441             return;
32442         }
32443         var size = this._getContainerSize();
32444         if ( size ) {
32445             this.el.setSize(size.width,size.height);
32446             this.boxesEl.setSize(size.width,size.height);
32447         }
32448     },
32449     
32450     
32451     
32452     _resetLayout : function()
32453     {
32454         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32455         this.colWidth = this.el.getWidth();
32456         //this.gutter = this.el.getWidth(); 
32457         
32458         this.measureColumns();
32459
32460         // reset column Y
32461         var i = this.cols;
32462         this.colYs = [];
32463         while (i--) {
32464             this.colYs.push( 0 );
32465         }
32466     
32467         this.maxY = 0;
32468     },
32469
32470     measureColumns : function()
32471     {
32472         this.getContainerWidth();
32473       // if columnWidth is 0, default to outerWidth of first item
32474         if ( !this.columnWidth ) {
32475             var firstItem = this.bricks.first();
32476             Roo.log(firstItem);
32477             this.columnWidth  = this.containerWidth;
32478             if (firstItem && firstItem.attr('originalwidth') ) {
32479                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32480             }
32481             // columnWidth fall back to item of first element
32482             Roo.log("set column width?");
32483                         this.initialColumnWidth = this.columnWidth  ;
32484
32485             // if first elem has no width, default to size of container
32486             
32487         }
32488         
32489         
32490         if (this.initialColumnWidth) {
32491             this.columnWidth = this.initialColumnWidth;
32492         }
32493         
32494         
32495             
32496         // column width is fixed at the top - however if container width get's smaller we should
32497         // reduce it...
32498         
32499         // this bit calcs how man columns..
32500             
32501         var columnWidth = this.columnWidth += this.gutter;
32502       
32503         // calculate columns
32504         var containerWidth = this.containerWidth + this.gutter;
32505         
32506         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32507         // fix rounding errors, typically with gutters
32508         var excess = columnWidth - containerWidth % columnWidth;
32509         
32510         
32511         // if overshoot is less than a pixel, round up, otherwise floor it
32512         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32513         cols = Math[ mathMethod ]( cols );
32514         this.cols = Math.max( cols, 1 );
32515         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32516         
32517          // padding positioning..
32518         var totalColWidth = this.cols * this.columnWidth;
32519         var padavail = this.containerWidth - totalColWidth;
32520         // so for 2 columns - we need 3 'pads'
32521         
32522         var padNeeded = (1+this.cols) * this.padWidth;
32523         
32524         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32525         
32526         this.columnWidth += padExtra
32527         //this.padWidth = Math.floor(padavail /  ( this.cols));
32528         
32529         // adjust colum width so that padding is fixed??
32530         
32531         // we have 3 columns ... total = width * 3
32532         // we have X left over... that should be used by 
32533         
32534         //if (this.expandC) {
32535             
32536         //}
32537         
32538         
32539         
32540     },
32541     
32542     getContainerWidth : function()
32543     {
32544        /* // container is parent if fit width
32545         var container = this.isFitWidth ? this.element.parentNode : this.element;
32546         // check that this.size and size are there
32547         // IE8 triggers resize on body size change, so they might not be
32548         
32549         var size = getSize( container );  //FIXME
32550         this.containerWidth = size && size.innerWidth; //FIXME
32551         */
32552          
32553         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32554         
32555     },
32556     
32557     _getItemLayoutPosition : function( item )  // what is item?
32558     {
32559         // we resize the item to our columnWidth..
32560       
32561         item.setWidth(this.columnWidth);
32562         item.autoBoxAdjust  = false;
32563         
32564         var sz = item.getSize();
32565  
32566         // how many columns does this brick span
32567         var remainder = this.containerWidth % this.columnWidth;
32568         
32569         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32570         // round if off by 1 pixel, otherwise use ceil
32571         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32572         colSpan = Math.min( colSpan, this.cols );
32573         
32574         // normally this should be '1' as we dont' currently allow multi width columns..
32575         
32576         var colGroup = this._getColGroup( colSpan );
32577         // get the minimum Y value from the columns
32578         var minimumY = Math.min.apply( Math, colGroup );
32579         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32580         
32581         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32582          
32583         // position the brick
32584         var position = {
32585             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32586             y: this.currentSize.y + minimumY + this.padHeight
32587         };
32588         
32589         Roo.log(position);
32590         // apply setHeight to necessary columns
32591         var setHeight = minimumY + sz.height + this.padHeight;
32592         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32593         
32594         var setSpan = this.cols + 1 - colGroup.length;
32595         for ( var i = 0; i < setSpan; i++ ) {
32596           this.colYs[ shortColIndex + i ] = setHeight ;
32597         }
32598       
32599         return position;
32600     },
32601     
32602     /**
32603      * @param {Number} colSpan - number of columns the element spans
32604      * @returns {Array} colGroup
32605      */
32606     _getColGroup : function( colSpan )
32607     {
32608         if ( colSpan < 2 ) {
32609           // if brick spans only one column, use all the column Ys
32610           return this.colYs;
32611         }
32612       
32613         var colGroup = [];
32614         // how many different places could this brick fit horizontally
32615         var groupCount = this.cols + 1 - colSpan;
32616         // for each group potential horizontal position
32617         for ( var i = 0; i < groupCount; i++ ) {
32618           // make an array of colY values for that one group
32619           var groupColYs = this.colYs.slice( i, i + colSpan );
32620           // and get the max value of the array
32621           colGroup[i] = Math.max.apply( Math, groupColYs );
32622         }
32623         return colGroup;
32624     },
32625     /*
32626     _manageStamp : function( stamp )
32627     {
32628         var stampSize =  stamp.getSize();
32629         var offset = stamp.getBox();
32630         // get the columns that this stamp affects
32631         var firstX = this.isOriginLeft ? offset.x : offset.right;
32632         var lastX = firstX + stampSize.width;
32633         var firstCol = Math.floor( firstX / this.columnWidth );
32634         firstCol = Math.max( 0, firstCol );
32635         
32636         var lastCol = Math.floor( lastX / this.columnWidth );
32637         // lastCol should not go over if multiple of columnWidth #425
32638         lastCol -= lastX % this.columnWidth ? 0 : 1;
32639         lastCol = Math.min( this.cols - 1, lastCol );
32640         
32641         // set colYs to bottom of the stamp
32642         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32643             stampSize.height;
32644             
32645         for ( var i = firstCol; i <= lastCol; i++ ) {
32646           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32647         }
32648     },
32649     */
32650     
32651     _getContainerSize : function()
32652     {
32653         this.maxY = Math.max.apply( Math, this.colYs );
32654         var size = {
32655             height: this.maxY
32656         };
32657       
32658         if ( this.isFitWidth ) {
32659             size.width = this._getContainerFitWidth();
32660         }
32661       
32662         return size;
32663     },
32664     
32665     _getContainerFitWidth : function()
32666     {
32667         var unusedCols = 0;
32668         // count unused columns
32669         var i = this.cols;
32670         while ( --i ) {
32671           if ( this.colYs[i] !== 0 ) {
32672             break;
32673           }
32674           unusedCols++;
32675         }
32676         // fit container to columns that have been used
32677         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32678     },
32679     
32680     needsResizeLayout : function()
32681     {
32682         var previousWidth = this.containerWidth;
32683         this.getContainerWidth();
32684         return previousWidth !== this.containerWidth;
32685     }
32686  
32687 });
32688
32689  
32690
32691  /*
32692  * - LGPL
32693  *
32694  * element
32695  * 
32696  */
32697
32698 /**
32699  * @class Roo.bootstrap.MasonryBrick
32700  * @extends Roo.bootstrap.Component
32701  * Bootstrap MasonryBrick class
32702  * 
32703  * @constructor
32704  * Create a new MasonryBrick
32705  * @param {Object} config The config object
32706  */
32707
32708 Roo.bootstrap.MasonryBrick = function(config){
32709     
32710     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32711     
32712     Roo.bootstrap.MasonryBrick.register(this);
32713     
32714     this.addEvents({
32715         // raw events
32716         /**
32717          * @event click
32718          * When a MasonryBrick is clcik
32719          * @param {Roo.bootstrap.MasonryBrick} this
32720          * @param {Roo.EventObject} e
32721          */
32722         "click" : true
32723     });
32724 };
32725
32726 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32727     
32728     /**
32729      * @cfg {String} title
32730      */   
32731     title : '',
32732     /**
32733      * @cfg {String} html
32734      */   
32735     html : '',
32736     /**
32737      * @cfg {String} bgimage
32738      */   
32739     bgimage : '',
32740     /**
32741      * @cfg {String} videourl
32742      */   
32743     videourl : '',
32744     /**
32745      * @cfg {String} cls
32746      */   
32747     cls : '',
32748     /**
32749      * @cfg {String} href
32750      */   
32751     href : '',
32752     /**
32753      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32754      */   
32755     size : 'xs',
32756     
32757     /**
32758      * @cfg {String} placetitle (center|bottom)
32759      */   
32760     placetitle : '',
32761     
32762     /**
32763      * @cfg {Boolean} isFitContainer defalut true
32764      */   
32765     isFitContainer : true, 
32766     
32767     /**
32768      * @cfg {Boolean} preventDefault defalut false
32769      */   
32770     preventDefault : false, 
32771     
32772     /**
32773      * @cfg {Boolean} inverse defalut false
32774      */   
32775     maskInverse : false, 
32776     
32777     getAutoCreate : function()
32778     {
32779         if(!this.isFitContainer){
32780             return this.getSplitAutoCreate();
32781         }
32782         
32783         var cls = 'masonry-brick masonry-brick-full';
32784         
32785         if(this.href.length){
32786             cls += ' masonry-brick-link';
32787         }
32788         
32789         if(this.bgimage.length){
32790             cls += ' masonry-brick-image';
32791         }
32792         
32793         if(this.maskInverse){
32794             cls += ' mask-inverse';
32795         }
32796         
32797         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32798             cls += ' enable-mask';
32799         }
32800         
32801         if(this.size){
32802             cls += ' masonry-' + this.size + '-brick';
32803         }
32804         
32805         if(this.placetitle.length){
32806             
32807             switch (this.placetitle) {
32808                 case 'center' :
32809                     cls += ' masonry-center-title';
32810                     break;
32811                 case 'bottom' :
32812                     cls += ' masonry-bottom-title';
32813                     break;
32814                 default:
32815                     break;
32816             }
32817             
32818         } else {
32819             if(!this.html.length && !this.bgimage.length){
32820                 cls += ' masonry-center-title';
32821             }
32822
32823             if(!this.html.length && this.bgimage.length){
32824                 cls += ' masonry-bottom-title';
32825             }
32826         }
32827         
32828         if(this.cls){
32829             cls += ' ' + this.cls;
32830         }
32831         
32832         var cfg = {
32833             tag: (this.href.length) ? 'a' : 'div',
32834             cls: cls,
32835             cn: [
32836                 {
32837                     tag: 'div',
32838                     cls: 'masonry-brick-mask'
32839                 },
32840                 {
32841                     tag: 'div',
32842                     cls: 'masonry-brick-paragraph',
32843                     cn: []
32844                 }
32845             ]
32846         };
32847         
32848         if(this.href.length){
32849             cfg.href = this.href;
32850         }
32851         
32852         var cn = cfg.cn[1].cn;
32853         
32854         if(this.title.length){
32855             cn.push({
32856                 tag: 'h4',
32857                 cls: 'masonry-brick-title',
32858                 html: this.title
32859             });
32860         }
32861         
32862         if(this.html.length){
32863             cn.push({
32864                 tag: 'p',
32865                 cls: 'masonry-brick-text',
32866                 html: this.html
32867             });
32868         }
32869         
32870         if (!this.title.length && !this.html.length) {
32871             cfg.cn[1].cls += ' hide';
32872         }
32873         
32874         if(this.bgimage.length){
32875             cfg.cn.push({
32876                 tag: 'img',
32877                 cls: 'masonry-brick-image-view',
32878                 src: this.bgimage
32879             });
32880         }
32881         
32882         if(this.videourl.length){
32883             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32884             // youtube support only?
32885             cfg.cn.push({
32886                 tag: 'iframe',
32887                 cls: 'masonry-brick-image-view',
32888                 src: vurl,
32889                 frameborder : 0,
32890                 allowfullscreen : true
32891             });
32892         }
32893         
32894         return cfg;
32895         
32896     },
32897     
32898     getSplitAutoCreate : function()
32899     {
32900         var cls = 'masonry-brick masonry-brick-split';
32901         
32902         if(this.href.length){
32903             cls += ' masonry-brick-link';
32904         }
32905         
32906         if(this.bgimage.length){
32907             cls += ' masonry-brick-image';
32908         }
32909         
32910         if(this.size){
32911             cls += ' masonry-' + this.size + '-brick';
32912         }
32913         
32914         switch (this.placetitle) {
32915             case 'center' :
32916                 cls += ' masonry-center-title';
32917                 break;
32918             case 'bottom' :
32919                 cls += ' masonry-bottom-title';
32920                 break;
32921             default:
32922                 if(!this.bgimage.length){
32923                     cls += ' masonry-center-title';
32924                 }
32925
32926                 if(this.bgimage.length){
32927                     cls += ' masonry-bottom-title';
32928                 }
32929                 break;
32930         }
32931         
32932         if(this.cls){
32933             cls += ' ' + this.cls;
32934         }
32935         
32936         var cfg = {
32937             tag: (this.href.length) ? 'a' : 'div',
32938             cls: cls,
32939             cn: [
32940                 {
32941                     tag: 'div',
32942                     cls: 'masonry-brick-split-head',
32943                     cn: [
32944                         {
32945                             tag: 'div',
32946                             cls: 'masonry-brick-paragraph',
32947                             cn: []
32948                         }
32949                     ]
32950                 },
32951                 {
32952                     tag: 'div',
32953                     cls: 'masonry-brick-split-body',
32954                     cn: []
32955                 }
32956             ]
32957         };
32958         
32959         if(this.href.length){
32960             cfg.href = this.href;
32961         }
32962         
32963         if(this.title.length){
32964             cfg.cn[0].cn[0].cn.push({
32965                 tag: 'h4',
32966                 cls: 'masonry-brick-title',
32967                 html: this.title
32968             });
32969         }
32970         
32971         if(this.html.length){
32972             cfg.cn[1].cn.push({
32973                 tag: 'p',
32974                 cls: 'masonry-brick-text',
32975                 html: this.html
32976             });
32977         }
32978
32979         if(this.bgimage.length){
32980             cfg.cn[0].cn.push({
32981                 tag: 'img',
32982                 cls: 'masonry-brick-image-view',
32983                 src: this.bgimage
32984             });
32985         }
32986         
32987         if(this.videourl.length){
32988             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32989             // youtube support only?
32990             cfg.cn[0].cn.cn.push({
32991                 tag: 'iframe',
32992                 cls: 'masonry-brick-image-view',
32993                 src: vurl,
32994                 frameborder : 0,
32995                 allowfullscreen : true
32996             });
32997         }
32998         
32999         return cfg;
33000     },
33001     
33002     initEvents: function() 
33003     {
33004         switch (this.size) {
33005             case 'xs' :
33006                 this.x = 1;
33007                 this.y = 1;
33008                 break;
33009             case 'sm' :
33010                 this.x = 2;
33011                 this.y = 2;
33012                 break;
33013             case 'md' :
33014             case 'md-left' :
33015             case 'md-right' :
33016                 this.x = 3;
33017                 this.y = 3;
33018                 break;
33019             case 'tall' :
33020                 this.x = 2;
33021                 this.y = 3;
33022                 break;
33023             case 'wide' :
33024                 this.x = 3;
33025                 this.y = 2;
33026                 break;
33027             case 'wide-thin' :
33028                 this.x = 3;
33029                 this.y = 1;
33030                 break;
33031                         
33032             default :
33033                 break;
33034         }
33035         
33036         if(Roo.isTouch){
33037             this.el.on('touchstart', this.onTouchStart, this);
33038             this.el.on('touchmove', this.onTouchMove, this);
33039             this.el.on('touchend', this.onTouchEnd, this);
33040             this.el.on('contextmenu', this.onContextMenu, this);
33041         } else {
33042             this.el.on('mouseenter'  ,this.enter, this);
33043             this.el.on('mouseleave', this.leave, this);
33044             this.el.on('click', this.onClick, this);
33045         }
33046         
33047         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33048             this.parent().bricks.push(this);   
33049         }
33050         
33051     },
33052     
33053     onClick: function(e, el)
33054     {
33055         var time = this.endTimer - this.startTimer;
33056         // Roo.log(e.preventDefault());
33057         if(Roo.isTouch){
33058             if(time > 1000){
33059                 e.preventDefault();
33060                 return;
33061             }
33062         }
33063         
33064         if(!this.preventDefault){
33065             return;
33066         }
33067         
33068         e.preventDefault();
33069         
33070         if (this.activeClass != '') {
33071             this.selectBrick();
33072         }
33073         
33074         this.fireEvent('click', this, e);
33075     },
33076     
33077     enter: function(e, el)
33078     {
33079         e.preventDefault();
33080         
33081         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33082             return;
33083         }
33084         
33085         if(this.bgimage.length && this.html.length){
33086             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33087         }
33088     },
33089     
33090     leave: function(e, el)
33091     {
33092         e.preventDefault();
33093         
33094         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33095             return;
33096         }
33097         
33098         if(this.bgimage.length && this.html.length){
33099             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33100         }
33101     },
33102     
33103     onTouchStart: function(e, el)
33104     {
33105 //        e.preventDefault();
33106         
33107         this.touchmoved = false;
33108         
33109         if(!this.isFitContainer){
33110             return;
33111         }
33112         
33113         if(!this.bgimage.length || !this.html.length){
33114             return;
33115         }
33116         
33117         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33118         
33119         this.timer = new Date().getTime();
33120         
33121     },
33122     
33123     onTouchMove: function(e, el)
33124     {
33125         this.touchmoved = true;
33126     },
33127     
33128     onContextMenu : function(e,el)
33129     {
33130         e.preventDefault();
33131         e.stopPropagation();
33132         return false;
33133     },
33134     
33135     onTouchEnd: function(e, el)
33136     {
33137 //        e.preventDefault();
33138         
33139         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33140         
33141             this.leave(e,el);
33142             
33143             return;
33144         }
33145         
33146         if(!this.bgimage.length || !this.html.length){
33147             
33148             if(this.href.length){
33149                 window.location.href = this.href;
33150             }
33151             
33152             return;
33153         }
33154         
33155         if(!this.isFitContainer){
33156             return;
33157         }
33158         
33159         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33160         
33161         window.location.href = this.href;
33162     },
33163     
33164     //selection on single brick only
33165     selectBrick : function() {
33166         
33167         if (!this.parentId) {
33168             return;
33169         }
33170         
33171         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33172         var index = m.selectedBrick.indexOf(this.id);
33173         
33174         if ( index > -1) {
33175             m.selectedBrick.splice(index,1);
33176             this.el.removeClass(this.activeClass);
33177             return;
33178         }
33179         
33180         for(var i = 0; i < m.selectedBrick.length; i++) {
33181             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33182             b.el.removeClass(b.activeClass);
33183         }
33184         
33185         m.selectedBrick = [];
33186         
33187         m.selectedBrick.push(this.id);
33188         this.el.addClass(this.activeClass);
33189         return;
33190     },
33191     
33192     isSelected : function(){
33193         return this.el.hasClass(this.activeClass);
33194         
33195     }
33196 });
33197
33198 Roo.apply(Roo.bootstrap.MasonryBrick, {
33199     
33200     //groups: {},
33201     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33202      /**
33203     * register a Masonry Brick
33204     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33205     */
33206     
33207     register : function(brick)
33208     {
33209         //this.groups[brick.id] = brick;
33210         this.groups.add(brick.id, brick);
33211     },
33212     /**
33213     * fetch a  masonry brick based on the masonry brick ID
33214     * @param {string} the masonry brick to add
33215     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33216     */
33217     
33218     get: function(brick_id) 
33219     {
33220         // if (typeof(this.groups[brick_id]) == 'undefined') {
33221         //     return false;
33222         // }
33223         // return this.groups[brick_id] ;
33224         
33225         if(this.groups.key(brick_id)) {
33226             return this.groups.key(brick_id);
33227         }
33228         
33229         return false;
33230     }
33231     
33232     
33233     
33234 });
33235
33236  /*
33237  * - LGPL
33238  *
33239  * element
33240  * 
33241  */
33242
33243 /**
33244  * @class Roo.bootstrap.Brick
33245  * @extends Roo.bootstrap.Component
33246  * Bootstrap Brick class
33247  * 
33248  * @constructor
33249  * Create a new Brick
33250  * @param {Object} config The config object
33251  */
33252
33253 Roo.bootstrap.Brick = function(config){
33254     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33255     
33256     this.addEvents({
33257         // raw events
33258         /**
33259          * @event click
33260          * When a Brick is click
33261          * @param {Roo.bootstrap.Brick} this
33262          * @param {Roo.EventObject} e
33263          */
33264         "click" : true
33265     });
33266 };
33267
33268 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33269     
33270     /**
33271      * @cfg {String} title
33272      */   
33273     title : '',
33274     /**
33275      * @cfg {String} html
33276      */   
33277     html : '',
33278     /**
33279      * @cfg {String} bgimage
33280      */   
33281     bgimage : '',
33282     /**
33283      * @cfg {String} cls
33284      */   
33285     cls : '',
33286     /**
33287      * @cfg {String} href
33288      */   
33289     href : '',
33290     /**
33291      * @cfg {String} video
33292      */   
33293     video : '',
33294     /**
33295      * @cfg {Boolean} square
33296      */   
33297     square : true,
33298     
33299     getAutoCreate : function()
33300     {
33301         var cls = 'roo-brick';
33302         
33303         if(this.href.length){
33304             cls += ' roo-brick-link';
33305         }
33306         
33307         if(this.bgimage.length){
33308             cls += ' roo-brick-image';
33309         }
33310         
33311         if(!this.html.length && !this.bgimage.length){
33312             cls += ' roo-brick-center-title';
33313         }
33314         
33315         if(!this.html.length && this.bgimage.length){
33316             cls += ' roo-brick-bottom-title';
33317         }
33318         
33319         if(this.cls){
33320             cls += ' ' + this.cls;
33321         }
33322         
33323         var cfg = {
33324             tag: (this.href.length) ? 'a' : 'div',
33325             cls: cls,
33326             cn: [
33327                 {
33328                     tag: 'div',
33329                     cls: 'roo-brick-paragraph',
33330                     cn: []
33331                 }
33332             ]
33333         };
33334         
33335         if(this.href.length){
33336             cfg.href = this.href;
33337         }
33338         
33339         var cn = cfg.cn[0].cn;
33340         
33341         if(this.title.length){
33342             cn.push({
33343                 tag: 'h4',
33344                 cls: 'roo-brick-title',
33345                 html: this.title
33346             });
33347         }
33348         
33349         if(this.html.length){
33350             cn.push({
33351                 tag: 'p',
33352                 cls: 'roo-brick-text',
33353                 html: this.html
33354             });
33355         } else {
33356             cn.cls += ' hide';
33357         }
33358         
33359         if(this.bgimage.length){
33360             cfg.cn.push({
33361                 tag: 'img',
33362                 cls: 'roo-brick-image-view',
33363                 src: this.bgimage
33364             });
33365         }
33366         
33367         return cfg;
33368     },
33369     
33370     initEvents: function() 
33371     {
33372         if(this.title.length || this.html.length){
33373             this.el.on('mouseenter'  ,this.enter, this);
33374             this.el.on('mouseleave', this.leave, this);
33375         }
33376         
33377         Roo.EventManager.onWindowResize(this.resize, this); 
33378         
33379         if(this.bgimage.length){
33380             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33381             this.imageEl.on('load', this.onImageLoad, this);
33382             return;
33383         }
33384         
33385         this.resize();
33386     },
33387     
33388     onImageLoad : function()
33389     {
33390         this.resize();
33391     },
33392     
33393     resize : function()
33394     {
33395         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33396         
33397         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33398         
33399         if(this.bgimage.length){
33400             var image = this.el.select('.roo-brick-image-view', true).first();
33401             
33402             image.setWidth(paragraph.getWidth());
33403             
33404             if(this.square){
33405                 image.setHeight(paragraph.getWidth());
33406             }
33407             
33408             this.el.setHeight(image.getHeight());
33409             paragraph.setHeight(image.getHeight());
33410             
33411         }
33412         
33413     },
33414     
33415     enter: function(e, el)
33416     {
33417         e.preventDefault();
33418         
33419         if(this.bgimage.length){
33420             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33421             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33422         }
33423     },
33424     
33425     leave: function(e, el)
33426     {
33427         e.preventDefault();
33428         
33429         if(this.bgimage.length){
33430             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33431             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33432         }
33433     }
33434     
33435 });
33436
33437  
33438
33439  /*
33440  * - LGPL
33441  *
33442  * Number field 
33443  */
33444
33445 /**
33446  * @class Roo.bootstrap.NumberField
33447  * @extends Roo.bootstrap.Input
33448  * Bootstrap NumberField class
33449  * 
33450  * 
33451  * 
33452  * 
33453  * @constructor
33454  * Create a new NumberField
33455  * @param {Object} config The config object
33456  */
33457
33458 Roo.bootstrap.NumberField = function(config){
33459     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33460 };
33461
33462 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33463     
33464     /**
33465      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33466      */
33467     allowDecimals : true,
33468     /**
33469      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33470      */
33471     decimalSeparator : ".",
33472     /**
33473      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33474      */
33475     decimalPrecision : 2,
33476     /**
33477      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33478      */
33479     allowNegative : true,
33480     
33481     /**
33482      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33483      */
33484     allowZero: true,
33485     /**
33486      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33487      */
33488     minValue : Number.NEGATIVE_INFINITY,
33489     /**
33490      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33491      */
33492     maxValue : Number.MAX_VALUE,
33493     /**
33494      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33495      */
33496     minText : "The minimum value for this field is {0}",
33497     /**
33498      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33499      */
33500     maxText : "The maximum value for this field is {0}",
33501     /**
33502      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33503      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33504      */
33505     nanText : "{0} is not a valid number",
33506     /**
33507      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33508      */
33509     thousandsDelimiter : false,
33510     /**
33511      * @cfg {String} valueAlign alignment of value
33512      */
33513     valueAlign : "left",
33514
33515     getAutoCreate : function()
33516     {
33517         var hiddenInput = {
33518             tag: 'input',
33519             type: 'hidden',
33520             id: Roo.id(),
33521             cls: 'hidden-number-input'
33522         };
33523         
33524         if (this.name) {
33525             hiddenInput.name = this.name;
33526         }
33527         
33528         this.name = '';
33529         
33530         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33531         
33532         this.name = hiddenInput.name;
33533         
33534         if(cfg.cn.length > 0) {
33535             cfg.cn.push(hiddenInput);
33536         }
33537         
33538         return cfg;
33539     },
33540
33541     // private
33542     initEvents : function()
33543     {   
33544         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33545         
33546         var allowed = "0123456789";
33547         
33548         if(this.allowDecimals){
33549             allowed += this.decimalSeparator;
33550         }
33551         
33552         if(this.allowNegative){
33553             allowed += "-";
33554         }
33555         
33556         if(this.thousandsDelimiter) {
33557             allowed += ",";
33558         }
33559         
33560         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33561         
33562         var keyPress = function(e){
33563             
33564             var k = e.getKey();
33565             
33566             var c = e.getCharCode();
33567             
33568             if(
33569                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33570                     allowed.indexOf(String.fromCharCode(c)) === -1
33571             ){
33572                 e.stopEvent();
33573                 return;
33574             }
33575             
33576             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33577                 return;
33578             }
33579             
33580             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33581                 e.stopEvent();
33582             }
33583         };
33584         
33585         this.el.on("keypress", keyPress, this);
33586     },
33587     
33588     validateValue : function(value)
33589     {
33590         
33591         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33592             return false;
33593         }
33594         
33595         var num = this.parseValue(value);
33596         
33597         if(isNaN(num)){
33598             this.markInvalid(String.format(this.nanText, value));
33599             return false;
33600         }
33601         
33602         if(num < this.minValue){
33603             this.markInvalid(String.format(this.minText, this.minValue));
33604             return false;
33605         }
33606         
33607         if(num > this.maxValue){
33608             this.markInvalid(String.format(this.maxText, this.maxValue));
33609             return false;
33610         }
33611         
33612         return true;
33613     },
33614
33615     getValue : function()
33616     {
33617         var v = this.hiddenEl().getValue();
33618         
33619         return this.fixPrecision(this.parseValue(v));
33620     },
33621
33622     parseValue : function(value)
33623     {
33624         if(this.thousandsDelimiter) {
33625             value += "";
33626             r = new RegExp(",", "g");
33627             value = value.replace(r, "");
33628         }
33629         
33630         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33631         return isNaN(value) ? '' : value;
33632     },
33633
33634     fixPrecision : function(value)
33635     {
33636         if(this.thousandsDelimiter) {
33637             value += "";
33638             r = new RegExp(",", "g");
33639             value = value.replace(r, "");
33640         }
33641         
33642         var nan = isNaN(value);
33643         
33644         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33645             return nan ? '' : value;
33646         }
33647         return parseFloat(value).toFixed(this.decimalPrecision);
33648     },
33649
33650     setValue : function(v)
33651     {
33652         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33653         
33654         this.value = v;
33655         
33656         if(this.rendered){
33657             
33658             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33659             
33660             this.inputEl().dom.value = (v == '') ? '' :
33661                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33662             
33663             if(!this.allowZero && v === '0') {
33664                 this.hiddenEl().dom.value = '';
33665                 this.inputEl().dom.value = '';
33666             }
33667             
33668             this.validate();
33669         }
33670     },
33671
33672     decimalPrecisionFcn : function(v)
33673     {
33674         return Math.floor(v);
33675     },
33676
33677     beforeBlur : function()
33678     {
33679         var v = this.parseValue(this.getRawValue());
33680         
33681         if(v || v === 0 || v === ''){
33682             this.setValue(v);
33683         }
33684     },
33685     
33686     hiddenEl : function()
33687     {
33688         return this.el.select('input.hidden-number-input',true).first();
33689     }
33690     
33691 });
33692
33693  
33694
33695 /*
33696 * Licence: LGPL
33697 */
33698
33699 /**
33700  * @class Roo.bootstrap.DocumentSlider
33701  * @extends Roo.bootstrap.Component
33702  * Bootstrap DocumentSlider class
33703  * 
33704  * @constructor
33705  * Create a new DocumentViewer
33706  * @param {Object} config The config object
33707  */
33708
33709 Roo.bootstrap.DocumentSlider = function(config){
33710     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33711     
33712     this.files = [];
33713     
33714     this.addEvents({
33715         /**
33716          * @event initial
33717          * Fire after initEvent
33718          * @param {Roo.bootstrap.DocumentSlider} this
33719          */
33720         "initial" : true,
33721         /**
33722          * @event update
33723          * Fire after update
33724          * @param {Roo.bootstrap.DocumentSlider} this
33725          */
33726         "update" : true,
33727         /**
33728          * @event click
33729          * Fire after click
33730          * @param {Roo.bootstrap.DocumentSlider} this
33731          */
33732         "click" : true
33733     });
33734 };
33735
33736 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33737     
33738     files : false,
33739     
33740     indicator : 0,
33741     
33742     getAutoCreate : function()
33743     {
33744         var cfg = {
33745             tag : 'div',
33746             cls : 'roo-document-slider',
33747             cn : [
33748                 {
33749                     tag : 'div',
33750                     cls : 'roo-document-slider-header',
33751                     cn : [
33752                         {
33753                             tag : 'div',
33754                             cls : 'roo-document-slider-header-title'
33755                         }
33756                     ]
33757                 },
33758                 {
33759                     tag : 'div',
33760                     cls : 'roo-document-slider-body',
33761                     cn : [
33762                         {
33763                             tag : 'div',
33764                             cls : 'roo-document-slider-prev',
33765                             cn : [
33766                                 {
33767                                     tag : 'i',
33768                                     cls : 'fa fa-chevron-left'
33769                                 }
33770                             ]
33771                         },
33772                         {
33773                             tag : 'div',
33774                             cls : 'roo-document-slider-thumb',
33775                             cn : [
33776                                 {
33777                                     tag : 'img',
33778                                     cls : 'roo-document-slider-image'
33779                                 }
33780                             ]
33781                         },
33782                         {
33783                             tag : 'div',
33784                             cls : 'roo-document-slider-next',
33785                             cn : [
33786                                 {
33787                                     tag : 'i',
33788                                     cls : 'fa fa-chevron-right'
33789                                 }
33790                             ]
33791                         }
33792                     ]
33793                 }
33794             ]
33795         };
33796         
33797         return cfg;
33798     },
33799     
33800     initEvents : function()
33801     {
33802         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33803         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33804         
33805         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33806         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33807         
33808         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33809         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33810         
33811         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33812         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33813         
33814         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33815         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33816         
33817         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33818         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33819         
33820         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33821         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33822         
33823         this.thumbEl.on('click', this.onClick, this);
33824         
33825         this.prevIndicator.on('click', this.prev, this);
33826         
33827         this.nextIndicator.on('click', this.next, this);
33828         
33829     },
33830     
33831     initial : function()
33832     {
33833         if(this.files.length){
33834             this.indicator = 1;
33835             this.update()
33836         }
33837         
33838         this.fireEvent('initial', this);
33839     },
33840     
33841     update : function()
33842     {
33843         this.imageEl.attr('src', this.files[this.indicator - 1]);
33844         
33845         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33846         
33847         this.prevIndicator.show();
33848         
33849         if(this.indicator == 1){
33850             this.prevIndicator.hide();
33851         }
33852         
33853         this.nextIndicator.show();
33854         
33855         if(this.indicator == this.files.length){
33856             this.nextIndicator.hide();
33857         }
33858         
33859         this.thumbEl.scrollTo('top');
33860         
33861         this.fireEvent('update', this);
33862     },
33863     
33864     onClick : function(e)
33865     {
33866         e.preventDefault();
33867         
33868         this.fireEvent('click', this);
33869     },
33870     
33871     prev : function(e)
33872     {
33873         e.preventDefault();
33874         
33875         this.indicator = Math.max(1, this.indicator - 1);
33876         
33877         this.update();
33878     },
33879     
33880     next : function(e)
33881     {
33882         e.preventDefault();
33883         
33884         this.indicator = Math.min(this.files.length, this.indicator + 1);
33885         
33886         this.update();
33887     }
33888 });
33889 /*
33890  * - LGPL
33891  *
33892  * RadioSet
33893  *
33894  *
33895  */
33896
33897 /**
33898  * @class Roo.bootstrap.RadioSet
33899  * @extends Roo.bootstrap.Input
33900  * Bootstrap RadioSet class
33901  * @cfg {String} indicatorpos (left|right) default left
33902  * @cfg {Boolean} inline (true|false) inline the element (default true)
33903  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33904  * @constructor
33905  * Create a new RadioSet
33906  * @param {Object} config The config object
33907  */
33908
33909 Roo.bootstrap.RadioSet = function(config){
33910     
33911     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33912     
33913     this.radioes = [];
33914     
33915     Roo.bootstrap.RadioSet.register(this);
33916     
33917     this.addEvents({
33918         /**
33919         * @event check
33920         * Fires when the element is checked or unchecked.
33921         * @param {Roo.bootstrap.RadioSet} this This radio
33922         * @param {Roo.bootstrap.Radio} item The checked item
33923         */
33924        check : true,
33925        /**
33926         * @event click
33927         * Fires when the element is click.
33928         * @param {Roo.bootstrap.RadioSet} this This radio set
33929         * @param {Roo.bootstrap.Radio} item The checked item
33930         * @param {Roo.EventObject} e The event object
33931         */
33932        click : true
33933     });
33934     
33935 };
33936
33937 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33938
33939     radioes : false,
33940     
33941     inline : true,
33942     
33943     weight : '',
33944     
33945     indicatorpos : 'left',
33946     
33947     getAutoCreate : function()
33948     {
33949         var label = {
33950             tag : 'label',
33951             cls : 'roo-radio-set-label',
33952             cn : [
33953                 {
33954                     tag : 'span',
33955                     html : this.fieldLabel
33956                 }
33957             ]
33958         };
33959         
33960         if(this.indicatorpos == 'left'){
33961             label.cn.unshift({
33962                 tag : 'i',
33963                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33964                 tooltip : 'This field is required'
33965             });
33966         } else {
33967             label.cn.push({
33968                 tag : 'i',
33969                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33970                 tooltip : 'This field is required'
33971             });
33972         }
33973         
33974         var items = {
33975             tag : 'div',
33976             cls : 'roo-radio-set-items'
33977         };
33978         
33979         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33980         
33981         if (align === 'left' && this.fieldLabel.length) {
33982             
33983             items = {
33984                 cls : "roo-radio-set-right", 
33985                 cn: [
33986                     items
33987                 ]
33988             };
33989             
33990             if(this.labelWidth > 12){
33991                 label.style = "width: " + this.labelWidth + 'px';
33992             }
33993             
33994             if(this.labelWidth < 13 && this.labelmd == 0){
33995                 this.labelmd = this.labelWidth;
33996             }
33997             
33998             if(this.labellg > 0){
33999                 label.cls += ' col-lg-' + this.labellg;
34000                 items.cls += ' col-lg-' + (12 - this.labellg);
34001             }
34002             
34003             if(this.labelmd > 0){
34004                 label.cls += ' col-md-' + this.labelmd;
34005                 items.cls += ' col-md-' + (12 - this.labelmd);
34006             }
34007             
34008             if(this.labelsm > 0){
34009                 label.cls += ' col-sm-' + this.labelsm;
34010                 items.cls += ' col-sm-' + (12 - this.labelsm);
34011             }
34012             
34013             if(this.labelxs > 0){
34014                 label.cls += ' col-xs-' + this.labelxs;
34015                 items.cls += ' col-xs-' + (12 - this.labelxs);
34016             }
34017         }
34018         
34019         var cfg = {
34020             tag : 'div',
34021             cls : 'roo-radio-set',
34022             cn : [
34023                 {
34024                     tag : 'input',
34025                     cls : 'roo-radio-set-input',
34026                     type : 'hidden',
34027                     name : this.name,
34028                     value : this.value ? this.value :  ''
34029                 },
34030                 label,
34031                 items
34032             ]
34033         };
34034         
34035         if(this.weight.length){
34036             cfg.cls += ' roo-radio-' + this.weight;
34037         }
34038         
34039         if(this.inline) {
34040             cfg.cls += ' roo-radio-set-inline';
34041         }
34042         
34043         var settings=this;
34044         ['xs','sm','md','lg'].map(function(size){
34045             if (settings[size]) {
34046                 cfg.cls += ' col-' + size + '-' + settings[size];
34047             }
34048         });
34049         
34050         return cfg;
34051         
34052     },
34053
34054     initEvents : function()
34055     {
34056         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34057         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34058         
34059         if(!this.fieldLabel.length){
34060             this.labelEl.hide();
34061         }
34062         
34063         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34064         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34065         
34066         this.indicator = this.indicatorEl();
34067         
34068         if(this.indicator){
34069             this.indicator.addClass('invisible');
34070         }
34071         
34072         this.originalValue = this.getValue();
34073         
34074     },
34075     
34076     inputEl: function ()
34077     {
34078         return this.el.select('.roo-radio-set-input', true).first();
34079     },
34080     
34081     getChildContainer : function()
34082     {
34083         return this.itemsEl;
34084     },
34085     
34086     register : function(item)
34087     {
34088         this.radioes.push(item);
34089         
34090     },
34091     
34092     validate : function()
34093     {   
34094         if(this.getVisibilityEl().hasClass('hidden')){
34095             return true;
34096         }
34097         
34098         var valid = false;
34099         
34100         Roo.each(this.radioes, function(i){
34101             if(!i.checked){
34102                 return;
34103             }
34104             
34105             valid = true;
34106             return false;
34107         });
34108         
34109         if(this.allowBlank) {
34110             return true;
34111         }
34112         
34113         if(this.disabled || valid){
34114             this.markValid();
34115             return true;
34116         }
34117         
34118         this.markInvalid();
34119         return false;
34120         
34121     },
34122     
34123     markValid : function()
34124     {
34125         if(this.labelEl.isVisible(true)){
34126             this.indicatorEl().removeClass('visible');
34127             this.indicatorEl().addClass('invisible');
34128         }
34129         
34130         this.el.removeClass([this.invalidClass, this.validClass]);
34131         this.el.addClass(this.validClass);
34132         
34133         this.fireEvent('valid', this);
34134     },
34135     
34136     markInvalid : function(msg)
34137     {
34138         if(this.allowBlank || this.disabled){
34139             return;
34140         }
34141         
34142         if(this.labelEl.isVisible(true)){
34143             this.indicatorEl().removeClass('invisible');
34144             this.indicatorEl().addClass('visible');
34145         }
34146         
34147         this.el.removeClass([this.invalidClass, this.validClass]);
34148         this.el.addClass(this.invalidClass);
34149         
34150         this.fireEvent('invalid', this, msg);
34151         
34152     },
34153     
34154     setValue : function(v, suppressEvent)
34155     {   
34156         if(this.value === v){
34157             return;
34158         }
34159         
34160         this.value = v;
34161         
34162         if(this.rendered){
34163             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34164         }
34165         
34166         Roo.each(this.radioes, function(i){
34167             i.checked = false;
34168             i.el.removeClass('checked');
34169         });
34170         
34171         Roo.each(this.radioes, function(i){
34172             
34173             if(i.value === v || i.value.toString() === v.toString()){
34174                 i.checked = true;
34175                 i.el.addClass('checked');
34176                 
34177                 if(suppressEvent !== true){
34178                     this.fireEvent('check', this, i);
34179                 }
34180                 
34181                 return false;
34182             }
34183             
34184         }, this);
34185         
34186         this.validate();
34187     },
34188     
34189     clearInvalid : function(){
34190         
34191         if(!this.el || this.preventMark){
34192             return;
34193         }
34194         
34195         this.el.removeClass([this.invalidClass]);
34196         
34197         this.fireEvent('valid', this);
34198     }
34199     
34200 });
34201
34202 Roo.apply(Roo.bootstrap.RadioSet, {
34203     
34204     groups: {},
34205     
34206     register : function(set)
34207     {
34208         this.groups[set.name] = set;
34209     },
34210     
34211     get: function(name) 
34212     {
34213         if (typeof(this.groups[name]) == 'undefined') {
34214             return false;
34215         }
34216         
34217         return this.groups[name] ;
34218     }
34219     
34220 });
34221 /*
34222  * Based on:
34223  * Ext JS Library 1.1.1
34224  * Copyright(c) 2006-2007, Ext JS, LLC.
34225  *
34226  * Originally Released Under LGPL - original licence link has changed is not relivant.
34227  *
34228  * Fork - LGPL
34229  * <script type="text/javascript">
34230  */
34231
34232
34233 /**
34234  * @class Roo.bootstrap.SplitBar
34235  * @extends Roo.util.Observable
34236  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34237  * <br><br>
34238  * Usage:
34239  * <pre><code>
34240 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34241                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34242 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34243 split.minSize = 100;
34244 split.maxSize = 600;
34245 split.animate = true;
34246 split.on('moved', splitterMoved);
34247 </code></pre>
34248  * @constructor
34249  * Create a new SplitBar
34250  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34251  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34252  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34253  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34254                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34255                         position of the SplitBar).
34256  */
34257 Roo.bootstrap.SplitBar = function(cfg){
34258     
34259     /** @private */
34260     
34261     //{
34262     //  dragElement : elm
34263     //  resizingElement: el,
34264         // optional..
34265     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34266     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34267         // existingProxy ???
34268     //}
34269     
34270     this.el = Roo.get(cfg.dragElement, true);
34271     this.el.dom.unselectable = "on";
34272     /** @private */
34273     this.resizingEl = Roo.get(cfg.resizingElement, true);
34274
34275     /**
34276      * @private
34277      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34278      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34279      * @type Number
34280      */
34281     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34282     
34283     /**
34284      * The minimum size of the resizing element. (Defaults to 0)
34285      * @type Number
34286      */
34287     this.minSize = 0;
34288     
34289     /**
34290      * The maximum size of the resizing element. (Defaults to 2000)
34291      * @type Number
34292      */
34293     this.maxSize = 2000;
34294     
34295     /**
34296      * Whether to animate the transition to the new size
34297      * @type Boolean
34298      */
34299     this.animate = false;
34300     
34301     /**
34302      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34303      * @type Boolean
34304      */
34305     this.useShim = false;
34306     
34307     /** @private */
34308     this.shim = null;
34309     
34310     if(!cfg.existingProxy){
34311         /** @private */
34312         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34313     }else{
34314         this.proxy = Roo.get(cfg.existingProxy).dom;
34315     }
34316     /** @private */
34317     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34318     
34319     /** @private */
34320     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34321     
34322     /** @private */
34323     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34324     
34325     /** @private */
34326     this.dragSpecs = {};
34327     
34328     /**
34329      * @private The adapter to use to positon and resize elements
34330      */
34331     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34332     this.adapter.init(this);
34333     
34334     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34335         /** @private */
34336         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34337         this.el.addClass("roo-splitbar-h");
34338     }else{
34339         /** @private */
34340         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34341         this.el.addClass("roo-splitbar-v");
34342     }
34343     
34344     this.addEvents({
34345         /**
34346          * @event resize
34347          * Fires when the splitter is moved (alias for {@link #event-moved})
34348          * @param {Roo.bootstrap.SplitBar} this
34349          * @param {Number} newSize the new width or height
34350          */
34351         "resize" : true,
34352         /**
34353          * @event moved
34354          * Fires when the splitter is moved
34355          * @param {Roo.bootstrap.SplitBar} this
34356          * @param {Number} newSize the new width or height
34357          */
34358         "moved" : true,
34359         /**
34360          * @event beforeresize
34361          * Fires before the splitter is dragged
34362          * @param {Roo.bootstrap.SplitBar} this
34363          */
34364         "beforeresize" : true,
34365
34366         "beforeapply" : true
34367     });
34368
34369     Roo.util.Observable.call(this);
34370 };
34371
34372 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34373     onStartProxyDrag : function(x, y){
34374         this.fireEvent("beforeresize", this);
34375         if(!this.overlay){
34376             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34377             o.unselectable();
34378             o.enableDisplayMode("block");
34379             // all splitbars share the same overlay
34380             Roo.bootstrap.SplitBar.prototype.overlay = o;
34381         }
34382         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34383         this.overlay.show();
34384         Roo.get(this.proxy).setDisplayed("block");
34385         var size = this.adapter.getElementSize(this);
34386         this.activeMinSize = this.getMinimumSize();;
34387         this.activeMaxSize = this.getMaximumSize();;
34388         var c1 = size - this.activeMinSize;
34389         var c2 = Math.max(this.activeMaxSize - size, 0);
34390         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34391             this.dd.resetConstraints();
34392             this.dd.setXConstraint(
34393                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34394                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34395             );
34396             this.dd.setYConstraint(0, 0);
34397         }else{
34398             this.dd.resetConstraints();
34399             this.dd.setXConstraint(0, 0);
34400             this.dd.setYConstraint(
34401                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34402                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34403             );
34404          }
34405         this.dragSpecs.startSize = size;
34406         this.dragSpecs.startPoint = [x, y];
34407         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34408     },
34409     
34410     /** 
34411      * @private Called after the drag operation by the DDProxy
34412      */
34413     onEndProxyDrag : function(e){
34414         Roo.get(this.proxy).setDisplayed(false);
34415         var endPoint = Roo.lib.Event.getXY(e);
34416         if(this.overlay){
34417             this.overlay.hide();
34418         }
34419         var newSize;
34420         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34421             newSize = this.dragSpecs.startSize + 
34422                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34423                     endPoint[0] - this.dragSpecs.startPoint[0] :
34424                     this.dragSpecs.startPoint[0] - endPoint[0]
34425                 );
34426         }else{
34427             newSize = this.dragSpecs.startSize + 
34428                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34429                     endPoint[1] - this.dragSpecs.startPoint[1] :
34430                     this.dragSpecs.startPoint[1] - endPoint[1]
34431                 );
34432         }
34433         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34434         if(newSize != this.dragSpecs.startSize){
34435             if(this.fireEvent('beforeapply', this, newSize) !== false){
34436                 this.adapter.setElementSize(this, newSize);
34437                 this.fireEvent("moved", this, newSize);
34438                 this.fireEvent("resize", this, newSize);
34439             }
34440         }
34441     },
34442     
34443     /**
34444      * Get the adapter this SplitBar uses
34445      * @return The adapter object
34446      */
34447     getAdapter : function(){
34448         return this.adapter;
34449     },
34450     
34451     /**
34452      * Set the adapter this SplitBar uses
34453      * @param {Object} adapter A SplitBar adapter object
34454      */
34455     setAdapter : function(adapter){
34456         this.adapter = adapter;
34457         this.adapter.init(this);
34458     },
34459     
34460     /**
34461      * Gets the minimum size for the resizing element
34462      * @return {Number} The minimum size
34463      */
34464     getMinimumSize : function(){
34465         return this.minSize;
34466     },
34467     
34468     /**
34469      * Sets the minimum size for the resizing element
34470      * @param {Number} minSize The minimum size
34471      */
34472     setMinimumSize : function(minSize){
34473         this.minSize = minSize;
34474     },
34475     
34476     /**
34477      * Gets the maximum size for the resizing element
34478      * @return {Number} The maximum size
34479      */
34480     getMaximumSize : function(){
34481         return this.maxSize;
34482     },
34483     
34484     /**
34485      * Sets the maximum size for the resizing element
34486      * @param {Number} maxSize The maximum size
34487      */
34488     setMaximumSize : function(maxSize){
34489         this.maxSize = maxSize;
34490     },
34491     
34492     /**
34493      * Sets the initialize size for the resizing element
34494      * @param {Number} size The initial size
34495      */
34496     setCurrentSize : function(size){
34497         var oldAnimate = this.animate;
34498         this.animate = false;
34499         this.adapter.setElementSize(this, size);
34500         this.animate = oldAnimate;
34501     },
34502     
34503     /**
34504      * Destroy this splitbar. 
34505      * @param {Boolean} removeEl True to remove the element
34506      */
34507     destroy : function(removeEl){
34508         if(this.shim){
34509             this.shim.remove();
34510         }
34511         this.dd.unreg();
34512         this.proxy.parentNode.removeChild(this.proxy);
34513         if(removeEl){
34514             this.el.remove();
34515         }
34516     }
34517 });
34518
34519 /**
34520  * @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.
34521  */
34522 Roo.bootstrap.SplitBar.createProxy = function(dir){
34523     var proxy = new Roo.Element(document.createElement("div"));
34524     proxy.unselectable();
34525     var cls = 'roo-splitbar-proxy';
34526     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34527     document.body.appendChild(proxy.dom);
34528     return proxy.dom;
34529 };
34530
34531 /** 
34532  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34533  * Default Adapter. It assumes the splitter and resizing element are not positioned
34534  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34535  */
34536 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34537 };
34538
34539 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34540     // do nothing for now
34541     init : function(s){
34542     
34543     },
34544     /**
34545      * Called before drag operations to get the current size of the resizing element. 
34546      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34547      */
34548      getElementSize : function(s){
34549         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34550             return s.resizingEl.getWidth();
34551         }else{
34552             return s.resizingEl.getHeight();
34553         }
34554     },
34555     
34556     /**
34557      * Called after drag operations to set the size of the resizing element.
34558      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34559      * @param {Number} newSize The new size to set
34560      * @param {Function} onComplete A function to be invoked when resizing is complete
34561      */
34562     setElementSize : function(s, newSize, onComplete){
34563         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34564             if(!s.animate){
34565                 s.resizingEl.setWidth(newSize);
34566                 if(onComplete){
34567                     onComplete(s, newSize);
34568                 }
34569             }else{
34570                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34571             }
34572         }else{
34573             
34574             if(!s.animate){
34575                 s.resizingEl.setHeight(newSize);
34576                 if(onComplete){
34577                     onComplete(s, newSize);
34578                 }
34579             }else{
34580                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34581             }
34582         }
34583     }
34584 };
34585
34586 /** 
34587  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34588  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34589  * Adapter that  moves the splitter element to align with the resized sizing element. 
34590  * Used with an absolute positioned SplitBar.
34591  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34592  * document.body, make sure you assign an id to the body element.
34593  */
34594 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34595     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34596     this.container = Roo.get(container);
34597 };
34598
34599 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34600     init : function(s){
34601         this.basic.init(s);
34602     },
34603     
34604     getElementSize : function(s){
34605         return this.basic.getElementSize(s);
34606     },
34607     
34608     setElementSize : function(s, newSize, onComplete){
34609         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34610     },
34611     
34612     moveSplitter : function(s){
34613         var yes = Roo.bootstrap.SplitBar;
34614         switch(s.placement){
34615             case yes.LEFT:
34616                 s.el.setX(s.resizingEl.getRight());
34617                 break;
34618             case yes.RIGHT:
34619                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34620                 break;
34621             case yes.TOP:
34622                 s.el.setY(s.resizingEl.getBottom());
34623                 break;
34624             case yes.BOTTOM:
34625                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34626                 break;
34627         }
34628     }
34629 };
34630
34631 /**
34632  * Orientation constant - Create a vertical SplitBar
34633  * @static
34634  * @type Number
34635  */
34636 Roo.bootstrap.SplitBar.VERTICAL = 1;
34637
34638 /**
34639  * Orientation constant - Create a horizontal SplitBar
34640  * @static
34641  * @type Number
34642  */
34643 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34644
34645 /**
34646  * Placement constant - The resizing element is to the left of the splitter element
34647  * @static
34648  * @type Number
34649  */
34650 Roo.bootstrap.SplitBar.LEFT = 1;
34651
34652 /**
34653  * Placement constant - The resizing element is to the right of the splitter element
34654  * @static
34655  * @type Number
34656  */
34657 Roo.bootstrap.SplitBar.RIGHT = 2;
34658
34659 /**
34660  * Placement constant - The resizing element is positioned above the splitter element
34661  * @static
34662  * @type Number
34663  */
34664 Roo.bootstrap.SplitBar.TOP = 3;
34665
34666 /**
34667  * Placement constant - The resizing element is positioned under splitter element
34668  * @static
34669  * @type Number
34670  */
34671 Roo.bootstrap.SplitBar.BOTTOM = 4;
34672 Roo.namespace("Roo.bootstrap.layout");/*
34673  * Based on:
34674  * Ext JS Library 1.1.1
34675  * Copyright(c) 2006-2007, Ext JS, LLC.
34676  *
34677  * Originally Released Under LGPL - original licence link has changed is not relivant.
34678  *
34679  * Fork - LGPL
34680  * <script type="text/javascript">
34681  */
34682
34683 /**
34684  * @class Roo.bootstrap.layout.Manager
34685  * @extends Roo.bootstrap.Component
34686  * Base class for layout managers.
34687  */
34688 Roo.bootstrap.layout.Manager = function(config)
34689 {
34690     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34691
34692
34693
34694
34695
34696     /** false to disable window resize monitoring @type Boolean */
34697     this.monitorWindowResize = true;
34698     this.regions = {};
34699     this.addEvents({
34700         /**
34701          * @event layout
34702          * Fires when a layout is performed.
34703          * @param {Roo.LayoutManager} this
34704          */
34705         "layout" : true,
34706         /**
34707          * @event regionresized
34708          * Fires when the user resizes a region.
34709          * @param {Roo.LayoutRegion} region The resized region
34710          * @param {Number} newSize The new size (width for east/west, height for north/south)
34711          */
34712         "regionresized" : true,
34713         /**
34714          * @event regioncollapsed
34715          * Fires when a region is collapsed.
34716          * @param {Roo.LayoutRegion} region The collapsed region
34717          */
34718         "regioncollapsed" : true,
34719         /**
34720          * @event regionexpanded
34721          * Fires when a region is expanded.
34722          * @param {Roo.LayoutRegion} region The expanded region
34723          */
34724         "regionexpanded" : true
34725     });
34726     this.updating = false;
34727
34728     if (config.el) {
34729         this.el = Roo.get(config.el);
34730         this.initEvents();
34731     }
34732
34733 };
34734
34735 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34736
34737
34738     regions : null,
34739
34740     monitorWindowResize : true,
34741
34742
34743     updating : false,
34744
34745
34746     onRender : function(ct, position)
34747     {
34748         if(!this.el){
34749             this.el = Roo.get(ct);
34750             this.initEvents();
34751         }
34752         //this.fireEvent('render',this);
34753     },
34754
34755
34756     initEvents: function()
34757     {
34758
34759
34760         // ie scrollbar fix
34761         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34762             document.body.scroll = "no";
34763         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34764             this.el.position('relative');
34765         }
34766         this.id = this.el.id;
34767         this.el.addClass("roo-layout-container");
34768         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34769         if(this.el.dom != document.body ) {
34770             this.el.on('resize', this.layout,this);
34771             this.el.on('show', this.layout,this);
34772         }
34773
34774     },
34775
34776     /**
34777      * Returns true if this layout is currently being updated
34778      * @return {Boolean}
34779      */
34780     isUpdating : function(){
34781         return this.updating;
34782     },
34783
34784     /**
34785      * Suspend the LayoutManager from doing auto-layouts while
34786      * making multiple add or remove calls
34787      */
34788     beginUpdate : function(){
34789         this.updating = true;
34790     },
34791
34792     /**
34793      * Restore auto-layouts and optionally disable the manager from performing a layout
34794      * @param {Boolean} noLayout true to disable a layout update
34795      */
34796     endUpdate : function(noLayout){
34797         this.updating = false;
34798         if(!noLayout){
34799             this.layout();
34800         }
34801     },
34802
34803     layout: function(){
34804         // abstract...
34805     },
34806
34807     onRegionResized : function(region, newSize){
34808         this.fireEvent("regionresized", region, newSize);
34809         this.layout();
34810     },
34811
34812     onRegionCollapsed : function(region){
34813         this.fireEvent("regioncollapsed", region);
34814     },
34815
34816     onRegionExpanded : function(region){
34817         this.fireEvent("regionexpanded", region);
34818     },
34819
34820     /**
34821      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34822      * performs box-model adjustments.
34823      * @return {Object} The size as an object {width: (the width), height: (the height)}
34824      */
34825     getViewSize : function()
34826     {
34827         var size;
34828         if(this.el.dom != document.body){
34829             size = this.el.getSize();
34830         }else{
34831             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34832         }
34833         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34834         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34835         return size;
34836     },
34837
34838     /**
34839      * Returns the Element this layout is bound to.
34840      * @return {Roo.Element}
34841      */
34842     getEl : function(){
34843         return this.el;
34844     },
34845
34846     /**
34847      * Returns the specified region.
34848      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34849      * @return {Roo.LayoutRegion}
34850      */
34851     getRegion : function(target){
34852         return this.regions[target.toLowerCase()];
34853     },
34854
34855     onWindowResize : function(){
34856         if(this.monitorWindowResize){
34857             this.layout();
34858         }
34859     }
34860 });
34861 /*
34862  * Based on:
34863  * Ext JS Library 1.1.1
34864  * Copyright(c) 2006-2007, Ext JS, LLC.
34865  *
34866  * Originally Released Under LGPL - original licence link has changed is not relivant.
34867  *
34868  * Fork - LGPL
34869  * <script type="text/javascript">
34870  */
34871 /**
34872  * @class Roo.bootstrap.layout.Border
34873  * @extends Roo.bootstrap.layout.Manager
34874  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34875  * please see: examples/bootstrap/nested.html<br><br>
34876  
34877 <b>The container the layout is rendered into can be either the body element or any other element.
34878 If it is not the body element, the container needs to either be an absolute positioned element,
34879 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34880 the container size if it is not the body element.</b>
34881
34882 * @constructor
34883 * Create a new Border
34884 * @param {Object} config Configuration options
34885  */
34886 Roo.bootstrap.layout.Border = function(config){
34887     config = config || {};
34888     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34889     
34890     
34891     
34892     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34893         if(config[region]){
34894             config[region].region = region;
34895             this.addRegion(config[region]);
34896         }
34897     },this);
34898     
34899 };
34900
34901 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34902
34903 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34904     /**
34905      * Creates and adds a new region if it doesn't already exist.
34906      * @param {String} target The target region key (north, south, east, west or center).
34907      * @param {Object} config The regions config object
34908      * @return {BorderLayoutRegion} The new region
34909      */
34910     addRegion : function(config)
34911     {
34912         if(!this.regions[config.region]){
34913             var r = this.factory(config);
34914             this.bindRegion(r);
34915         }
34916         return this.regions[config.region];
34917     },
34918
34919     // private (kinda)
34920     bindRegion : function(r){
34921         this.regions[r.config.region] = r;
34922         
34923         r.on("visibilitychange",    this.layout, this);
34924         r.on("paneladded",          this.layout, this);
34925         r.on("panelremoved",        this.layout, this);
34926         r.on("invalidated",         this.layout, this);
34927         r.on("resized",             this.onRegionResized, this);
34928         r.on("collapsed",           this.onRegionCollapsed, this);
34929         r.on("expanded",            this.onRegionExpanded, this);
34930     },
34931
34932     /**
34933      * Performs a layout update.
34934      */
34935     layout : function()
34936     {
34937         if(this.updating) {
34938             return;
34939         }
34940         
34941         // render all the rebions if they have not been done alreayd?
34942         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34943             if(this.regions[region] && !this.regions[region].bodyEl){
34944                 this.regions[region].onRender(this.el)
34945             }
34946         },this);
34947         
34948         var size = this.getViewSize();
34949         var w = size.width;
34950         var h = size.height;
34951         var centerW = w;
34952         var centerH = h;
34953         var centerY = 0;
34954         var centerX = 0;
34955         //var x = 0, y = 0;
34956
34957         var rs = this.regions;
34958         var north = rs["north"];
34959         var south = rs["south"]; 
34960         var west = rs["west"];
34961         var east = rs["east"];
34962         var center = rs["center"];
34963         //if(this.hideOnLayout){ // not supported anymore
34964             //c.el.setStyle("display", "none");
34965         //}
34966         if(north && north.isVisible()){
34967             var b = north.getBox();
34968             var m = north.getMargins();
34969             b.width = w - (m.left+m.right);
34970             b.x = m.left;
34971             b.y = m.top;
34972             centerY = b.height + b.y + m.bottom;
34973             centerH -= centerY;
34974             north.updateBox(this.safeBox(b));
34975         }
34976         if(south && south.isVisible()){
34977             var b = south.getBox();
34978             var m = south.getMargins();
34979             b.width = w - (m.left+m.right);
34980             b.x = m.left;
34981             var totalHeight = (b.height + m.top + m.bottom);
34982             b.y = h - totalHeight + m.top;
34983             centerH -= totalHeight;
34984             south.updateBox(this.safeBox(b));
34985         }
34986         if(west && west.isVisible()){
34987             var b = west.getBox();
34988             var m = west.getMargins();
34989             b.height = centerH - (m.top+m.bottom);
34990             b.x = m.left;
34991             b.y = centerY + m.top;
34992             var totalWidth = (b.width + m.left + m.right);
34993             centerX += totalWidth;
34994             centerW -= totalWidth;
34995             west.updateBox(this.safeBox(b));
34996         }
34997         if(east && east.isVisible()){
34998             var b = east.getBox();
34999             var m = east.getMargins();
35000             b.height = centerH - (m.top+m.bottom);
35001             var totalWidth = (b.width + m.left + m.right);
35002             b.x = w - totalWidth + m.left;
35003             b.y = centerY + m.top;
35004             centerW -= totalWidth;
35005             east.updateBox(this.safeBox(b));
35006         }
35007         if(center){
35008             var m = center.getMargins();
35009             var centerBox = {
35010                 x: centerX + m.left,
35011                 y: centerY + m.top,
35012                 width: centerW - (m.left+m.right),
35013                 height: centerH - (m.top+m.bottom)
35014             };
35015             //if(this.hideOnLayout){
35016                 //center.el.setStyle("display", "block");
35017             //}
35018             center.updateBox(this.safeBox(centerBox));
35019         }
35020         this.el.repaint();
35021         this.fireEvent("layout", this);
35022     },
35023
35024     // private
35025     safeBox : function(box){
35026         box.width = Math.max(0, box.width);
35027         box.height = Math.max(0, box.height);
35028         return box;
35029     },
35030
35031     /**
35032      * Adds a ContentPanel (or subclass) to this layout.
35033      * @param {String} target The target region key (north, south, east, west or center).
35034      * @param {Roo.ContentPanel} panel The panel to add
35035      * @return {Roo.ContentPanel} The added panel
35036      */
35037     add : function(target, panel){
35038          
35039         target = target.toLowerCase();
35040         return this.regions[target].add(panel);
35041     },
35042
35043     /**
35044      * Remove a ContentPanel (or subclass) to this layout.
35045      * @param {String} target The target region key (north, south, east, west or center).
35046      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35047      * @return {Roo.ContentPanel} The removed panel
35048      */
35049     remove : function(target, panel){
35050         target = target.toLowerCase();
35051         return this.regions[target].remove(panel);
35052     },
35053
35054     /**
35055      * Searches all regions for a panel with the specified id
35056      * @param {String} panelId
35057      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35058      */
35059     findPanel : function(panelId){
35060         var rs = this.regions;
35061         for(var target in rs){
35062             if(typeof rs[target] != "function"){
35063                 var p = rs[target].getPanel(panelId);
35064                 if(p){
35065                     return p;
35066                 }
35067             }
35068         }
35069         return null;
35070     },
35071
35072     /**
35073      * Searches all regions for a panel with the specified id and activates (shows) it.
35074      * @param {String/ContentPanel} panelId The panels id or the panel itself
35075      * @return {Roo.ContentPanel} The shown panel or null
35076      */
35077     showPanel : function(panelId) {
35078       var rs = this.regions;
35079       for(var target in rs){
35080          var r = rs[target];
35081          if(typeof r != "function"){
35082             if(r.hasPanel(panelId)){
35083                return r.showPanel(panelId);
35084             }
35085          }
35086       }
35087       return null;
35088    },
35089
35090    /**
35091      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35092      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35093      */
35094    /*
35095     restoreState : function(provider){
35096         if(!provider){
35097             provider = Roo.state.Manager;
35098         }
35099         var sm = new Roo.LayoutStateManager();
35100         sm.init(this, provider);
35101     },
35102 */
35103  
35104  
35105     /**
35106      * Adds a xtype elements to the layout.
35107      * <pre><code>
35108
35109 layout.addxtype({
35110        xtype : 'ContentPanel',
35111        region: 'west',
35112        items: [ .... ]
35113    }
35114 );
35115
35116 layout.addxtype({
35117         xtype : 'NestedLayoutPanel',
35118         region: 'west',
35119         layout: {
35120            center: { },
35121            west: { }   
35122         },
35123         items : [ ... list of content panels or nested layout panels.. ]
35124    }
35125 );
35126 </code></pre>
35127      * @param {Object} cfg Xtype definition of item to add.
35128      */
35129     addxtype : function(cfg)
35130     {
35131         // basically accepts a pannel...
35132         // can accept a layout region..!?!?
35133         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35134         
35135         
35136         // theory?  children can only be panels??
35137         
35138         //if (!cfg.xtype.match(/Panel$/)) {
35139         //    return false;
35140         //}
35141         var ret = false;
35142         
35143         if (typeof(cfg.region) == 'undefined') {
35144             Roo.log("Failed to add Panel, region was not set");
35145             Roo.log(cfg);
35146             return false;
35147         }
35148         var region = cfg.region;
35149         delete cfg.region;
35150         
35151           
35152         var xitems = [];
35153         if (cfg.items) {
35154             xitems = cfg.items;
35155             delete cfg.items;
35156         }
35157         var nb = false;
35158         
35159         switch(cfg.xtype) 
35160         {
35161             case 'Content':  // ContentPanel (el, cfg)
35162             case 'Scroll':  // ContentPanel (el, cfg)
35163             case 'View': 
35164                 cfg.autoCreate = true;
35165                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35166                 //} else {
35167                 //    var el = this.el.createChild();
35168                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35169                 //}
35170                 
35171                 this.add(region, ret);
35172                 break;
35173             
35174             /*
35175             case 'TreePanel': // our new panel!
35176                 cfg.el = this.el.createChild();
35177                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35178                 this.add(region, ret);
35179                 break;
35180             */
35181             
35182             case 'Nest': 
35183                 // create a new Layout (which is  a Border Layout...
35184                 
35185                 var clayout = cfg.layout;
35186                 clayout.el  = this.el.createChild();
35187                 clayout.items   = clayout.items  || [];
35188                 
35189                 delete cfg.layout;
35190                 
35191                 // replace this exitems with the clayout ones..
35192                 xitems = clayout.items;
35193                  
35194                 // force background off if it's in center...
35195                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35196                     cfg.background = false;
35197                 }
35198                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35199                 
35200                 
35201                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35202                 //console.log('adding nested layout panel '  + cfg.toSource());
35203                 this.add(region, ret);
35204                 nb = {}; /// find first...
35205                 break;
35206             
35207             case 'Grid':
35208                 
35209                 // needs grid and region
35210                 
35211                 //var el = this.getRegion(region).el.createChild();
35212                 /*
35213                  *var el = this.el.createChild();
35214                 // create the grid first...
35215                 cfg.grid.container = el;
35216                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35217                 */
35218                 
35219                 if (region == 'center' && this.active ) {
35220                     cfg.background = false;
35221                 }
35222                 
35223                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35224                 
35225                 this.add(region, ret);
35226                 /*
35227                 if (cfg.background) {
35228                     // render grid on panel activation (if panel background)
35229                     ret.on('activate', function(gp) {
35230                         if (!gp.grid.rendered) {
35231                     //        gp.grid.render(el);
35232                         }
35233                     });
35234                 } else {
35235                   //  cfg.grid.render(el);
35236                 }
35237                 */
35238                 break;
35239            
35240            
35241             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35242                 // it was the old xcomponent building that caused this before.
35243                 // espeically if border is the top element in the tree.
35244                 ret = this;
35245                 break; 
35246                 
35247                     
35248                 
35249                 
35250                 
35251             default:
35252                 /*
35253                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35254                     
35255                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35256                     this.add(region, ret);
35257                 } else {
35258                 */
35259                     Roo.log(cfg);
35260                     throw "Can not add '" + cfg.xtype + "' to Border";
35261                     return null;
35262              
35263                                 
35264              
35265         }
35266         this.beginUpdate();
35267         // add children..
35268         var region = '';
35269         var abn = {};
35270         Roo.each(xitems, function(i)  {
35271             region = nb && i.region ? i.region : false;
35272             
35273             var add = ret.addxtype(i);
35274            
35275             if (region) {
35276                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35277                 if (!i.background) {
35278                     abn[region] = nb[region] ;
35279                 }
35280             }
35281             
35282         });
35283         this.endUpdate();
35284
35285         // make the last non-background panel active..
35286         //if (nb) { Roo.log(abn); }
35287         if (nb) {
35288             
35289             for(var r in abn) {
35290                 region = this.getRegion(r);
35291                 if (region) {
35292                     // tried using nb[r], but it does not work..
35293                      
35294                     region.showPanel(abn[r]);
35295                    
35296                 }
35297             }
35298         }
35299         return ret;
35300         
35301     },
35302     
35303     
35304 // private
35305     factory : function(cfg)
35306     {
35307         
35308         var validRegions = Roo.bootstrap.layout.Border.regions;
35309
35310         var target = cfg.region;
35311         cfg.mgr = this;
35312         
35313         var r = Roo.bootstrap.layout;
35314         Roo.log(target);
35315         switch(target){
35316             case "north":
35317                 return new r.North(cfg);
35318             case "south":
35319                 return new r.South(cfg);
35320             case "east":
35321                 return new r.East(cfg);
35322             case "west":
35323                 return new r.West(cfg);
35324             case "center":
35325                 return new r.Center(cfg);
35326         }
35327         throw 'Layout region "'+target+'" not supported.';
35328     }
35329     
35330     
35331 });
35332  /*
35333  * Based on:
35334  * Ext JS Library 1.1.1
35335  * Copyright(c) 2006-2007, Ext JS, LLC.
35336  *
35337  * Originally Released Under LGPL - original licence link has changed is not relivant.
35338  *
35339  * Fork - LGPL
35340  * <script type="text/javascript">
35341  */
35342  
35343 /**
35344  * @class Roo.bootstrap.layout.Basic
35345  * @extends Roo.util.Observable
35346  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35347  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35348  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35349  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35350  * @cfg {string}   region  the region that it inhabits..
35351  * @cfg {bool}   skipConfig skip config?
35352  * 
35353
35354  */
35355 Roo.bootstrap.layout.Basic = function(config){
35356     
35357     this.mgr = config.mgr;
35358     
35359     this.position = config.region;
35360     
35361     var skipConfig = config.skipConfig;
35362     
35363     this.events = {
35364         /**
35365          * @scope Roo.BasicLayoutRegion
35366          */
35367         
35368         /**
35369          * @event beforeremove
35370          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35371          * @param {Roo.LayoutRegion} this
35372          * @param {Roo.ContentPanel} panel The panel
35373          * @param {Object} e The cancel event object
35374          */
35375         "beforeremove" : true,
35376         /**
35377          * @event invalidated
35378          * Fires when the layout for this region is changed.
35379          * @param {Roo.LayoutRegion} this
35380          */
35381         "invalidated" : true,
35382         /**
35383          * @event visibilitychange
35384          * Fires when this region is shown or hidden 
35385          * @param {Roo.LayoutRegion} this
35386          * @param {Boolean} visibility true or false
35387          */
35388         "visibilitychange" : true,
35389         /**
35390          * @event paneladded
35391          * Fires when a panel is added. 
35392          * @param {Roo.LayoutRegion} this
35393          * @param {Roo.ContentPanel} panel The panel
35394          */
35395         "paneladded" : true,
35396         /**
35397          * @event panelremoved
35398          * Fires when a panel is removed. 
35399          * @param {Roo.LayoutRegion} this
35400          * @param {Roo.ContentPanel} panel The panel
35401          */
35402         "panelremoved" : true,
35403         /**
35404          * @event beforecollapse
35405          * Fires when this region before collapse.
35406          * @param {Roo.LayoutRegion} this
35407          */
35408         "beforecollapse" : true,
35409         /**
35410          * @event collapsed
35411          * Fires when this region is collapsed.
35412          * @param {Roo.LayoutRegion} this
35413          */
35414         "collapsed" : true,
35415         /**
35416          * @event expanded
35417          * Fires when this region is expanded.
35418          * @param {Roo.LayoutRegion} this
35419          */
35420         "expanded" : true,
35421         /**
35422          * @event slideshow
35423          * Fires when this region is slid into view.
35424          * @param {Roo.LayoutRegion} this
35425          */
35426         "slideshow" : true,
35427         /**
35428          * @event slidehide
35429          * Fires when this region slides out of view. 
35430          * @param {Roo.LayoutRegion} this
35431          */
35432         "slidehide" : true,
35433         /**
35434          * @event panelactivated
35435          * Fires when a panel is activated. 
35436          * @param {Roo.LayoutRegion} this
35437          * @param {Roo.ContentPanel} panel The activated panel
35438          */
35439         "panelactivated" : true,
35440         /**
35441          * @event resized
35442          * Fires when the user resizes this region. 
35443          * @param {Roo.LayoutRegion} this
35444          * @param {Number} newSize The new size (width for east/west, height for north/south)
35445          */
35446         "resized" : true
35447     };
35448     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35449     this.panels = new Roo.util.MixedCollection();
35450     this.panels.getKey = this.getPanelId.createDelegate(this);
35451     this.box = null;
35452     this.activePanel = null;
35453     // ensure listeners are added...
35454     
35455     if (config.listeners || config.events) {
35456         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35457             listeners : config.listeners || {},
35458             events : config.events || {}
35459         });
35460     }
35461     
35462     if(skipConfig !== true){
35463         this.applyConfig(config);
35464     }
35465 };
35466
35467 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35468 {
35469     getPanelId : function(p){
35470         return p.getId();
35471     },
35472     
35473     applyConfig : function(config){
35474         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35475         this.config = config;
35476         
35477     },
35478     
35479     /**
35480      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35481      * the width, for horizontal (north, south) the height.
35482      * @param {Number} newSize The new width or height
35483      */
35484     resizeTo : function(newSize){
35485         var el = this.el ? this.el :
35486                  (this.activePanel ? this.activePanel.getEl() : null);
35487         if(el){
35488             switch(this.position){
35489                 case "east":
35490                 case "west":
35491                     el.setWidth(newSize);
35492                     this.fireEvent("resized", this, newSize);
35493                 break;
35494                 case "north":
35495                 case "south":
35496                     el.setHeight(newSize);
35497                     this.fireEvent("resized", this, newSize);
35498                 break;                
35499             }
35500         }
35501     },
35502     
35503     getBox : function(){
35504         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35505     },
35506     
35507     getMargins : function(){
35508         return this.margins;
35509     },
35510     
35511     updateBox : function(box){
35512         this.box = box;
35513         var el = this.activePanel.getEl();
35514         el.dom.style.left = box.x + "px";
35515         el.dom.style.top = box.y + "px";
35516         this.activePanel.setSize(box.width, box.height);
35517     },
35518     
35519     /**
35520      * Returns the container element for this region.
35521      * @return {Roo.Element}
35522      */
35523     getEl : function(){
35524         return this.activePanel;
35525     },
35526     
35527     /**
35528      * Returns true if this region is currently visible.
35529      * @return {Boolean}
35530      */
35531     isVisible : function(){
35532         return this.activePanel ? true : false;
35533     },
35534     
35535     setActivePanel : function(panel){
35536         panel = this.getPanel(panel);
35537         if(this.activePanel && this.activePanel != panel){
35538             this.activePanel.setActiveState(false);
35539             this.activePanel.getEl().setLeftTop(-10000,-10000);
35540         }
35541         this.activePanel = panel;
35542         panel.setActiveState(true);
35543         if(this.box){
35544             panel.setSize(this.box.width, this.box.height);
35545         }
35546         this.fireEvent("panelactivated", this, panel);
35547         this.fireEvent("invalidated");
35548     },
35549     
35550     /**
35551      * Show the specified panel.
35552      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35553      * @return {Roo.ContentPanel} The shown panel or null
35554      */
35555     showPanel : function(panel){
35556         panel = this.getPanel(panel);
35557         if(panel){
35558             this.setActivePanel(panel);
35559         }
35560         return panel;
35561     },
35562     
35563     /**
35564      * Get the active panel for this region.
35565      * @return {Roo.ContentPanel} The active panel or null
35566      */
35567     getActivePanel : function(){
35568         return this.activePanel;
35569     },
35570     
35571     /**
35572      * Add the passed ContentPanel(s)
35573      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35574      * @return {Roo.ContentPanel} The panel added (if only one was added)
35575      */
35576     add : function(panel){
35577         if(arguments.length > 1){
35578             for(var i = 0, len = arguments.length; i < len; i++) {
35579                 this.add(arguments[i]);
35580             }
35581             return null;
35582         }
35583         if(this.hasPanel(panel)){
35584             this.showPanel(panel);
35585             return panel;
35586         }
35587         var el = panel.getEl();
35588         if(el.dom.parentNode != this.mgr.el.dom){
35589             this.mgr.el.dom.appendChild(el.dom);
35590         }
35591         if(panel.setRegion){
35592             panel.setRegion(this);
35593         }
35594         this.panels.add(panel);
35595         el.setStyle("position", "absolute");
35596         if(!panel.background){
35597             this.setActivePanel(panel);
35598             if(this.config.initialSize && this.panels.getCount()==1){
35599                 this.resizeTo(this.config.initialSize);
35600             }
35601         }
35602         this.fireEvent("paneladded", this, panel);
35603         return panel;
35604     },
35605     
35606     /**
35607      * Returns true if the panel is in this region.
35608      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35609      * @return {Boolean}
35610      */
35611     hasPanel : function(panel){
35612         if(typeof panel == "object"){ // must be panel obj
35613             panel = panel.getId();
35614         }
35615         return this.getPanel(panel) ? true : false;
35616     },
35617     
35618     /**
35619      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35620      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35621      * @param {Boolean} preservePanel Overrides the config preservePanel option
35622      * @return {Roo.ContentPanel} The panel that was removed
35623      */
35624     remove : function(panel, preservePanel){
35625         panel = this.getPanel(panel);
35626         if(!panel){
35627             return null;
35628         }
35629         var e = {};
35630         this.fireEvent("beforeremove", this, panel, e);
35631         if(e.cancel === true){
35632             return null;
35633         }
35634         var panelId = panel.getId();
35635         this.panels.removeKey(panelId);
35636         return panel;
35637     },
35638     
35639     /**
35640      * Returns the panel specified or null if it's not in this region.
35641      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35642      * @return {Roo.ContentPanel}
35643      */
35644     getPanel : function(id){
35645         if(typeof id == "object"){ // must be panel obj
35646             return id;
35647         }
35648         return this.panels.get(id);
35649     },
35650     
35651     /**
35652      * Returns this regions position (north/south/east/west/center).
35653      * @return {String} 
35654      */
35655     getPosition: function(){
35656         return this.position;    
35657     }
35658 });/*
35659  * Based on:
35660  * Ext JS Library 1.1.1
35661  * Copyright(c) 2006-2007, Ext JS, LLC.
35662  *
35663  * Originally Released Under LGPL - original licence link has changed is not relivant.
35664  *
35665  * Fork - LGPL
35666  * <script type="text/javascript">
35667  */
35668  
35669 /**
35670  * @class Roo.bootstrap.layout.Region
35671  * @extends Roo.bootstrap.layout.Basic
35672  * This class represents a region in a layout manager.
35673  
35674  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35675  * @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})
35676  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35677  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35678  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35679  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35680  * @cfg {String}    title           The title for the region (overrides panel titles)
35681  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35682  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35683  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35684  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35685  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35686  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35687  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35688  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35689  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35690  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35691
35692  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35693  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35694  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35695  * @cfg {Number}    width           For East/West panels
35696  * @cfg {Number}    height          For North/South panels
35697  * @cfg {Boolean}   split           To show the splitter
35698  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35699  * 
35700  * @cfg {string}   cls             Extra CSS classes to add to region
35701  * 
35702  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35703  * @cfg {string}   region  the region that it inhabits..
35704  *
35705
35706  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35707  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35708
35709  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35710  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35711  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35712  */
35713 Roo.bootstrap.layout.Region = function(config)
35714 {
35715     this.applyConfig(config);
35716
35717     var mgr = config.mgr;
35718     var pos = config.region;
35719     config.skipConfig = true;
35720     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35721     
35722     if (mgr.el) {
35723         this.onRender(mgr.el);   
35724     }
35725      
35726     this.visible = true;
35727     this.collapsed = false;
35728     this.unrendered_panels = [];
35729 };
35730
35731 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35732
35733     position: '', // set by wrapper (eg. north/south etc..)
35734     unrendered_panels : null,  // unrendered panels.
35735     createBody : function(){
35736         /** This region's body element 
35737         * @type Roo.Element */
35738         this.bodyEl = this.el.createChild({
35739                 tag: "div",
35740                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35741         });
35742     },
35743
35744     onRender: function(ctr, pos)
35745     {
35746         var dh = Roo.DomHelper;
35747         /** This region's container element 
35748         * @type Roo.Element */
35749         this.el = dh.append(ctr.dom, {
35750                 tag: "div",
35751                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35752             }, true);
35753         /** This region's title element 
35754         * @type Roo.Element */
35755     
35756         this.titleEl = dh.append(this.el.dom,
35757             {
35758                     tag: "div",
35759                     unselectable: "on",
35760                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35761                     children:[
35762                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35763                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35764                     ]}, true);
35765         
35766         this.titleEl.enableDisplayMode();
35767         /** This region's title text element 
35768         * @type HTMLElement */
35769         this.titleTextEl = this.titleEl.dom.firstChild;
35770         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35771         /*
35772         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35773         this.closeBtn.enableDisplayMode();
35774         this.closeBtn.on("click", this.closeClicked, this);
35775         this.closeBtn.hide();
35776     */
35777         this.createBody(this.config);
35778         if(this.config.hideWhenEmpty){
35779             this.hide();
35780             this.on("paneladded", this.validateVisibility, this);
35781             this.on("panelremoved", this.validateVisibility, this);
35782         }
35783         if(this.autoScroll){
35784             this.bodyEl.setStyle("overflow", "auto");
35785         }else{
35786             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35787         }
35788         //if(c.titlebar !== false){
35789             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35790                 this.titleEl.hide();
35791             }else{
35792                 this.titleEl.show();
35793                 if(this.config.title){
35794                     this.titleTextEl.innerHTML = this.config.title;
35795                 }
35796             }
35797         //}
35798         if(this.config.collapsed){
35799             this.collapse(true);
35800         }
35801         if(this.config.hidden){
35802             this.hide();
35803         }
35804         
35805         if (this.unrendered_panels && this.unrendered_panels.length) {
35806             for (var i =0;i< this.unrendered_panels.length; i++) {
35807                 this.add(this.unrendered_panels[i]);
35808             }
35809             this.unrendered_panels = null;
35810             
35811         }
35812         
35813     },
35814     
35815     applyConfig : function(c)
35816     {
35817         /*
35818          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35819             var dh = Roo.DomHelper;
35820             if(c.titlebar !== false){
35821                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35822                 this.collapseBtn.on("click", this.collapse, this);
35823                 this.collapseBtn.enableDisplayMode();
35824                 /*
35825                 if(c.showPin === true || this.showPin){
35826                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35827                     this.stickBtn.enableDisplayMode();
35828                     this.stickBtn.on("click", this.expand, this);
35829                     this.stickBtn.hide();
35830                 }
35831                 
35832             }
35833             */
35834             /** This region's collapsed element
35835             * @type Roo.Element */
35836             /*
35837              *
35838             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35839                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35840             ]}, true);
35841             
35842             if(c.floatable !== false){
35843                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35844                this.collapsedEl.on("click", this.collapseClick, this);
35845             }
35846
35847             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35848                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35849                    id: "message", unselectable: "on", style:{"float":"left"}});
35850                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35851              }
35852             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35853             this.expandBtn.on("click", this.expand, this);
35854             
35855         }
35856         
35857         if(this.collapseBtn){
35858             this.collapseBtn.setVisible(c.collapsible == true);
35859         }
35860         
35861         this.cmargins = c.cmargins || this.cmargins ||
35862                          (this.position == "west" || this.position == "east" ?
35863                              {top: 0, left: 2, right:2, bottom: 0} :
35864                              {top: 2, left: 0, right:0, bottom: 2});
35865         */
35866         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35867         
35868         
35869         this.bottomTabs = c.tabPosition != "top";
35870         
35871         this.autoScroll = c.autoScroll || false;
35872         
35873         
35874        
35875         
35876         this.duration = c.duration || .30;
35877         this.slideDuration = c.slideDuration || .45;
35878         this.config = c;
35879        
35880     },
35881     /**
35882      * Returns true if this region is currently visible.
35883      * @return {Boolean}
35884      */
35885     isVisible : function(){
35886         return this.visible;
35887     },
35888
35889     /**
35890      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35891      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35892      */
35893     //setCollapsedTitle : function(title){
35894     //    title = title || "&#160;";
35895      //   if(this.collapsedTitleTextEl){
35896       //      this.collapsedTitleTextEl.innerHTML = title;
35897        // }
35898     //},
35899
35900     getBox : function(){
35901         var b;
35902       //  if(!this.collapsed){
35903             b = this.el.getBox(false, true);
35904        // }else{
35905           //  b = this.collapsedEl.getBox(false, true);
35906         //}
35907         return b;
35908     },
35909
35910     getMargins : function(){
35911         return this.margins;
35912         //return this.collapsed ? this.cmargins : this.margins;
35913     },
35914 /*
35915     highlight : function(){
35916         this.el.addClass("x-layout-panel-dragover");
35917     },
35918
35919     unhighlight : function(){
35920         this.el.removeClass("x-layout-panel-dragover");
35921     },
35922 */
35923     updateBox : function(box)
35924     {
35925         if (!this.bodyEl) {
35926             return; // not rendered yet..
35927         }
35928         
35929         this.box = box;
35930         if(!this.collapsed){
35931             this.el.dom.style.left = box.x + "px";
35932             this.el.dom.style.top = box.y + "px";
35933             this.updateBody(box.width, box.height);
35934         }else{
35935             this.collapsedEl.dom.style.left = box.x + "px";
35936             this.collapsedEl.dom.style.top = box.y + "px";
35937             this.collapsedEl.setSize(box.width, box.height);
35938         }
35939         if(this.tabs){
35940             this.tabs.autoSizeTabs();
35941         }
35942     },
35943
35944     updateBody : function(w, h)
35945     {
35946         if(w !== null){
35947             this.el.setWidth(w);
35948             w -= this.el.getBorderWidth("rl");
35949             if(this.config.adjustments){
35950                 w += this.config.adjustments[0];
35951             }
35952         }
35953         if(h !== null && h > 0){
35954             this.el.setHeight(h);
35955             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35956             h -= this.el.getBorderWidth("tb");
35957             if(this.config.adjustments){
35958                 h += this.config.adjustments[1];
35959             }
35960             this.bodyEl.setHeight(h);
35961             if(this.tabs){
35962                 h = this.tabs.syncHeight(h);
35963             }
35964         }
35965         if(this.panelSize){
35966             w = w !== null ? w : this.panelSize.width;
35967             h = h !== null ? h : this.panelSize.height;
35968         }
35969         if(this.activePanel){
35970             var el = this.activePanel.getEl();
35971             w = w !== null ? w : el.getWidth();
35972             h = h !== null ? h : el.getHeight();
35973             this.panelSize = {width: w, height: h};
35974             this.activePanel.setSize(w, h);
35975         }
35976         if(Roo.isIE && this.tabs){
35977             this.tabs.el.repaint();
35978         }
35979     },
35980
35981     /**
35982      * Returns the container element for this region.
35983      * @return {Roo.Element}
35984      */
35985     getEl : function(){
35986         return this.el;
35987     },
35988
35989     /**
35990      * Hides this region.
35991      */
35992     hide : function(){
35993         //if(!this.collapsed){
35994             this.el.dom.style.left = "-2000px";
35995             this.el.hide();
35996         //}else{
35997          //   this.collapsedEl.dom.style.left = "-2000px";
35998          //   this.collapsedEl.hide();
35999        // }
36000         this.visible = false;
36001         this.fireEvent("visibilitychange", this, false);
36002     },
36003
36004     /**
36005      * Shows this region if it was previously hidden.
36006      */
36007     show : function(){
36008         //if(!this.collapsed){
36009             this.el.show();
36010         //}else{
36011         //    this.collapsedEl.show();
36012        // }
36013         this.visible = true;
36014         this.fireEvent("visibilitychange", this, true);
36015     },
36016 /*
36017     closeClicked : function(){
36018         if(this.activePanel){
36019             this.remove(this.activePanel);
36020         }
36021     },
36022
36023     collapseClick : function(e){
36024         if(this.isSlid){
36025            e.stopPropagation();
36026            this.slideIn();
36027         }else{
36028            e.stopPropagation();
36029            this.slideOut();
36030         }
36031     },
36032 */
36033     /**
36034      * Collapses this region.
36035      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36036      */
36037     /*
36038     collapse : function(skipAnim, skipCheck = false){
36039         if(this.collapsed) {
36040             return;
36041         }
36042         
36043         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36044             
36045             this.collapsed = true;
36046             if(this.split){
36047                 this.split.el.hide();
36048             }
36049             if(this.config.animate && skipAnim !== true){
36050                 this.fireEvent("invalidated", this);
36051                 this.animateCollapse();
36052             }else{
36053                 this.el.setLocation(-20000,-20000);
36054                 this.el.hide();
36055                 this.collapsedEl.show();
36056                 this.fireEvent("collapsed", this);
36057                 this.fireEvent("invalidated", this);
36058             }
36059         }
36060         
36061     },
36062 */
36063     animateCollapse : function(){
36064         // overridden
36065     },
36066
36067     /**
36068      * Expands this region if it was previously collapsed.
36069      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36070      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36071      */
36072     /*
36073     expand : function(e, skipAnim){
36074         if(e) {
36075             e.stopPropagation();
36076         }
36077         if(!this.collapsed || this.el.hasActiveFx()) {
36078             return;
36079         }
36080         if(this.isSlid){
36081             this.afterSlideIn();
36082             skipAnim = true;
36083         }
36084         this.collapsed = false;
36085         if(this.config.animate && skipAnim !== true){
36086             this.animateExpand();
36087         }else{
36088             this.el.show();
36089             if(this.split){
36090                 this.split.el.show();
36091             }
36092             this.collapsedEl.setLocation(-2000,-2000);
36093             this.collapsedEl.hide();
36094             this.fireEvent("invalidated", this);
36095             this.fireEvent("expanded", this);
36096         }
36097     },
36098 */
36099     animateExpand : function(){
36100         // overridden
36101     },
36102
36103     initTabs : function()
36104     {
36105         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36106         
36107         var ts = new Roo.bootstrap.panel.Tabs({
36108                 el: this.bodyEl.dom,
36109                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36110                 disableTooltips: this.config.disableTabTips,
36111                 toolbar : this.config.toolbar
36112             });
36113         
36114         if(this.config.hideTabs){
36115             ts.stripWrap.setDisplayed(false);
36116         }
36117         this.tabs = ts;
36118         ts.resizeTabs = this.config.resizeTabs === true;
36119         ts.minTabWidth = this.config.minTabWidth || 40;
36120         ts.maxTabWidth = this.config.maxTabWidth || 250;
36121         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36122         ts.monitorResize = false;
36123         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36124         ts.bodyEl.addClass('roo-layout-tabs-body');
36125         this.panels.each(this.initPanelAsTab, this);
36126     },
36127
36128     initPanelAsTab : function(panel){
36129         var ti = this.tabs.addTab(
36130             panel.getEl().id,
36131             panel.getTitle(),
36132             null,
36133             this.config.closeOnTab && panel.isClosable(),
36134             panel.tpl
36135         );
36136         if(panel.tabTip !== undefined){
36137             ti.setTooltip(panel.tabTip);
36138         }
36139         ti.on("activate", function(){
36140               this.setActivePanel(panel);
36141         }, this);
36142         
36143         if(this.config.closeOnTab){
36144             ti.on("beforeclose", function(t, e){
36145                 e.cancel = true;
36146                 this.remove(panel);
36147             }, this);
36148         }
36149         
36150         panel.tabItem = ti;
36151         
36152         return ti;
36153     },
36154
36155     updatePanelTitle : function(panel, title)
36156     {
36157         if(this.activePanel == panel){
36158             this.updateTitle(title);
36159         }
36160         if(this.tabs){
36161             var ti = this.tabs.getTab(panel.getEl().id);
36162             ti.setText(title);
36163             if(panel.tabTip !== undefined){
36164                 ti.setTooltip(panel.tabTip);
36165             }
36166         }
36167     },
36168
36169     updateTitle : function(title){
36170         if(this.titleTextEl && !this.config.title){
36171             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36172         }
36173     },
36174
36175     setActivePanel : function(panel)
36176     {
36177         panel = this.getPanel(panel);
36178         if(this.activePanel && this.activePanel != panel){
36179             if(this.activePanel.setActiveState(false) === false){
36180                 return;
36181             }
36182         }
36183         this.activePanel = panel;
36184         panel.setActiveState(true);
36185         if(this.panelSize){
36186             panel.setSize(this.panelSize.width, this.panelSize.height);
36187         }
36188         if(this.closeBtn){
36189             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36190         }
36191         this.updateTitle(panel.getTitle());
36192         if(this.tabs){
36193             this.fireEvent("invalidated", this);
36194         }
36195         this.fireEvent("panelactivated", this, panel);
36196     },
36197
36198     /**
36199      * Shows the specified panel.
36200      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36201      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36202      */
36203     showPanel : function(panel)
36204     {
36205         panel = this.getPanel(panel);
36206         if(panel){
36207             if(this.tabs){
36208                 var tab = this.tabs.getTab(panel.getEl().id);
36209                 if(tab.isHidden()){
36210                     this.tabs.unhideTab(tab.id);
36211                 }
36212                 tab.activate();
36213             }else{
36214                 this.setActivePanel(panel);
36215             }
36216         }
36217         return panel;
36218     },
36219
36220     /**
36221      * Get the active panel for this region.
36222      * @return {Roo.ContentPanel} The active panel or null
36223      */
36224     getActivePanel : function(){
36225         return this.activePanel;
36226     },
36227
36228     validateVisibility : function(){
36229         if(this.panels.getCount() < 1){
36230             this.updateTitle("&#160;");
36231             this.closeBtn.hide();
36232             this.hide();
36233         }else{
36234             if(!this.isVisible()){
36235                 this.show();
36236             }
36237         }
36238     },
36239
36240     /**
36241      * Adds the passed ContentPanel(s) to this region.
36242      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36243      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36244      */
36245     add : function(panel)
36246     {
36247         if(arguments.length > 1){
36248             for(var i = 0, len = arguments.length; i < len; i++) {
36249                 this.add(arguments[i]);
36250             }
36251             return null;
36252         }
36253         
36254         // if we have not been rendered yet, then we can not really do much of this..
36255         if (!this.bodyEl) {
36256             this.unrendered_panels.push(panel);
36257             return panel;
36258         }
36259         
36260         
36261         
36262         
36263         if(this.hasPanel(panel)){
36264             this.showPanel(panel);
36265             return panel;
36266         }
36267         panel.setRegion(this);
36268         this.panels.add(panel);
36269        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36270             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36271             // and hide them... ???
36272             this.bodyEl.dom.appendChild(panel.getEl().dom);
36273             if(panel.background !== true){
36274                 this.setActivePanel(panel);
36275             }
36276             this.fireEvent("paneladded", this, panel);
36277             return panel;
36278         }
36279         */
36280         if(!this.tabs){
36281             this.initTabs();
36282         }else{
36283             this.initPanelAsTab(panel);
36284         }
36285         
36286         
36287         if(panel.background !== true){
36288             this.tabs.activate(panel.getEl().id);
36289         }
36290         this.fireEvent("paneladded", this, panel);
36291         return panel;
36292     },
36293
36294     /**
36295      * Hides the tab for the specified panel.
36296      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36297      */
36298     hidePanel : function(panel){
36299         if(this.tabs && (panel = this.getPanel(panel))){
36300             this.tabs.hideTab(panel.getEl().id);
36301         }
36302     },
36303
36304     /**
36305      * Unhides the tab for a previously hidden panel.
36306      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36307      */
36308     unhidePanel : function(panel){
36309         if(this.tabs && (panel = this.getPanel(panel))){
36310             this.tabs.unhideTab(panel.getEl().id);
36311         }
36312     },
36313
36314     clearPanels : function(){
36315         while(this.panels.getCount() > 0){
36316              this.remove(this.panels.first());
36317         }
36318     },
36319
36320     /**
36321      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36322      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36323      * @param {Boolean} preservePanel Overrides the config preservePanel option
36324      * @return {Roo.ContentPanel} The panel that was removed
36325      */
36326     remove : function(panel, preservePanel)
36327     {
36328         panel = this.getPanel(panel);
36329         if(!panel){
36330             return null;
36331         }
36332         var e = {};
36333         this.fireEvent("beforeremove", this, panel, e);
36334         if(e.cancel === true){
36335             return null;
36336         }
36337         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36338         var panelId = panel.getId();
36339         this.panels.removeKey(panelId);
36340         if(preservePanel){
36341             document.body.appendChild(panel.getEl().dom);
36342         }
36343         if(this.tabs){
36344             this.tabs.removeTab(panel.getEl().id);
36345         }else if (!preservePanel){
36346             this.bodyEl.dom.removeChild(panel.getEl().dom);
36347         }
36348         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36349             var p = this.panels.first();
36350             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36351             tempEl.appendChild(p.getEl().dom);
36352             this.bodyEl.update("");
36353             this.bodyEl.dom.appendChild(p.getEl().dom);
36354             tempEl = null;
36355             this.updateTitle(p.getTitle());
36356             this.tabs = null;
36357             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36358             this.setActivePanel(p);
36359         }
36360         panel.setRegion(null);
36361         if(this.activePanel == panel){
36362             this.activePanel = null;
36363         }
36364         if(this.config.autoDestroy !== false && preservePanel !== true){
36365             try{panel.destroy();}catch(e){}
36366         }
36367         this.fireEvent("panelremoved", this, panel);
36368         return panel;
36369     },
36370
36371     /**
36372      * Returns the TabPanel component used by this region
36373      * @return {Roo.TabPanel}
36374      */
36375     getTabs : function(){
36376         return this.tabs;
36377     },
36378
36379     createTool : function(parentEl, className){
36380         var btn = Roo.DomHelper.append(parentEl, {
36381             tag: "div",
36382             cls: "x-layout-tools-button",
36383             children: [ {
36384                 tag: "div",
36385                 cls: "roo-layout-tools-button-inner " + className,
36386                 html: "&#160;"
36387             }]
36388         }, true);
36389         btn.addClassOnOver("roo-layout-tools-button-over");
36390         return btn;
36391     }
36392 });/*
36393  * Based on:
36394  * Ext JS Library 1.1.1
36395  * Copyright(c) 2006-2007, Ext JS, LLC.
36396  *
36397  * Originally Released Under LGPL - original licence link has changed is not relivant.
36398  *
36399  * Fork - LGPL
36400  * <script type="text/javascript">
36401  */
36402  
36403
36404
36405 /**
36406  * @class Roo.SplitLayoutRegion
36407  * @extends Roo.LayoutRegion
36408  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36409  */
36410 Roo.bootstrap.layout.Split = function(config){
36411     this.cursor = config.cursor;
36412     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36413 };
36414
36415 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36416 {
36417     splitTip : "Drag to resize.",
36418     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36419     useSplitTips : false,
36420
36421     applyConfig : function(config){
36422         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36423     },
36424     
36425     onRender : function(ctr,pos) {
36426         
36427         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36428         if(!this.config.split){
36429             return;
36430         }
36431         if(!this.split){
36432             
36433             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36434                             tag: "div",
36435                             id: this.el.id + "-split",
36436                             cls: "roo-layout-split roo-layout-split-"+this.position,
36437                             html: "&#160;"
36438             });
36439             /** The SplitBar for this region 
36440             * @type Roo.SplitBar */
36441             // does not exist yet...
36442             Roo.log([this.position, this.orientation]);
36443             
36444             this.split = new Roo.bootstrap.SplitBar({
36445                 dragElement : splitEl,
36446                 resizingElement: this.el,
36447                 orientation : this.orientation
36448             });
36449             
36450             this.split.on("moved", this.onSplitMove, this);
36451             this.split.useShim = this.config.useShim === true;
36452             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36453             if(this.useSplitTips){
36454                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36455             }
36456             //if(config.collapsible){
36457             //    this.split.el.on("dblclick", this.collapse,  this);
36458             //}
36459         }
36460         if(typeof this.config.minSize != "undefined"){
36461             this.split.minSize = this.config.minSize;
36462         }
36463         if(typeof this.config.maxSize != "undefined"){
36464             this.split.maxSize = this.config.maxSize;
36465         }
36466         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36467             this.hideSplitter();
36468         }
36469         
36470     },
36471
36472     getHMaxSize : function(){
36473          var cmax = this.config.maxSize || 10000;
36474          var center = this.mgr.getRegion("center");
36475          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36476     },
36477
36478     getVMaxSize : function(){
36479          var cmax = this.config.maxSize || 10000;
36480          var center = this.mgr.getRegion("center");
36481          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36482     },
36483
36484     onSplitMove : function(split, newSize){
36485         this.fireEvent("resized", this, newSize);
36486     },
36487     
36488     /** 
36489      * Returns the {@link Roo.SplitBar} for this region.
36490      * @return {Roo.SplitBar}
36491      */
36492     getSplitBar : function(){
36493         return this.split;
36494     },
36495     
36496     hide : function(){
36497         this.hideSplitter();
36498         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36499     },
36500
36501     hideSplitter : function(){
36502         if(this.split){
36503             this.split.el.setLocation(-2000,-2000);
36504             this.split.el.hide();
36505         }
36506     },
36507
36508     show : function(){
36509         if(this.split){
36510             this.split.el.show();
36511         }
36512         Roo.bootstrap.layout.Split.superclass.show.call(this);
36513     },
36514     
36515     beforeSlide: function(){
36516         if(Roo.isGecko){// firefox overflow auto bug workaround
36517             this.bodyEl.clip();
36518             if(this.tabs) {
36519                 this.tabs.bodyEl.clip();
36520             }
36521             if(this.activePanel){
36522                 this.activePanel.getEl().clip();
36523                 
36524                 if(this.activePanel.beforeSlide){
36525                     this.activePanel.beforeSlide();
36526                 }
36527             }
36528         }
36529     },
36530     
36531     afterSlide : function(){
36532         if(Roo.isGecko){// firefox overflow auto bug workaround
36533             this.bodyEl.unclip();
36534             if(this.tabs) {
36535                 this.tabs.bodyEl.unclip();
36536             }
36537             if(this.activePanel){
36538                 this.activePanel.getEl().unclip();
36539                 if(this.activePanel.afterSlide){
36540                     this.activePanel.afterSlide();
36541                 }
36542             }
36543         }
36544     },
36545
36546     initAutoHide : function(){
36547         if(this.autoHide !== false){
36548             if(!this.autoHideHd){
36549                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36550                 this.autoHideHd = {
36551                     "mouseout": function(e){
36552                         if(!e.within(this.el, true)){
36553                             st.delay(500);
36554                         }
36555                     },
36556                     "mouseover" : function(e){
36557                         st.cancel();
36558                     },
36559                     scope : this
36560                 };
36561             }
36562             this.el.on(this.autoHideHd);
36563         }
36564     },
36565
36566     clearAutoHide : function(){
36567         if(this.autoHide !== false){
36568             this.el.un("mouseout", this.autoHideHd.mouseout);
36569             this.el.un("mouseover", this.autoHideHd.mouseover);
36570         }
36571     },
36572
36573     clearMonitor : function(){
36574         Roo.get(document).un("click", this.slideInIf, this);
36575     },
36576
36577     // these names are backwards but not changed for compat
36578     slideOut : function(){
36579         if(this.isSlid || this.el.hasActiveFx()){
36580             return;
36581         }
36582         this.isSlid = true;
36583         if(this.collapseBtn){
36584             this.collapseBtn.hide();
36585         }
36586         this.closeBtnState = this.closeBtn.getStyle('display');
36587         this.closeBtn.hide();
36588         if(this.stickBtn){
36589             this.stickBtn.show();
36590         }
36591         this.el.show();
36592         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36593         this.beforeSlide();
36594         this.el.setStyle("z-index", 10001);
36595         this.el.slideIn(this.getSlideAnchor(), {
36596             callback: function(){
36597                 this.afterSlide();
36598                 this.initAutoHide();
36599                 Roo.get(document).on("click", this.slideInIf, this);
36600                 this.fireEvent("slideshow", this);
36601             },
36602             scope: this,
36603             block: true
36604         });
36605     },
36606
36607     afterSlideIn : function(){
36608         this.clearAutoHide();
36609         this.isSlid = false;
36610         this.clearMonitor();
36611         this.el.setStyle("z-index", "");
36612         if(this.collapseBtn){
36613             this.collapseBtn.show();
36614         }
36615         this.closeBtn.setStyle('display', this.closeBtnState);
36616         if(this.stickBtn){
36617             this.stickBtn.hide();
36618         }
36619         this.fireEvent("slidehide", this);
36620     },
36621
36622     slideIn : function(cb){
36623         if(!this.isSlid || this.el.hasActiveFx()){
36624             Roo.callback(cb);
36625             return;
36626         }
36627         this.isSlid = false;
36628         this.beforeSlide();
36629         this.el.slideOut(this.getSlideAnchor(), {
36630             callback: function(){
36631                 this.el.setLeftTop(-10000, -10000);
36632                 this.afterSlide();
36633                 this.afterSlideIn();
36634                 Roo.callback(cb);
36635             },
36636             scope: this,
36637             block: true
36638         });
36639     },
36640     
36641     slideInIf : function(e){
36642         if(!e.within(this.el)){
36643             this.slideIn();
36644         }
36645     },
36646
36647     animateCollapse : function(){
36648         this.beforeSlide();
36649         this.el.setStyle("z-index", 20000);
36650         var anchor = this.getSlideAnchor();
36651         this.el.slideOut(anchor, {
36652             callback : function(){
36653                 this.el.setStyle("z-index", "");
36654                 this.collapsedEl.slideIn(anchor, {duration:.3});
36655                 this.afterSlide();
36656                 this.el.setLocation(-10000,-10000);
36657                 this.el.hide();
36658                 this.fireEvent("collapsed", this);
36659             },
36660             scope: this,
36661             block: true
36662         });
36663     },
36664
36665     animateExpand : function(){
36666         this.beforeSlide();
36667         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36668         this.el.setStyle("z-index", 20000);
36669         this.collapsedEl.hide({
36670             duration:.1
36671         });
36672         this.el.slideIn(this.getSlideAnchor(), {
36673             callback : function(){
36674                 this.el.setStyle("z-index", "");
36675                 this.afterSlide();
36676                 if(this.split){
36677                     this.split.el.show();
36678                 }
36679                 this.fireEvent("invalidated", this);
36680                 this.fireEvent("expanded", this);
36681             },
36682             scope: this,
36683             block: true
36684         });
36685     },
36686
36687     anchors : {
36688         "west" : "left",
36689         "east" : "right",
36690         "north" : "top",
36691         "south" : "bottom"
36692     },
36693
36694     sanchors : {
36695         "west" : "l",
36696         "east" : "r",
36697         "north" : "t",
36698         "south" : "b"
36699     },
36700
36701     canchors : {
36702         "west" : "tl-tr",
36703         "east" : "tr-tl",
36704         "north" : "tl-bl",
36705         "south" : "bl-tl"
36706     },
36707
36708     getAnchor : function(){
36709         return this.anchors[this.position];
36710     },
36711
36712     getCollapseAnchor : function(){
36713         return this.canchors[this.position];
36714     },
36715
36716     getSlideAnchor : function(){
36717         return this.sanchors[this.position];
36718     },
36719
36720     getAlignAdj : function(){
36721         var cm = this.cmargins;
36722         switch(this.position){
36723             case "west":
36724                 return [0, 0];
36725             break;
36726             case "east":
36727                 return [0, 0];
36728             break;
36729             case "north":
36730                 return [0, 0];
36731             break;
36732             case "south":
36733                 return [0, 0];
36734             break;
36735         }
36736     },
36737
36738     getExpandAdj : function(){
36739         var c = this.collapsedEl, cm = this.cmargins;
36740         switch(this.position){
36741             case "west":
36742                 return [-(cm.right+c.getWidth()+cm.left), 0];
36743             break;
36744             case "east":
36745                 return [cm.right+c.getWidth()+cm.left, 0];
36746             break;
36747             case "north":
36748                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36749             break;
36750             case "south":
36751                 return [0, cm.top+cm.bottom+c.getHeight()];
36752             break;
36753         }
36754     }
36755 });/*
36756  * Based on:
36757  * Ext JS Library 1.1.1
36758  * Copyright(c) 2006-2007, Ext JS, LLC.
36759  *
36760  * Originally Released Under LGPL - original licence link has changed is not relivant.
36761  *
36762  * Fork - LGPL
36763  * <script type="text/javascript">
36764  */
36765 /*
36766  * These classes are private internal classes
36767  */
36768 Roo.bootstrap.layout.Center = function(config){
36769     config.region = "center";
36770     Roo.bootstrap.layout.Region.call(this, config);
36771     this.visible = true;
36772     this.minWidth = config.minWidth || 20;
36773     this.minHeight = config.minHeight || 20;
36774 };
36775
36776 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36777     hide : function(){
36778         // center panel can't be hidden
36779     },
36780     
36781     show : function(){
36782         // center panel can't be hidden
36783     },
36784     
36785     getMinWidth: function(){
36786         return this.minWidth;
36787     },
36788     
36789     getMinHeight: function(){
36790         return this.minHeight;
36791     }
36792 });
36793
36794
36795
36796
36797  
36798
36799
36800
36801
36802
36803 Roo.bootstrap.layout.North = function(config)
36804 {
36805     config.region = 'north';
36806     config.cursor = 'n-resize';
36807     
36808     Roo.bootstrap.layout.Split.call(this, config);
36809     
36810     
36811     if(this.split){
36812         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36813         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36814         this.split.el.addClass("roo-layout-split-v");
36815     }
36816     var size = config.initialSize || config.height;
36817     if(typeof size != "undefined"){
36818         this.el.setHeight(size);
36819     }
36820 };
36821 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36822 {
36823     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36824     
36825     
36826     
36827     getBox : function(){
36828         if(this.collapsed){
36829             return this.collapsedEl.getBox();
36830         }
36831         var box = this.el.getBox();
36832         if(this.split){
36833             box.height += this.split.el.getHeight();
36834         }
36835         return box;
36836     },
36837     
36838     updateBox : function(box){
36839         if(this.split && !this.collapsed){
36840             box.height -= this.split.el.getHeight();
36841             this.split.el.setLeft(box.x);
36842             this.split.el.setTop(box.y+box.height);
36843             this.split.el.setWidth(box.width);
36844         }
36845         if(this.collapsed){
36846             this.updateBody(box.width, null);
36847         }
36848         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36849     }
36850 });
36851
36852
36853
36854
36855
36856 Roo.bootstrap.layout.South = function(config){
36857     config.region = 'south';
36858     config.cursor = 's-resize';
36859     Roo.bootstrap.layout.Split.call(this, config);
36860     if(this.split){
36861         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36862         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36863         this.split.el.addClass("roo-layout-split-v");
36864     }
36865     var size = config.initialSize || config.height;
36866     if(typeof size != "undefined"){
36867         this.el.setHeight(size);
36868     }
36869 };
36870
36871 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36872     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36873     getBox : function(){
36874         if(this.collapsed){
36875             return this.collapsedEl.getBox();
36876         }
36877         var box = this.el.getBox();
36878         if(this.split){
36879             var sh = this.split.el.getHeight();
36880             box.height += sh;
36881             box.y -= sh;
36882         }
36883         return box;
36884     },
36885     
36886     updateBox : function(box){
36887         if(this.split && !this.collapsed){
36888             var sh = this.split.el.getHeight();
36889             box.height -= sh;
36890             box.y += sh;
36891             this.split.el.setLeft(box.x);
36892             this.split.el.setTop(box.y-sh);
36893             this.split.el.setWidth(box.width);
36894         }
36895         if(this.collapsed){
36896             this.updateBody(box.width, null);
36897         }
36898         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36899     }
36900 });
36901
36902 Roo.bootstrap.layout.East = function(config){
36903     config.region = "east";
36904     config.cursor = "e-resize";
36905     Roo.bootstrap.layout.Split.call(this, config);
36906     if(this.split){
36907         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36908         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36909         this.split.el.addClass("roo-layout-split-h");
36910     }
36911     var size = config.initialSize || config.width;
36912     if(typeof size != "undefined"){
36913         this.el.setWidth(size);
36914     }
36915 };
36916 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36917     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36918     getBox : function(){
36919         if(this.collapsed){
36920             return this.collapsedEl.getBox();
36921         }
36922         var box = this.el.getBox();
36923         if(this.split){
36924             var sw = this.split.el.getWidth();
36925             box.width += sw;
36926             box.x -= sw;
36927         }
36928         return box;
36929     },
36930
36931     updateBox : function(box){
36932         if(this.split && !this.collapsed){
36933             var sw = this.split.el.getWidth();
36934             box.width -= sw;
36935             this.split.el.setLeft(box.x);
36936             this.split.el.setTop(box.y);
36937             this.split.el.setHeight(box.height);
36938             box.x += sw;
36939         }
36940         if(this.collapsed){
36941             this.updateBody(null, box.height);
36942         }
36943         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36944     }
36945 });
36946
36947 Roo.bootstrap.layout.West = function(config){
36948     config.region = "west";
36949     config.cursor = "w-resize";
36950     
36951     Roo.bootstrap.layout.Split.call(this, config);
36952     if(this.split){
36953         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36954         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36955         this.split.el.addClass("roo-layout-split-h");
36956     }
36957     
36958 };
36959 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36960     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36961     
36962     onRender: function(ctr, pos)
36963     {
36964         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36965         var size = this.config.initialSize || this.config.width;
36966         if(typeof size != "undefined"){
36967             this.el.setWidth(size);
36968         }
36969     },
36970     
36971     getBox : function(){
36972         if(this.collapsed){
36973             return this.collapsedEl.getBox();
36974         }
36975         var box = this.el.getBox();
36976         if(this.split){
36977             box.width += this.split.el.getWidth();
36978         }
36979         return box;
36980     },
36981     
36982     updateBox : function(box){
36983         if(this.split && !this.collapsed){
36984             var sw = this.split.el.getWidth();
36985             box.width -= sw;
36986             this.split.el.setLeft(box.x+box.width);
36987             this.split.el.setTop(box.y);
36988             this.split.el.setHeight(box.height);
36989         }
36990         if(this.collapsed){
36991             this.updateBody(null, box.height);
36992         }
36993         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36994     }
36995 });
36996 Roo.namespace("Roo.bootstrap.panel");/*
36997  * Based on:
36998  * Ext JS Library 1.1.1
36999  * Copyright(c) 2006-2007, Ext JS, LLC.
37000  *
37001  * Originally Released Under LGPL - original licence link has changed is not relivant.
37002  *
37003  * Fork - LGPL
37004  * <script type="text/javascript">
37005  */
37006 /**
37007  * @class Roo.ContentPanel
37008  * @extends Roo.util.Observable
37009  * A basic ContentPanel element.
37010  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37011  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37012  * @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
37013  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37014  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37015  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37016  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37017  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37018  * @cfg {String} title          The title for this panel
37019  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37020  * @cfg {String} url            Calls {@link #setUrl} with this value
37021  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37022  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37023  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37024  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37025  * @cfg {Boolean} badges render the badges
37026
37027  * @constructor
37028  * Create a new ContentPanel.
37029  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37030  * @param {String/Object} config A string to set only the title or a config object
37031  * @param {String} content (optional) Set the HTML content for this panel
37032  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37033  */
37034 Roo.bootstrap.panel.Content = function( config){
37035     
37036     this.tpl = config.tpl || false;
37037     
37038     var el = config.el;
37039     var content = config.content;
37040
37041     if(config.autoCreate){ // xtype is available if this is called from factory
37042         el = Roo.id();
37043     }
37044     this.el = Roo.get(el);
37045     if(!this.el && config && config.autoCreate){
37046         if(typeof config.autoCreate == "object"){
37047             if(!config.autoCreate.id){
37048                 config.autoCreate.id = config.id||el;
37049             }
37050             this.el = Roo.DomHelper.append(document.body,
37051                         config.autoCreate, true);
37052         }else{
37053             var elcfg =  {   tag: "div",
37054                             cls: "roo-layout-inactive-content",
37055                             id: config.id||el
37056                             };
37057             if (config.html) {
37058                 elcfg.html = config.html;
37059                 
37060             }
37061                         
37062             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37063         }
37064     } 
37065     this.closable = false;
37066     this.loaded = false;
37067     this.active = false;
37068    
37069       
37070     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37071         
37072         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37073         
37074         this.wrapEl = this.el; //this.el.wrap();
37075         var ti = [];
37076         if (config.toolbar.items) {
37077             ti = config.toolbar.items ;
37078             delete config.toolbar.items ;
37079         }
37080         
37081         var nitems = [];
37082         this.toolbar.render(this.wrapEl, 'before');
37083         for(var i =0;i < ti.length;i++) {
37084           //  Roo.log(['add child', items[i]]);
37085             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37086         }
37087         this.toolbar.items = nitems;
37088         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37089         delete config.toolbar;
37090         
37091     }
37092     /*
37093     // xtype created footer. - not sure if will work as we normally have to render first..
37094     if (this.footer && !this.footer.el && this.footer.xtype) {
37095         if (!this.wrapEl) {
37096             this.wrapEl = this.el.wrap();
37097         }
37098     
37099         this.footer.container = this.wrapEl.createChild();
37100          
37101         this.footer = Roo.factory(this.footer, Roo);
37102         
37103     }
37104     */
37105     
37106      if(typeof config == "string"){
37107         this.title = config;
37108     }else{
37109         Roo.apply(this, config);
37110     }
37111     
37112     if(this.resizeEl){
37113         this.resizeEl = Roo.get(this.resizeEl, true);
37114     }else{
37115         this.resizeEl = this.el;
37116     }
37117     // handle view.xtype
37118     
37119  
37120     
37121     
37122     this.addEvents({
37123         /**
37124          * @event activate
37125          * Fires when this panel is activated. 
37126          * @param {Roo.ContentPanel} this
37127          */
37128         "activate" : true,
37129         /**
37130          * @event deactivate
37131          * Fires when this panel is activated. 
37132          * @param {Roo.ContentPanel} this
37133          */
37134         "deactivate" : true,
37135
37136         /**
37137          * @event resize
37138          * Fires when this panel is resized if fitToFrame is true.
37139          * @param {Roo.ContentPanel} this
37140          * @param {Number} width The width after any component adjustments
37141          * @param {Number} height The height after any component adjustments
37142          */
37143         "resize" : true,
37144         
37145          /**
37146          * @event render
37147          * Fires when this tab is created
37148          * @param {Roo.ContentPanel} this
37149          */
37150         "render" : true
37151         
37152         
37153         
37154     });
37155     
37156
37157     
37158     
37159     if(this.autoScroll){
37160         this.resizeEl.setStyle("overflow", "auto");
37161     } else {
37162         // fix randome scrolling
37163         //this.el.on('scroll', function() {
37164         //    Roo.log('fix random scolling');
37165         //    this.scrollTo('top',0); 
37166         //});
37167     }
37168     content = content || this.content;
37169     if(content){
37170         this.setContent(content);
37171     }
37172     if(config && config.url){
37173         this.setUrl(this.url, this.params, this.loadOnce);
37174     }
37175     
37176     
37177     
37178     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37179     
37180     if (this.view && typeof(this.view.xtype) != 'undefined') {
37181         this.view.el = this.el.appendChild(document.createElement("div"));
37182         this.view = Roo.factory(this.view); 
37183         this.view.render  &&  this.view.render(false, '');  
37184     }
37185     
37186     
37187     this.fireEvent('render', this);
37188 };
37189
37190 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37191     
37192     tabTip : '',
37193     
37194     setRegion : function(region){
37195         this.region = region;
37196         this.setActiveClass(region && !this.background);
37197     },
37198     
37199     
37200     setActiveClass: function(state)
37201     {
37202         if(state){
37203            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37204            this.el.setStyle('position','relative');
37205         }else{
37206            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37207            this.el.setStyle('position', 'absolute');
37208         } 
37209     },
37210     
37211     /**
37212      * Returns the toolbar for this Panel if one was configured. 
37213      * @return {Roo.Toolbar} 
37214      */
37215     getToolbar : function(){
37216         return this.toolbar;
37217     },
37218     
37219     setActiveState : function(active)
37220     {
37221         this.active = active;
37222         this.setActiveClass(active);
37223         if(!active){
37224             if(this.fireEvent("deactivate", this) === false){
37225                 return false;
37226             }
37227             return true;
37228         }
37229         this.fireEvent("activate", this);
37230         return true;
37231     },
37232     /**
37233      * Updates this panel's element
37234      * @param {String} content The new content
37235      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37236     */
37237     setContent : function(content, loadScripts){
37238         this.el.update(content, loadScripts);
37239     },
37240
37241     ignoreResize : function(w, h){
37242         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37243             return true;
37244         }else{
37245             this.lastSize = {width: w, height: h};
37246             return false;
37247         }
37248     },
37249     /**
37250      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37251      * @return {Roo.UpdateManager} The UpdateManager
37252      */
37253     getUpdateManager : function(){
37254         return this.el.getUpdateManager();
37255     },
37256      /**
37257      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37258      * @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:
37259 <pre><code>
37260 panel.load({
37261     url: "your-url.php",
37262     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37263     callback: yourFunction,
37264     scope: yourObject, //(optional scope)
37265     discardUrl: false,
37266     nocache: false,
37267     text: "Loading...",
37268     timeout: 30,
37269     scripts: false
37270 });
37271 </code></pre>
37272      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37273      * 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.
37274      * @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}
37275      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37276      * @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.
37277      * @return {Roo.ContentPanel} this
37278      */
37279     load : function(){
37280         var um = this.el.getUpdateManager();
37281         um.update.apply(um, arguments);
37282         return this;
37283     },
37284
37285
37286     /**
37287      * 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.
37288      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37289      * @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)
37290      * @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)
37291      * @return {Roo.UpdateManager} The UpdateManager
37292      */
37293     setUrl : function(url, params, loadOnce){
37294         if(this.refreshDelegate){
37295             this.removeListener("activate", this.refreshDelegate);
37296         }
37297         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37298         this.on("activate", this.refreshDelegate);
37299         return this.el.getUpdateManager();
37300     },
37301     
37302     _handleRefresh : function(url, params, loadOnce){
37303         if(!loadOnce || !this.loaded){
37304             var updater = this.el.getUpdateManager();
37305             updater.update(url, params, this._setLoaded.createDelegate(this));
37306         }
37307     },
37308     
37309     _setLoaded : function(){
37310         this.loaded = true;
37311     }, 
37312     
37313     /**
37314      * Returns this panel's id
37315      * @return {String} 
37316      */
37317     getId : function(){
37318         return this.el.id;
37319     },
37320     
37321     /** 
37322      * Returns this panel's element - used by regiosn to add.
37323      * @return {Roo.Element} 
37324      */
37325     getEl : function(){
37326         return this.wrapEl || this.el;
37327     },
37328     
37329    
37330     
37331     adjustForComponents : function(width, height)
37332     {
37333         //Roo.log('adjustForComponents ');
37334         if(this.resizeEl != this.el){
37335             width -= this.el.getFrameWidth('lr');
37336             height -= this.el.getFrameWidth('tb');
37337         }
37338         if(this.toolbar){
37339             var te = this.toolbar.getEl();
37340             te.setWidth(width);
37341             height -= te.getHeight();
37342         }
37343         if(this.footer){
37344             var te = this.footer.getEl();
37345             te.setWidth(width);
37346             height -= te.getHeight();
37347         }
37348         
37349         
37350         if(this.adjustments){
37351             width += this.adjustments[0];
37352             height += this.adjustments[1];
37353         }
37354         return {"width": width, "height": height};
37355     },
37356     
37357     setSize : function(width, height){
37358         if(this.fitToFrame && !this.ignoreResize(width, height)){
37359             if(this.fitContainer && this.resizeEl != this.el){
37360                 this.el.setSize(width, height);
37361             }
37362             var size = this.adjustForComponents(width, height);
37363             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37364             this.fireEvent('resize', this, size.width, size.height);
37365         }
37366     },
37367     
37368     /**
37369      * Returns this panel's title
37370      * @return {String} 
37371      */
37372     getTitle : function(){
37373         
37374         if (typeof(this.title) != 'object') {
37375             return this.title;
37376         }
37377         
37378         var t = '';
37379         for (var k in this.title) {
37380             if (!this.title.hasOwnProperty(k)) {
37381                 continue;
37382             }
37383             
37384             if (k.indexOf('-') >= 0) {
37385                 var s = k.split('-');
37386                 for (var i = 0; i<s.length; i++) {
37387                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37388                 }
37389             } else {
37390                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37391             }
37392         }
37393         return t;
37394     },
37395     
37396     /**
37397      * Set this panel's title
37398      * @param {String} title
37399      */
37400     setTitle : function(title){
37401         this.title = title;
37402         if(this.region){
37403             this.region.updatePanelTitle(this, title);
37404         }
37405     },
37406     
37407     /**
37408      * Returns true is this panel was configured to be closable
37409      * @return {Boolean} 
37410      */
37411     isClosable : function(){
37412         return this.closable;
37413     },
37414     
37415     beforeSlide : function(){
37416         this.el.clip();
37417         this.resizeEl.clip();
37418     },
37419     
37420     afterSlide : function(){
37421         this.el.unclip();
37422         this.resizeEl.unclip();
37423     },
37424     
37425     /**
37426      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37427      *   Will fail silently if the {@link #setUrl} method has not been called.
37428      *   This does not activate the panel, just updates its content.
37429      */
37430     refresh : function(){
37431         if(this.refreshDelegate){
37432            this.loaded = false;
37433            this.refreshDelegate();
37434         }
37435     },
37436     
37437     /**
37438      * Destroys this panel
37439      */
37440     destroy : function(){
37441         this.el.removeAllListeners();
37442         var tempEl = document.createElement("span");
37443         tempEl.appendChild(this.el.dom);
37444         tempEl.innerHTML = "";
37445         this.el.remove();
37446         this.el = null;
37447     },
37448     
37449     /**
37450      * form - if the content panel contains a form - this is a reference to it.
37451      * @type {Roo.form.Form}
37452      */
37453     form : false,
37454     /**
37455      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37456      *    This contains a reference to it.
37457      * @type {Roo.View}
37458      */
37459     view : false,
37460     
37461       /**
37462      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37463      * <pre><code>
37464
37465 layout.addxtype({
37466        xtype : 'Form',
37467        items: [ .... ]
37468    }
37469 );
37470
37471 </code></pre>
37472      * @param {Object} cfg Xtype definition of item to add.
37473      */
37474     
37475     
37476     getChildContainer: function () {
37477         return this.getEl();
37478     }
37479     
37480     
37481     /*
37482         var  ret = new Roo.factory(cfg);
37483         return ret;
37484         
37485         
37486         // add form..
37487         if (cfg.xtype.match(/^Form$/)) {
37488             
37489             var el;
37490             //if (this.footer) {
37491             //    el = this.footer.container.insertSibling(false, 'before');
37492             //} else {
37493                 el = this.el.createChild();
37494             //}
37495
37496             this.form = new  Roo.form.Form(cfg);
37497             
37498             
37499             if ( this.form.allItems.length) {
37500                 this.form.render(el.dom);
37501             }
37502             return this.form;
37503         }
37504         // should only have one of theses..
37505         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37506             // views.. should not be just added - used named prop 'view''
37507             
37508             cfg.el = this.el.appendChild(document.createElement("div"));
37509             // factory?
37510             
37511             var ret = new Roo.factory(cfg);
37512              
37513              ret.render && ret.render(false, ''); // render blank..
37514             this.view = ret;
37515             return ret;
37516         }
37517         return false;
37518     }
37519     \*/
37520 });
37521  
37522 /**
37523  * @class Roo.bootstrap.panel.Grid
37524  * @extends Roo.bootstrap.panel.Content
37525  * @constructor
37526  * Create a new GridPanel.
37527  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37528  * @param {Object} config A the config object
37529   
37530  */
37531
37532
37533
37534 Roo.bootstrap.panel.Grid = function(config)
37535 {
37536     
37537       
37538     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37539         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37540
37541     config.el = this.wrapper;
37542     //this.el = this.wrapper;
37543     
37544       if (config.container) {
37545         // ctor'ed from a Border/panel.grid
37546         
37547         
37548         this.wrapper.setStyle("overflow", "hidden");
37549         this.wrapper.addClass('roo-grid-container');
37550
37551     }
37552     
37553     
37554     if(config.toolbar){
37555         var tool_el = this.wrapper.createChild();    
37556         this.toolbar = Roo.factory(config.toolbar);
37557         var ti = [];
37558         if (config.toolbar.items) {
37559             ti = config.toolbar.items ;
37560             delete config.toolbar.items ;
37561         }
37562         
37563         var nitems = [];
37564         this.toolbar.render(tool_el);
37565         for(var i =0;i < ti.length;i++) {
37566           //  Roo.log(['add child', items[i]]);
37567             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37568         }
37569         this.toolbar.items = nitems;
37570         
37571         delete config.toolbar;
37572     }
37573     
37574     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37575     config.grid.scrollBody = true;;
37576     config.grid.monitorWindowResize = false; // turn off autosizing
37577     config.grid.autoHeight = false;
37578     config.grid.autoWidth = false;
37579     
37580     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37581     
37582     if (config.background) {
37583         // render grid on panel activation (if panel background)
37584         this.on('activate', function(gp) {
37585             if (!gp.grid.rendered) {
37586                 gp.grid.render(this.wrapper);
37587                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37588             }
37589         });
37590             
37591     } else {
37592         this.grid.render(this.wrapper);
37593         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37594
37595     }
37596     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37597     // ??? needed ??? config.el = this.wrapper;
37598     
37599     
37600     
37601   
37602     // xtype created footer. - not sure if will work as we normally have to render first..
37603     if (this.footer && !this.footer.el && this.footer.xtype) {
37604         
37605         var ctr = this.grid.getView().getFooterPanel(true);
37606         this.footer.dataSource = this.grid.dataSource;
37607         this.footer = Roo.factory(this.footer, Roo);
37608         this.footer.render(ctr);
37609         
37610     }
37611     
37612     
37613     
37614     
37615      
37616 };
37617
37618 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37619     getId : function(){
37620         return this.grid.id;
37621     },
37622     
37623     /**
37624      * Returns the grid for this panel
37625      * @return {Roo.bootstrap.Table} 
37626      */
37627     getGrid : function(){
37628         return this.grid;    
37629     },
37630     
37631     setSize : function(width, height){
37632         if(!this.ignoreResize(width, height)){
37633             var grid = this.grid;
37634             var size = this.adjustForComponents(width, height);
37635             var gridel = grid.getGridEl();
37636             gridel.setSize(size.width, size.height);
37637             /*
37638             var thd = grid.getGridEl().select('thead',true).first();
37639             var tbd = grid.getGridEl().select('tbody', true).first();
37640             if (tbd) {
37641                 tbd.setSize(width, height - thd.getHeight());
37642             }
37643             */
37644             grid.autoSize();
37645         }
37646     },
37647      
37648     
37649     
37650     beforeSlide : function(){
37651         this.grid.getView().scroller.clip();
37652     },
37653     
37654     afterSlide : function(){
37655         this.grid.getView().scroller.unclip();
37656     },
37657     
37658     destroy : function(){
37659         this.grid.destroy();
37660         delete this.grid;
37661         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37662     }
37663 });
37664
37665 /**
37666  * @class Roo.bootstrap.panel.Nest
37667  * @extends Roo.bootstrap.panel.Content
37668  * @constructor
37669  * Create a new Panel, that can contain a layout.Border.
37670  * 
37671  * 
37672  * @param {Roo.BorderLayout} layout The layout for this panel
37673  * @param {String/Object} config A string to set only the title or a config object
37674  */
37675 Roo.bootstrap.panel.Nest = function(config)
37676 {
37677     // construct with only one argument..
37678     /* FIXME - implement nicer consturctors
37679     if (layout.layout) {
37680         config = layout;
37681         layout = config.layout;
37682         delete config.layout;
37683     }
37684     if (layout.xtype && !layout.getEl) {
37685         // then layout needs constructing..
37686         layout = Roo.factory(layout, Roo);
37687     }
37688     */
37689     
37690     config.el =  config.layout.getEl();
37691     
37692     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37693     
37694     config.layout.monitorWindowResize = false; // turn off autosizing
37695     this.layout = config.layout;
37696     this.layout.getEl().addClass("roo-layout-nested-layout");
37697     
37698     
37699     
37700     
37701 };
37702
37703 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37704
37705     setSize : function(width, height){
37706         if(!this.ignoreResize(width, height)){
37707             var size = this.adjustForComponents(width, height);
37708             var el = this.layout.getEl();
37709             if (size.height < 1) {
37710                 el.setWidth(size.width);   
37711             } else {
37712                 el.setSize(size.width, size.height);
37713             }
37714             var touch = el.dom.offsetWidth;
37715             this.layout.layout();
37716             // ie requires a double layout on the first pass
37717             if(Roo.isIE && !this.initialized){
37718                 this.initialized = true;
37719                 this.layout.layout();
37720             }
37721         }
37722     },
37723     
37724     // activate all subpanels if not currently active..
37725     
37726     setActiveState : function(active){
37727         this.active = active;
37728         this.setActiveClass(active);
37729         
37730         if(!active){
37731             this.fireEvent("deactivate", this);
37732             return;
37733         }
37734         
37735         this.fireEvent("activate", this);
37736         // not sure if this should happen before or after..
37737         if (!this.layout) {
37738             return; // should not happen..
37739         }
37740         var reg = false;
37741         for (var r in this.layout.regions) {
37742             reg = this.layout.getRegion(r);
37743             if (reg.getActivePanel()) {
37744                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37745                 reg.setActivePanel(reg.getActivePanel());
37746                 continue;
37747             }
37748             if (!reg.panels.length) {
37749                 continue;
37750             }
37751             reg.showPanel(reg.getPanel(0));
37752         }
37753         
37754         
37755         
37756         
37757     },
37758     
37759     /**
37760      * Returns the nested BorderLayout for this panel
37761      * @return {Roo.BorderLayout} 
37762      */
37763     getLayout : function(){
37764         return this.layout;
37765     },
37766     
37767      /**
37768      * Adds a xtype elements to the layout of the nested panel
37769      * <pre><code>
37770
37771 panel.addxtype({
37772        xtype : 'ContentPanel',
37773        region: 'west',
37774        items: [ .... ]
37775    }
37776 );
37777
37778 panel.addxtype({
37779         xtype : 'NestedLayoutPanel',
37780         region: 'west',
37781         layout: {
37782            center: { },
37783            west: { }   
37784         },
37785         items : [ ... list of content panels or nested layout panels.. ]
37786    }
37787 );
37788 </code></pre>
37789      * @param {Object} cfg Xtype definition of item to add.
37790      */
37791     addxtype : function(cfg) {
37792         return this.layout.addxtype(cfg);
37793     
37794     }
37795 });        /*
37796  * Based on:
37797  * Ext JS Library 1.1.1
37798  * Copyright(c) 2006-2007, Ext JS, LLC.
37799  *
37800  * Originally Released Under LGPL - original licence link has changed is not relivant.
37801  *
37802  * Fork - LGPL
37803  * <script type="text/javascript">
37804  */
37805 /**
37806  * @class Roo.TabPanel
37807  * @extends Roo.util.Observable
37808  * A lightweight tab container.
37809  * <br><br>
37810  * Usage:
37811  * <pre><code>
37812 // basic tabs 1, built from existing content
37813 var tabs = new Roo.TabPanel("tabs1");
37814 tabs.addTab("script", "View Script");
37815 tabs.addTab("markup", "View Markup");
37816 tabs.activate("script");
37817
37818 // more advanced tabs, built from javascript
37819 var jtabs = new Roo.TabPanel("jtabs");
37820 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37821
37822 // set up the UpdateManager
37823 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37824 var updater = tab2.getUpdateManager();
37825 updater.setDefaultUrl("ajax1.htm");
37826 tab2.on('activate', updater.refresh, updater, true);
37827
37828 // Use setUrl for Ajax loading
37829 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37830 tab3.setUrl("ajax2.htm", null, true);
37831
37832 // Disabled tab
37833 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37834 tab4.disable();
37835
37836 jtabs.activate("jtabs-1");
37837  * </code></pre>
37838  * @constructor
37839  * Create a new TabPanel.
37840  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37841  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37842  */
37843 Roo.bootstrap.panel.Tabs = function(config){
37844     /**
37845     * The container element for this TabPanel.
37846     * @type Roo.Element
37847     */
37848     this.el = Roo.get(config.el);
37849     delete config.el;
37850     if(config){
37851         if(typeof config == "boolean"){
37852             this.tabPosition = config ? "bottom" : "top";
37853         }else{
37854             Roo.apply(this, config);
37855         }
37856     }
37857     
37858     if(this.tabPosition == "bottom"){
37859         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37860         this.el.addClass("roo-tabs-bottom");
37861     }
37862     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37863     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37864     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37865     if(Roo.isIE){
37866         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37867     }
37868     if(this.tabPosition != "bottom"){
37869         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37870          * @type Roo.Element
37871          */
37872         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37873         this.el.addClass("roo-tabs-top");
37874     }
37875     this.items = [];
37876
37877     this.bodyEl.setStyle("position", "relative");
37878
37879     this.active = null;
37880     this.activateDelegate = this.activate.createDelegate(this);
37881
37882     this.addEvents({
37883         /**
37884          * @event tabchange
37885          * Fires when the active tab changes
37886          * @param {Roo.TabPanel} this
37887          * @param {Roo.TabPanelItem} activePanel The new active tab
37888          */
37889         "tabchange": true,
37890         /**
37891          * @event beforetabchange
37892          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37893          * @param {Roo.TabPanel} this
37894          * @param {Object} e Set cancel to true on this object to cancel the tab change
37895          * @param {Roo.TabPanelItem} tab The tab being changed to
37896          */
37897         "beforetabchange" : true
37898     });
37899
37900     Roo.EventManager.onWindowResize(this.onResize, this);
37901     this.cpad = this.el.getPadding("lr");
37902     this.hiddenCount = 0;
37903
37904
37905     // toolbar on the tabbar support...
37906     if (this.toolbar) {
37907         alert("no toolbar support yet");
37908         this.toolbar  = false;
37909         /*
37910         var tcfg = this.toolbar;
37911         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37912         this.toolbar = new Roo.Toolbar(tcfg);
37913         if (Roo.isSafari) {
37914             var tbl = tcfg.container.child('table', true);
37915             tbl.setAttribute('width', '100%');
37916         }
37917         */
37918         
37919     }
37920    
37921
37922
37923     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37924 };
37925
37926 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37927     /*
37928      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37929      */
37930     tabPosition : "top",
37931     /*
37932      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37933      */
37934     currentTabWidth : 0,
37935     /*
37936      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37937      */
37938     minTabWidth : 40,
37939     /*
37940      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37941      */
37942     maxTabWidth : 250,
37943     /*
37944      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37945      */
37946     preferredTabWidth : 175,
37947     /*
37948      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37949      */
37950     resizeTabs : false,
37951     /*
37952      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37953      */
37954     monitorResize : true,
37955     /*
37956      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37957      */
37958     toolbar : false,
37959
37960     /**
37961      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37962      * @param {String} id The id of the div to use <b>or create</b>
37963      * @param {String} text The text for the tab
37964      * @param {String} content (optional) Content to put in the TabPanelItem body
37965      * @param {Boolean} closable (optional) True to create a close icon on the tab
37966      * @return {Roo.TabPanelItem} The created TabPanelItem
37967      */
37968     addTab : function(id, text, content, closable, tpl)
37969     {
37970         var item = new Roo.bootstrap.panel.TabItem({
37971             panel: this,
37972             id : id,
37973             text : text,
37974             closable : closable,
37975             tpl : tpl
37976         });
37977         this.addTabItem(item);
37978         if(content){
37979             item.setContent(content);
37980         }
37981         return item;
37982     },
37983
37984     /**
37985      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37986      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37987      * @return {Roo.TabPanelItem}
37988      */
37989     getTab : function(id){
37990         return this.items[id];
37991     },
37992
37993     /**
37994      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37995      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37996      */
37997     hideTab : function(id){
37998         var t = this.items[id];
37999         if(!t.isHidden()){
38000            t.setHidden(true);
38001            this.hiddenCount++;
38002            this.autoSizeTabs();
38003         }
38004     },
38005
38006     /**
38007      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38008      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38009      */
38010     unhideTab : function(id){
38011         var t = this.items[id];
38012         if(t.isHidden()){
38013            t.setHidden(false);
38014            this.hiddenCount--;
38015            this.autoSizeTabs();
38016         }
38017     },
38018
38019     /**
38020      * Adds an existing {@link Roo.TabPanelItem}.
38021      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38022      */
38023     addTabItem : function(item){
38024         this.items[item.id] = item;
38025         this.items.push(item);
38026       //  if(this.resizeTabs){
38027     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38028   //         this.autoSizeTabs();
38029 //        }else{
38030 //            item.autoSize();
38031        // }
38032     },
38033
38034     /**
38035      * Removes a {@link Roo.TabPanelItem}.
38036      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38037      */
38038     removeTab : function(id){
38039         var items = this.items;
38040         var tab = items[id];
38041         if(!tab) { return; }
38042         var index = items.indexOf(tab);
38043         if(this.active == tab && items.length > 1){
38044             var newTab = this.getNextAvailable(index);
38045             if(newTab) {
38046                 newTab.activate();
38047             }
38048         }
38049         this.stripEl.dom.removeChild(tab.pnode.dom);
38050         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38051             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38052         }
38053         items.splice(index, 1);
38054         delete this.items[tab.id];
38055         tab.fireEvent("close", tab);
38056         tab.purgeListeners();
38057         this.autoSizeTabs();
38058     },
38059
38060     getNextAvailable : function(start){
38061         var items = this.items;
38062         var index = start;
38063         // look for a next tab that will slide over to
38064         // replace the one being removed
38065         while(index < items.length){
38066             var item = items[++index];
38067             if(item && !item.isHidden()){
38068                 return item;
38069             }
38070         }
38071         // if one isn't found select the previous tab (on the left)
38072         index = start;
38073         while(index >= 0){
38074             var item = items[--index];
38075             if(item && !item.isHidden()){
38076                 return item;
38077             }
38078         }
38079         return null;
38080     },
38081
38082     /**
38083      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38084      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38085      */
38086     disableTab : function(id){
38087         var tab = this.items[id];
38088         if(tab && this.active != tab){
38089             tab.disable();
38090         }
38091     },
38092
38093     /**
38094      * Enables a {@link Roo.TabPanelItem} that is disabled.
38095      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38096      */
38097     enableTab : function(id){
38098         var tab = this.items[id];
38099         tab.enable();
38100     },
38101
38102     /**
38103      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38104      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38105      * @return {Roo.TabPanelItem} The TabPanelItem.
38106      */
38107     activate : function(id){
38108         var tab = this.items[id];
38109         if(!tab){
38110             return null;
38111         }
38112         if(tab == this.active || tab.disabled){
38113             return tab;
38114         }
38115         var e = {};
38116         this.fireEvent("beforetabchange", this, e, tab);
38117         if(e.cancel !== true && !tab.disabled){
38118             if(this.active){
38119                 this.active.hide();
38120             }
38121             this.active = this.items[id];
38122             this.active.show();
38123             this.fireEvent("tabchange", this, this.active);
38124         }
38125         return tab;
38126     },
38127
38128     /**
38129      * Gets the active {@link Roo.TabPanelItem}.
38130      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38131      */
38132     getActiveTab : function(){
38133         return this.active;
38134     },
38135
38136     /**
38137      * Updates the tab body element to fit the height of the container element
38138      * for overflow scrolling
38139      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38140      */
38141     syncHeight : function(targetHeight){
38142         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38143         var bm = this.bodyEl.getMargins();
38144         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38145         this.bodyEl.setHeight(newHeight);
38146         return newHeight;
38147     },
38148
38149     onResize : function(){
38150         if(this.monitorResize){
38151             this.autoSizeTabs();
38152         }
38153     },
38154
38155     /**
38156      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38157      */
38158     beginUpdate : function(){
38159         this.updating = true;
38160     },
38161
38162     /**
38163      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38164      */
38165     endUpdate : function(){
38166         this.updating = false;
38167         this.autoSizeTabs();
38168     },
38169
38170     /**
38171      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38172      */
38173     autoSizeTabs : function(){
38174         var count = this.items.length;
38175         var vcount = count - this.hiddenCount;
38176         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38177             return;
38178         }
38179         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38180         var availWidth = Math.floor(w / vcount);
38181         var b = this.stripBody;
38182         if(b.getWidth() > w){
38183             var tabs = this.items;
38184             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38185             if(availWidth < this.minTabWidth){
38186                 /*if(!this.sleft){    // incomplete scrolling code
38187                     this.createScrollButtons();
38188                 }
38189                 this.showScroll();
38190                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38191             }
38192         }else{
38193             if(this.currentTabWidth < this.preferredTabWidth){
38194                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38195             }
38196         }
38197     },
38198
38199     /**
38200      * Returns the number of tabs in this TabPanel.
38201      * @return {Number}
38202      */
38203      getCount : function(){
38204          return this.items.length;
38205      },
38206
38207     /**
38208      * Resizes all the tabs to the passed width
38209      * @param {Number} The new width
38210      */
38211     setTabWidth : function(width){
38212         this.currentTabWidth = width;
38213         for(var i = 0, len = this.items.length; i < len; i++) {
38214                 if(!this.items[i].isHidden()) {
38215                 this.items[i].setWidth(width);
38216             }
38217         }
38218     },
38219
38220     /**
38221      * Destroys this TabPanel
38222      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38223      */
38224     destroy : function(removeEl){
38225         Roo.EventManager.removeResizeListener(this.onResize, this);
38226         for(var i = 0, len = this.items.length; i < len; i++){
38227             this.items[i].purgeListeners();
38228         }
38229         if(removeEl === true){
38230             this.el.update("");
38231             this.el.remove();
38232         }
38233     },
38234     
38235     createStrip : function(container)
38236     {
38237         var strip = document.createElement("nav");
38238         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38239         container.appendChild(strip);
38240         return strip;
38241     },
38242     
38243     createStripList : function(strip)
38244     {
38245         // div wrapper for retard IE
38246         // returns the "tr" element.
38247         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38248         //'<div class="x-tabs-strip-wrap">'+
38249           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38250           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38251         return strip.firstChild; //.firstChild.firstChild.firstChild;
38252     },
38253     createBody : function(container)
38254     {
38255         var body = document.createElement("div");
38256         Roo.id(body, "tab-body");
38257         //Roo.fly(body).addClass("x-tabs-body");
38258         Roo.fly(body).addClass("tab-content");
38259         container.appendChild(body);
38260         return body;
38261     },
38262     createItemBody :function(bodyEl, id){
38263         var body = Roo.getDom(id);
38264         if(!body){
38265             body = document.createElement("div");
38266             body.id = id;
38267         }
38268         //Roo.fly(body).addClass("x-tabs-item-body");
38269         Roo.fly(body).addClass("tab-pane");
38270          bodyEl.insertBefore(body, bodyEl.firstChild);
38271         return body;
38272     },
38273     /** @private */
38274     createStripElements :  function(stripEl, text, closable, tpl)
38275     {
38276         var td = document.createElement("li"); // was td..
38277         
38278         
38279         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38280         
38281         
38282         stripEl.appendChild(td);
38283         /*if(closable){
38284             td.className = "x-tabs-closable";
38285             if(!this.closeTpl){
38286                 this.closeTpl = new Roo.Template(
38287                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38288                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38289                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38290                 );
38291             }
38292             var el = this.closeTpl.overwrite(td, {"text": text});
38293             var close = el.getElementsByTagName("div")[0];
38294             var inner = el.getElementsByTagName("em")[0];
38295             return {"el": el, "close": close, "inner": inner};
38296         } else {
38297         */
38298         // not sure what this is..
38299 //            if(!this.tabTpl){
38300                 //this.tabTpl = new Roo.Template(
38301                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38302                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38303                 //);
38304 //                this.tabTpl = new Roo.Template(
38305 //                   '<a href="#">' +
38306 //                   '<span unselectable="on"' +
38307 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38308 //                            ' >{text}</span></a>'
38309 //                );
38310 //                
38311 //            }
38312
38313
38314             var template = tpl || this.tabTpl || false;
38315             
38316             if(!template){
38317                 
38318                 template = new Roo.Template(
38319                    '<a href="#">' +
38320                    '<span unselectable="on"' +
38321                             (this.disableTooltips ? '' : ' title="{text}"') +
38322                             ' >{text}</span></a>'
38323                 );
38324             }
38325             
38326             switch (typeof(template)) {
38327                 case 'object' :
38328                     break;
38329                 case 'string' :
38330                     template = new Roo.Template(template);
38331                     break;
38332                 default :
38333                     break;
38334             }
38335             
38336             var el = template.overwrite(td, {"text": text});
38337             
38338             var inner = el.getElementsByTagName("span")[0];
38339             
38340             return {"el": el, "inner": inner};
38341             
38342     }
38343         
38344     
38345 });
38346
38347 /**
38348  * @class Roo.TabPanelItem
38349  * @extends Roo.util.Observable
38350  * Represents an individual item (tab plus body) in a TabPanel.
38351  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38352  * @param {String} id The id of this TabPanelItem
38353  * @param {String} text The text for the tab of this TabPanelItem
38354  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38355  */
38356 Roo.bootstrap.panel.TabItem = function(config){
38357     /**
38358      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38359      * @type Roo.TabPanel
38360      */
38361     this.tabPanel = config.panel;
38362     /**
38363      * The id for this TabPanelItem
38364      * @type String
38365      */
38366     this.id = config.id;
38367     /** @private */
38368     this.disabled = false;
38369     /** @private */
38370     this.text = config.text;
38371     /** @private */
38372     this.loaded = false;
38373     this.closable = config.closable;
38374
38375     /**
38376      * The body element for this TabPanelItem.
38377      * @type Roo.Element
38378      */
38379     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38380     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38381     this.bodyEl.setStyle("display", "block");
38382     this.bodyEl.setStyle("zoom", "1");
38383     //this.hideAction();
38384
38385     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38386     /** @private */
38387     this.el = Roo.get(els.el);
38388     this.inner = Roo.get(els.inner, true);
38389     this.textEl = Roo.get(this.el.dom.firstChild, true);
38390     this.pnode = Roo.get(els.el.parentNode, true);
38391 //    this.el.on("mousedown", this.onTabMouseDown, this);
38392     this.el.on("click", this.onTabClick, this);
38393     /** @private */
38394     if(config.closable){
38395         var c = Roo.get(els.close, true);
38396         c.dom.title = this.closeText;
38397         c.addClassOnOver("close-over");
38398         c.on("click", this.closeClick, this);
38399      }
38400
38401     this.addEvents({
38402          /**
38403          * @event activate
38404          * Fires when this tab becomes the active tab.
38405          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38406          * @param {Roo.TabPanelItem} this
38407          */
38408         "activate": true,
38409         /**
38410          * @event beforeclose
38411          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38412          * @param {Roo.TabPanelItem} this
38413          * @param {Object} e Set cancel to true on this object to cancel the close.
38414          */
38415         "beforeclose": true,
38416         /**
38417          * @event close
38418          * Fires when this tab is closed.
38419          * @param {Roo.TabPanelItem} this
38420          */
38421          "close": true,
38422         /**
38423          * @event deactivate
38424          * Fires when this tab is no longer the active tab.
38425          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38426          * @param {Roo.TabPanelItem} this
38427          */
38428          "deactivate" : true
38429     });
38430     this.hidden = false;
38431
38432     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38433 };
38434
38435 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38436            {
38437     purgeListeners : function(){
38438        Roo.util.Observable.prototype.purgeListeners.call(this);
38439        this.el.removeAllListeners();
38440     },
38441     /**
38442      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38443      */
38444     show : function(){
38445         this.pnode.addClass("active");
38446         this.showAction();
38447         if(Roo.isOpera){
38448             this.tabPanel.stripWrap.repaint();
38449         }
38450         this.fireEvent("activate", this.tabPanel, this);
38451     },
38452
38453     /**
38454      * Returns true if this tab is the active tab.
38455      * @return {Boolean}
38456      */
38457     isActive : function(){
38458         return this.tabPanel.getActiveTab() == this;
38459     },
38460
38461     /**
38462      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38463      */
38464     hide : function(){
38465         this.pnode.removeClass("active");
38466         this.hideAction();
38467         this.fireEvent("deactivate", this.tabPanel, this);
38468     },
38469
38470     hideAction : function(){
38471         this.bodyEl.hide();
38472         this.bodyEl.setStyle("position", "absolute");
38473         this.bodyEl.setLeft("-20000px");
38474         this.bodyEl.setTop("-20000px");
38475     },
38476
38477     showAction : function(){
38478         this.bodyEl.setStyle("position", "relative");
38479         this.bodyEl.setTop("");
38480         this.bodyEl.setLeft("");
38481         this.bodyEl.show();
38482     },
38483
38484     /**
38485      * Set the tooltip for the tab.
38486      * @param {String} tooltip The tab's tooltip
38487      */
38488     setTooltip : function(text){
38489         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38490             this.textEl.dom.qtip = text;
38491             this.textEl.dom.removeAttribute('title');
38492         }else{
38493             this.textEl.dom.title = text;
38494         }
38495     },
38496
38497     onTabClick : function(e){
38498         e.preventDefault();
38499         this.tabPanel.activate(this.id);
38500     },
38501
38502     onTabMouseDown : function(e){
38503         e.preventDefault();
38504         this.tabPanel.activate(this.id);
38505     },
38506 /*
38507     getWidth : function(){
38508         return this.inner.getWidth();
38509     },
38510
38511     setWidth : function(width){
38512         var iwidth = width - this.pnode.getPadding("lr");
38513         this.inner.setWidth(iwidth);
38514         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38515         this.pnode.setWidth(width);
38516     },
38517 */
38518     /**
38519      * Show or hide the tab
38520      * @param {Boolean} hidden True to hide or false to show.
38521      */
38522     setHidden : function(hidden){
38523         this.hidden = hidden;
38524         this.pnode.setStyle("display", hidden ? "none" : "");
38525     },
38526
38527     /**
38528      * Returns true if this tab is "hidden"
38529      * @return {Boolean}
38530      */
38531     isHidden : function(){
38532         return this.hidden;
38533     },
38534
38535     /**
38536      * Returns the text for this tab
38537      * @return {String}
38538      */
38539     getText : function(){
38540         return this.text;
38541     },
38542     /*
38543     autoSize : function(){
38544         //this.el.beginMeasure();
38545         this.textEl.setWidth(1);
38546         /*
38547          *  #2804 [new] Tabs in Roojs
38548          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38549          */
38550         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38551         //this.el.endMeasure();
38552     //},
38553
38554     /**
38555      * Sets the text for the tab (Note: this also sets the tooltip text)
38556      * @param {String} text The tab's text and tooltip
38557      */
38558     setText : function(text){
38559         this.text = text;
38560         this.textEl.update(text);
38561         this.setTooltip(text);
38562         //if(!this.tabPanel.resizeTabs){
38563         //    this.autoSize();
38564         //}
38565     },
38566     /**
38567      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38568      */
38569     activate : function(){
38570         this.tabPanel.activate(this.id);
38571     },
38572
38573     /**
38574      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38575      */
38576     disable : function(){
38577         if(this.tabPanel.active != this){
38578             this.disabled = true;
38579             this.pnode.addClass("disabled");
38580         }
38581     },
38582
38583     /**
38584      * Enables this TabPanelItem if it was previously disabled.
38585      */
38586     enable : function(){
38587         this.disabled = false;
38588         this.pnode.removeClass("disabled");
38589     },
38590
38591     /**
38592      * Sets the content for this TabPanelItem.
38593      * @param {String} content The content
38594      * @param {Boolean} loadScripts true to look for and load scripts
38595      */
38596     setContent : function(content, loadScripts){
38597         this.bodyEl.update(content, loadScripts);
38598     },
38599
38600     /**
38601      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38602      * @return {Roo.UpdateManager} The UpdateManager
38603      */
38604     getUpdateManager : function(){
38605         return this.bodyEl.getUpdateManager();
38606     },
38607
38608     /**
38609      * Set a URL to be used to load the content for this TabPanelItem.
38610      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38611      * @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)
38612      * @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)
38613      * @return {Roo.UpdateManager} The UpdateManager
38614      */
38615     setUrl : function(url, params, loadOnce){
38616         if(this.refreshDelegate){
38617             this.un('activate', this.refreshDelegate);
38618         }
38619         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38620         this.on("activate", this.refreshDelegate);
38621         return this.bodyEl.getUpdateManager();
38622     },
38623
38624     /** @private */
38625     _handleRefresh : function(url, params, loadOnce){
38626         if(!loadOnce || !this.loaded){
38627             var updater = this.bodyEl.getUpdateManager();
38628             updater.update(url, params, this._setLoaded.createDelegate(this));
38629         }
38630     },
38631
38632     /**
38633      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38634      *   Will fail silently if the setUrl method has not been called.
38635      *   This does not activate the panel, just updates its content.
38636      */
38637     refresh : function(){
38638         if(this.refreshDelegate){
38639            this.loaded = false;
38640            this.refreshDelegate();
38641         }
38642     },
38643
38644     /** @private */
38645     _setLoaded : function(){
38646         this.loaded = true;
38647     },
38648
38649     /** @private */
38650     closeClick : function(e){
38651         var o = {};
38652         e.stopEvent();
38653         this.fireEvent("beforeclose", this, o);
38654         if(o.cancel !== true){
38655             this.tabPanel.removeTab(this.id);
38656         }
38657     },
38658     /**
38659      * The text displayed in the tooltip for the close icon.
38660      * @type String
38661      */
38662     closeText : "Close this tab"
38663 });
38664 /**
38665 *    This script refer to:
38666 *    Title: International Telephone Input
38667 *    Author: Jack O'Connor
38668 *    Code version:  v12.1.12
38669 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38670 **/
38671
38672 Roo.bootstrap.PhoneInputData = function() {
38673     var d = [
38674       [
38675         "Afghanistan (‫افغانستان‬‎)",
38676         "af",
38677         "93"
38678       ],
38679       [
38680         "Albania (Shqipëri)",
38681         "al",
38682         "355"
38683       ],
38684       [
38685         "Algeria (‫الجزائر‬‎)",
38686         "dz",
38687         "213"
38688       ],
38689       [
38690         "American Samoa",
38691         "as",
38692         "1684"
38693       ],
38694       [
38695         "Andorra",
38696         "ad",
38697         "376"
38698       ],
38699       [
38700         "Angola",
38701         "ao",
38702         "244"
38703       ],
38704       [
38705         "Anguilla",
38706         "ai",
38707         "1264"
38708       ],
38709       [
38710         "Antigua and Barbuda",
38711         "ag",
38712         "1268"
38713       ],
38714       [
38715         "Argentina",
38716         "ar",
38717         "54"
38718       ],
38719       [
38720         "Armenia (Հայաստան)",
38721         "am",
38722         "374"
38723       ],
38724       [
38725         "Aruba",
38726         "aw",
38727         "297"
38728       ],
38729       [
38730         "Australia",
38731         "au",
38732         "61",
38733         0
38734       ],
38735       [
38736         "Austria (Österreich)",
38737         "at",
38738         "43"
38739       ],
38740       [
38741         "Azerbaijan (Azərbaycan)",
38742         "az",
38743         "994"
38744       ],
38745       [
38746         "Bahamas",
38747         "bs",
38748         "1242"
38749       ],
38750       [
38751         "Bahrain (‫البحرين‬‎)",
38752         "bh",
38753         "973"
38754       ],
38755       [
38756         "Bangladesh (বাংলাদেশ)",
38757         "bd",
38758         "880"
38759       ],
38760       [
38761         "Barbados",
38762         "bb",
38763         "1246"
38764       ],
38765       [
38766         "Belarus (Беларусь)",
38767         "by",
38768         "375"
38769       ],
38770       [
38771         "Belgium (België)",
38772         "be",
38773         "32"
38774       ],
38775       [
38776         "Belize",
38777         "bz",
38778         "501"
38779       ],
38780       [
38781         "Benin (Bénin)",
38782         "bj",
38783         "229"
38784       ],
38785       [
38786         "Bermuda",
38787         "bm",
38788         "1441"
38789       ],
38790       [
38791         "Bhutan (འབྲུག)",
38792         "bt",
38793         "975"
38794       ],
38795       [
38796         "Bolivia",
38797         "bo",
38798         "591"
38799       ],
38800       [
38801         "Bosnia and Herzegovina (Босна и Херцеговина)",
38802         "ba",
38803         "387"
38804       ],
38805       [
38806         "Botswana",
38807         "bw",
38808         "267"
38809       ],
38810       [
38811         "Brazil (Brasil)",
38812         "br",
38813         "55"
38814       ],
38815       [
38816         "British Indian Ocean Territory",
38817         "io",
38818         "246"
38819       ],
38820       [
38821         "British Virgin Islands",
38822         "vg",
38823         "1284"
38824       ],
38825       [
38826         "Brunei",
38827         "bn",
38828         "673"
38829       ],
38830       [
38831         "Bulgaria (България)",
38832         "bg",
38833         "359"
38834       ],
38835       [
38836         "Burkina Faso",
38837         "bf",
38838         "226"
38839       ],
38840       [
38841         "Burundi (Uburundi)",
38842         "bi",
38843         "257"
38844       ],
38845       [
38846         "Cambodia (កម្ពុជា)",
38847         "kh",
38848         "855"
38849       ],
38850       [
38851         "Cameroon (Cameroun)",
38852         "cm",
38853         "237"
38854       ],
38855       [
38856         "Canada",
38857         "ca",
38858         "1",
38859         1,
38860         ["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"]
38861       ],
38862       [
38863         "Cape Verde (Kabu Verdi)",
38864         "cv",
38865         "238"
38866       ],
38867       [
38868         "Caribbean Netherlands",
38869         "bq",
38870         "599",
38871         1
38872       ],
38873       [
38874         "Cayman Islands",
38875         "ky",
38876         "1345"
38877       ],
38878       [
38879         "Central African Republic (République centrafricaine)",
38880         "cf",
38881         "236"
38882       ],
38883       [
38884         "Chad (Tchad)",
38885         "td",
38886         "235"
38887       ],
38888       [
38889         "Chile",
38890         "cl",
38891         "56"
38892       ],
38893       [
38894         "China (中国)",
38895         "cn",
38896         "86"
38897       ],
38898       [
38899         "Christmas Island",
38900         "cx",
38901         "61",
38902         2
38903       ],
38904       [
38905         "Cocos (Keeling) Islands",
38906         "cc",
38907         "61",
38908         1
38909       ],
38910       [
38911         "Colombia",
38912         "co",
38913         "57"
38914       ],
38915       [
38916         "Comoros (‫جزر القمر‬‎)",
38917         "km",
38918         "269"
38919       ],
38920       [
38921         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38922         "cd",
38923         "243"
38924       ],
38925       [
38926         "Congo (Republic) (Congo-Brazzaville)",
38927         "cg",
38928         "242"
38929       ],
38930       [
38931         "Cook Islands",
38932         "ck",
38933         "682"
38934       ],
38935       [
38936         "Costa Rica",
38937         "cr",
38938         "506"
38939       ],
38940       [
38941         "Côte d’Ivoire",
38942         "ci",
38943         "225"
38944       ],
38945       [
38946         "Croatia (Hrvatska)",
38947         "hr",
38948         "385"
38949       ],
38950       [
38951         "Cuba",
38952         "cu",
38953         "53"
38954       ],
38955       [
38956         "Curaçao",
38957         "cw",
38958         "599",
38959         0
38960       ],
38961       [
38962         "Cyprus (Κύπρος)",
38963         "cy",
38964         "357"
38965       ],
38966       [
38967         "Czech Republic (Česká republika)",
38968         "cz",
38969         "420"
38970       ],
38971       [
38972         "Denmark (Danmark)",
38973         "dk",
38974         "45"
38975       ],
38976       [
38977         "Djibouti",
38978         "dj",
38979         "253"
38980       ],
38981       [
38982         "Dominica",
38983         "dm",
38984         "1767"
38985       ],
38986       [
38987         "Dominican Republic (República Dominicana)",
38988         "do",
38989         "1",
38990         2,
38991         ["809", "829", "849"]
38992       ],
38993       [
38994         "Ecuador",
38995         "ec",
38996         "593"
38997       ],
38998       [
38999         "Egypt (‫مصر‬‎)",
39000         "eg",
39001         "20"
39002       ],
39003       [
39004         "El Salvador",
39005         "sv",
39006         "503"
39007       ],
39008       [
39009         "Equatorial Guinea (Guinea Ecuatorial)",
39010         "gq",
39011         "240"
39012       ],
39013       [
39014         "Eritrea",
39015         "er",
39016         "291"
39017       ],
39018       [
39019         "Estonia (Eesti)",
39020         "ee",
39021         "372"
39022       ],
39023       [
39024         "Ethiopia",
39025         "et",
39026         "251"
39027       ],
39028       [
39029         "Falkland Islands (Islas Malvinas)",
39030         "fk",
39031         "500"
39032       ],
39033       [
39034         "Faroe Islands (Føroyar)",
39035         "fo",
39036         "298"
39037       ],
39038       [
39039         "Fiji",
39040         "fj",
39041         "679"
39042       ],
39043       [
39044         "Finland (Suomi)",
39045         "fi",
39046         "358",
39047         0
39048       ],
39049       [
39050         "France",
39051         "fr",
39052         "33"
39053       ],
39054       [
39055         "French Guiana (Guyane française)",
39056         "gf",
39057         "594"
39058       ],
39059       [
39060         "French Polynesia (Polynésie française)",
39061         "pf",
39062         "689"
39063       ],
39064       [
39065         "Gabon",
39066         "ga",
39067         "241"
39068       ],
39069       [
39070         "Gambia",
39071         "gm",
39072         "220"
39073       ],
39074       [
39075         "Georgia (საქართველო)",
39076         "ge",
39077         "995"
39078       ],
39079       [
39080         "Germany (Deutschland)",
39081         "de",
39082         "49"
39083       ],
39084       [
39085         "Ghana (Gaana)",
39086         "gh",
39087         "233"
39088       ],
39089       [
39090         "Gibraltar",
39091         "gi",
39092         "350"
39093       ],
39094       [
39095         "Greece (Ελλάδα)",
39096         "gr",
39097         "30"
39098       ],
39099       [
39100         "Greenland (Kalaallit Nunaat)",
39101         "gl",
39102         "299"
39103       ],
39104       [
39105         "Grenada",
39106         "gd",
39107         "1473"
39108       ],
39109       [
39110         "Guadeloupe",
39111         "gp",
39112         "590",
39113         0
39114       ],
39115       [
39116         "Guam",
39117         "gu",
39118         "1671"
39119       ],
39120       [
39121         "Guatemala",
39122         "gt",
39123         "502"
39124       ],
39125       [
39126         "Guernsey",
39127         "gg",
39128         "44",
39129         1
39130       ],
39131       [
39132         "Guinea (Guinée)",
39133         "gn",
39134         "224"
39135       ],
39136       [
39137         "Guinea-Bissau (Guiné Bissau)",
39138         "gw",
39139         "245"
39140       ],
39141       [
39142         "Guyana",
39143         "gy",
39144         "592"
39145       ],
39146       [
39147         "Haiti",
39148         "ht",
39149         "509"
39150       ],
39151       [
39152         "Honduras",
39153         "hn",
39154         "504"
39155       ],
39156       [
39157         "Hong Kong (香港)",
39158         "hk",
39159         "852"
39160       ],
39161       [
39162         "Hungary (Magyarország)",
39163         "hu",
39164         "36"
39165       ],
39166       [
39167         "Iceland (Ísland)",
39168         "is",
39169         "354"
39170       ],
39171       [
39172         "India (भारत)",
39173         "in",
39174         "91"
39175       ],
39176       [
39177         "Indonesia",
39178         "id",
39179         "62"
39180       ],
39181       [
39182         "Iran (‫ایران‬‎)",
39183         "ir",
39184         "98"
39185       ],
39186       [
39187         "Iraq (‫العراق‬‎)",
39188         "iq",
39189         "964"
39190       ],
39191       [
39192         "Ireland",
39193         "ie",
39194         "353"
39195       ],
39196       [
39197         "Isle of Man",
39198         "im",
39199         "44",
39200         2
39201       ],
39202       [
39203         "Israel (‫ישראל‬‎)",
39204         "il",
39205         "972"
39206       ],
39207       [
39208         "Italy (Italia)",
39209         "it",
39210         "39",
39211         0
39212       ],
39213       [
39214         "Jamaica",
39215         "jm",
39216         "1876"
39217       ],
39218       [
39219         "Japan (日本)",
39220         "jp",
39221         "81"
39222       ],
39223       [
39224         "Jersey",
39225         "je",
39226         "44",
39227         3
39228       ],
39229       [
39230         "Jordan (‫الأردن‬‎)",
39231         "jo",
39232         "962"
39233       ],
39234       [
39235         "Kazakhstan (Казахстан)",
39236         "kz",
39237         "7",
39238         1
39239       ],
39240       [
39241         "Kenya",
39242         "ke",
39243         "254"
39244       ],
39245       [
39246         "Kiribati",
39247         "ki",
39248         "686"
39249       ],
39250       [
39251         "Kosovo",
39252         "xk",
39253         "383"
39254       ],
39255       [
39256         "Kuwait (‫الكويت‬‎)",
39257         "kw",
39258         "965"
39259       ],
39260       [
39261         "Kyrgyzstan (Кыргызстан)",
39262         "kg",
39263         "996"
39264       ],
39265       [
39266         "Laos (ລາວ)",
39267         "la",
39268         "856"
39269       ],
39270       [
39271         "Latvia (Latvija)",
39272         "lv",
39273         "371"
39274       ],
39275       [
39276         "Lebanon (‫لبنان‬‎)",
39277         "lb",
39278         "961"
39279       ],
39280       [
39281         "Lesotho",
39282         "ls",
39283         "266"
39284       ],
39285       [
39286         "Liberia",
39287         "lr",
39288         "231"
39289       ],
39290       [
39291         "Libya (‫ليبيا‬‎)",
39292         "ly",
39293         "218"
39294       ],
39295       [
39296         "Liechtenstein",
39297         "li",
39298         "423"
39299       ],
39300       [
39301         "Lithuania (Lietuva)",
39302         "lt",
39303         "370"
39304       ],
39305       [
39306         "Luxembourg",
39307         "lu",
39308         "352"
39309       ],
39310       [
39311         "Macau (澳門)",
39312         "mo",
39313         "853"
39314       ],
39315       [
39316         "Macedonia (FYROM) (Македонија)",
39317         "mk",
39318         "389"
39319       ],
39320       [
39321         "Madagascar (Madagasikara)",
39322         "mg",
39323         "261"
39324       ],
39325       [
39326         "Malawi",
39327         "mw",
39328         "265"
39329       ],
39330       [
39331         "Malaysia",
39332         "my",
39333         "60"
39334       ],
39335       [
39336         "Maldives",
39337         "mv",
39338         "960"
39339       ],
39340       [
39341         "Mali",
39342         "ml",
39343         "223"
39344       ],
39345       [
39346         "Malta",
39347         "mt",
39348         "356"
39349       ],
39350       [
39351         "Marshall Islands",
39352         "mh",
39353         "692"
39354       ],
39355       [
39356         "Martinique",
39357         "mq",
39358         "596"
39359       ],
39360       [
39361         "Mauritania (‫موريتانيا‬‎)",
39362         "mr",
39363         "222"
39364       ],
39365       [
39366         "Mauritius (Moris)",
39367         "mu",
39368         "230"
39369       ],
39370       [
39371         "Mayotte",
39372         "yt",
39373         "262",
39374         1
39375       ],
39376       [
39377         "Mexico (México)",
39378         "mx",
39379         "52"
39380       ],
39381       [
39382         "Micronesia",
39383         "fm",
39384         "691"
39385       ],
39386       [
39387         "Moldova (Republica Moldova)",
39388         "md",
39389         "373"
39390       ],
39391       [
39392         "Monaco",
39393         "mc",
39394         "377"
39395       ],
39396       [
39397         "Mongolia (Монгол)",
39398         "mn",
39399         "976"
39400       ],
39401       [
39402         "Montenegro (Crna Gora)",
39403         "me",
39404         "382"
39405       ],
39406       [
39407         "Montserrat",
39408         "ms",
39409         "1664"
39410       ],
39411       [
39412         "Morocco (‫المغرب‬‎)",
39413         "ma",
39414         "212",
39415         0
39416       ],
39417       [
39418         "Mozambique (Moçambique)",
39419         "mz",
39420         "258"
39421       ],
39422       [
39423         "Myanmar (Burma) (မြန်မာ)",
39424         "mm",
39425         "95"
39426       ],
39427       [
39428         "Namibia (Namibië)",
39429         "na",
39430         "264"
39431       ],
39432       [
39433         "Nauru",
39434         "nr",
39435         "674"
39436       ],
39437       [
39438         "Nepal (नेपाल)",
39439         "np",
39440         "977"
39441       ],
39442       [
39443         "Netherlands (Nederland)",
39444         "nl",
39445         "31"
39446       ],
39447       [
39448         "New Caledonia (Nouvelle-Calédonie)",
39449         "nc",
39450         "687"
39451       ],
39452       [
39453         "New Zealand",
39454         "nz",
39455         "64"
39456       ],
39457       [
39458         "Nicaragua",
39459         "ni",
39460         "505"
39461       ],
39462       [
39463         "Niger (Nijar)",
39464         "ne",
39465         "227"
39466       ],
39467       [
39468         "Nigeria",
39469         "ng",
39470         "234"
39471       ],
39472       [
39473         "Niue",
39474         "nu",
39475         "683"
39476       ],
39477       [
39478         "Norfolk Island",
39479         "nf",
39480         "672"
39481       ],
39482       [
39483         "North Korea (조선 민주주의 인민 공화국)",
39484         "kp",
39485         "850"
39486       ],
39487       [
39488         "Northern Mariana Islands",
39489         "mp",
39490         "1670"
39491       ],
39492       [
39493         "Norway (Norge)",
39494         "no",
39495         "47",
39496         0
39497       ],
39498       [
39499         "Oman (‫عُمان‬‎)",
39500         "om",
39501         "968"
39502       ],
39503       [
39504         "Pakistan (‫پاکستان‬‎)",
39505         "pk",
39506         "92"
39507       ],
39508       [
39509         "Palau",
39510         "pw",
39511         "680"
39512       ],
39513       [
39514         "Palestine (‫فلسطين‬‎)",
39515         "ps",
39516         "970"
39517       ],
39518       [
39519         "Panama (Panamá)",
39520         "pa",
39521         "507"
39522       ],
39523       [
39524         "Papua New Guinea",
39525         "pg",
39526         "675"
39527       ],
39528       [
39529         "Paraguay",
39530         "py",
39531         "595"
39532       ],
39533       [
39534         "Peru (Perú)",
39535         "pe",
39536         "51"
39537       ],
39538       [
39539         "Philippines",
39540         "ph",
39541         "63"
39542       ],
39543       [
39544         "Poland (Polska)",
39545         "pl",
39546         "48"
39547       ],
39548       [
39549         "Portugal",
39550         "pt",
39551         "351"
39552       ],
39553       [
39554         "Puerto Rico",
39555         "pr",
39556         "1",
39557         3,
39558         ["787", "939"]
39559       ],
39560       [
39561         "Qatar (‫قطر‬‎)",
39562         "qa",
39563         "974"
39564       ],
39565       [
39566         "Réunion (La Réunion)",
39567         "re",
39568         "262",
39569         0
39570       ],
39571       [
39572         "Romania (România)",
39573         "ro",
39574         "40"
39575       ],
39576       [
39577         "Russia (Россия)",
39578         "ru",
39579         "7",
39580         0
39581       ],
39582       [
39583         "Rwanda",
39584         "rw",
39585         "250"
39586       ],
39587       [
39588         "Saint Barthélemy",
39589         "bl",
39590         "590",
39591         1
39592       ],
39593       [
39594         "Saint Helena",
39595         "sh",
39596         "290"
39597       ],
39598       [
39599         "Saint Kitts and Nevis",
39600         "kn",
39601         "1869"
39602       ],
39603       [
39604         "Saint Lucia",
39605         "lc",
39606         "1758"
39607       ],
39608       [
39609         "Saint Martin (Saint-Martin (partie française))",
39610         "mf",
39611         "590",
39612         2
39613       ],
39614       [
39615         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39616         "pm",
39617         "508"
39618       ],
39619       [
39620         "Saint Vincent and the Grenadines",
39621         "vc",
39622         "1784"
39623       ],
39624       [
39625         "Samoa",
39626         "ws",
39627         "685"
39628       ],
39629       [
39630         "San Marino",
39631         "sm",
39632         "378"
39633       ],
39634       [
39635         "São Tomé and Príncipe (São Tomé e Príncipe)",
39636         "st",
39637         "239"
39638       ],
39639       [
39640         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39641         "sa",
39642         "966"
39643       ],
39644       [
39645         "Senegal (Sénégal)",
39646         "sn",
39647         "221"
39648       ],
39649       [
39650         "Serbia (Србија)",
39651         "rs",
39652         "381"
39653       ],
39654       [
39655         "Seychelles",
39656         "sc",
39657         "248"
39658       ],
39659       [
39660         "Sierra Leone",
39661         "sl",
39662         "232"
39663       ],
39664       [
39665         "Singapore",
39666         "sg",
39667         "65"
39668       ],
39669       [
39670         "Sint Maarten",
39671         "sx",
39672         "1721"
39673       ],
39674       [
39675         "Slovakia (Slovensko)",
39676         "sk",
39677         "421"
39678       ],
39679       [
39680         "Slovenia (Slovenija)",
39681         "si",
39682         "386"
39683       ],
39684       [
39685         "Solomon Islands",
39686         "sb",
39687         "677"
39688       ],
39689       [
39690         "Somalia (Soomaaliya)",
39691         "so",
39692         "252"
39693       ],
39694       [
39695         "South Africa",
39696         "za",
39697         "27"
39698       ],
39699       [
39700         "South Korea (대한민국)",
39701         "kr",
39702         "82"
39703       ],
39704       [
39705         "South Sudan (‫جنوب السودان‬‎)",
39706         "ss",
39707         "211"
39708       ],
39709       [
39710         "Spain (España)",
39711         "es",
39712         "34"
39713       ],
39714       [
39715         "Sri Lanka (ශ්‍රී ලංකාව)",
39716         "lk",
39717         "94"
39718       ],
39719       [
39720         "Sudan (‫السودان‬‎)",
39721         "sd",
39722         "249"
39723       ],
39724       [
39725         "Suriname",
39726         "sr",
39727         "597"
39728       ],
39729       [
39730         "Svalbard and Jan Mayen",
39731         "sj",
39732         "47",
39733         1
39734       ],
39735       [
39736         "Swaziland",
39737         "sz",
39738         "268"
39739       ],
39740       [
39741         "Sweden (Sverige)",
39742         "se",
39743         "46"
39744       ],
39745       [
39746         "Switzerland (Schweiz)",
39747         "ch",
39748         "41"
39749       ],
39750       [
39751         "Syria (‫سوريا‬‎)",
39752         "sy",
39753         "963"
39754       ],
39755       [
39756         "Taiwan (台灣)",
39757         "tw",
39758         "886"
39759       ],
39760       [
39761         "Tajikistan",
39762         "tj",
39763         "992"
39764       ],
39765       [
39766         "Tanzania",
39767         "tz",
39768         "255"
39769       ],
39770       [
39771         "Thailand (ไทย)",
39772         "th",
39773         "66"
39774       ],
39775       [
39776         "Timor-Leste",
39777         "tl",
39778         "670"
39779       ],
39780       [
39781         "Togo",
39782         "tg",
39783         "228"
39784       ],
39785       [
39786         "Tokelau",
39787         "tk",
39788         "690"
39789       ],
39790       [
39791         "Tonga",
39792         "to",
39793         "676"
39794       ],
39795       [
39796         "Trinidad and Tobago",
39797         "tt",
39798         "1868"
39799       ],
39800       [
39801         "Tunisia (‫تونس‬‎)",
39802         "tn",
39803         "216"
39804       ],
39805       [
39806         "Turkey (Türkiye)",
39807         "tr",
39808         "90"
39809       ],
39810       [
39811         "Turkmenistan",
39812         "tm",
39813         "993"
39814       ],
39815       [
39816         "Turks and Caicos Islands",
39817         "tc",
39818         "1649"
39819       ],
39820       [
39821         "Tuvalu",
39822         "tv",
39823         "688"
39824       ],
39825       [
39826         "U.S. Virgin Islands",
39827         "vi",
39828         "1340"
39829       ],
39830       [
39831         "Uganda",
39832         "ug",
39833         "256"
39834       ],
39835       [
39836         "Ukraine (Україна)",
39837         "ua",
39838         "380"
39839       ],
39840       [
39841         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39842         "ae",
39843         "971"
39844       ],
39845       [
39846         "United Kingdom",
39847         "gb",
39848         "44",
39849         0
39850       ],
39851       [
39852         "United States",
39853         "us",
39854         "1",
39855         0
39856       ],
39857       [
39858         "Uruguay",
39859         "uy",
39860         "598"
39861       ],
39862       [
39863         "Uzbekistan (Oʻzbekiston)",
39864         "uz",
39865         "998"
39866       ],
39867       [
39868         "Vanuatu",
39869         "vu",
39870         "678"
39871       ],
39872       [
39873         "Vatican City (Città del Vaticano)",
39874         "va",
39875         "39",
39876         1
39877       ],
39878       [
39879         "Venezuela",
39880         "ve",
39881         "58"
39882       ],
39883       [
39884         "Vietnam (Việt Nam)",
39885         "vn",
39886         "84"
39887       ],
39888       [
39889         "Wallis and Futuna (Wallis-et-Futuna)",
39890         "wf",
39891         "681"
39892       ],
39893       [
39894         "Western Sahara (‫الصحراء الغربية‬‎)",
39895         "eh",
39896         "212",
39897         1
39898       ],
39899       [
39900         "Yemen (‫اليمن‬‎)",
39901         "ye",
39902         "967"
39903       ],
39904       [
39905         "Zambia",
39906         "zm",
39907         "260"
39908       ],
39909       [
39910         "Zimbabwe",
39911         "zw",
39912         "263"
39913       ],
39914       [
39915         "Åland Islands",
39916         "ax",
39917         "358",
39918         1
39919       ]
39920   ];
39921   
39922   return d;
39923 }/**
39924 *    This script refer to:
39925 *    Title: International Telephone Input
39926 *    Author: Jack O'Connor
39927 *    Code version:  v12.1.12
39928 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39929 **/
39930
39931 /**
39932  * @class Roo.bootstrap.PhoneInput
39933  * @extends Roo.bootstrap.TriggerField
39934  * An input with International dial-code selection
39935  
39936  * @cfg {String} defaultDialCode default '+852'
39937  * @cfg {Array} preferedCountries default []
39938   
39939  * @constructor
39940  * Create a new PhoneInput.
39941  * @param {Object} config Configuration options
39942  */
39943
39944 Roo.bootstrap.PhoneInput = function(config) {
39945     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39946 };
39947
39948 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39949         
39950         listWidth: undefined,
39951         
39952         selectedClass: 'active',
39953         
39954         invalidClass : "has-warning",
39955         
39956         validClass: 'has-success',
39957         
39958         allowed: '0123456789',
39959         
39960         max_length: 15,
39961         
39962         /**
39963          * @cfg {String} defaultDialCode The default dial code when initializing the input
39964          */
39965         defaultDialCode: '+852',
39966         
39967         /**
39968          * @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
39969          */
39970         preferedCountries: false,
39971         
39972         getAutoCreate : function()
39973         {
39974             var data = Roo.bootstrap.PhoneInputData();
39975             var align = this.labelAlign || this.parentLabelAlign();
39976             var id = Roo.id();
39977             
39978             this.allCountries = [];
39979             this.dialCodeMapping = [];
39980             
39981             for (var i = 0; i < data.length; i++) {
39982               var c = data[i];
39983               this.allCountries[i] = {
39984                 name: c[0],
39985                 iso2: c[1],
39986                 dialCode: c[2],
39987                 priority: c[3] || 0,
39988                 areaCodes: c[4] || null
39989               };
39990               this.dialCodeMapping[c[2]] = {
39991                   name: c[0],
39992                   iso2: c[1],
39993                   priority: c[3] || 0,
39994                   areaCodes: c[4] || null
39995               };
39996             }
39997             
39998             var cfg = {
39999                 cls: 'form-group',
40000                 cn: []
40001             };
40002             
40003             var input =  {
40004                 tag: 'input',
40005                 id : id,
40006                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40007                 maxlength: this.max_length,
40008                 cls : 'form-control tel-input',
40009                 autocomplete: 'new-password'
40010             };
40011             
40012             var hiddenInput = {
40013                 tag: 'input',
40014                 type: 'hidden',
40015                 cls: 'hidden-tel-input'
40016             };
40017             
40018             if (this.name) {
40019                 hiddenInput.name = this.name;
40020             }
40021             
40022             if (this.disabled) {
40023                 input.disabled = true;
40024             }
40025             
40026             var flag_container = {
40027                 tag: 'div',
40028                 cls: 'flag-box',
40029                 cn: [
40030                     {
40031                         tag: 'div',
40032                         cls: 'flag'
40033                     },
40034                     {
40035                         tag: 'div',
40036                         cls: 'caret'
40037                     }
40038                 ]
40039             };
40040             
40041             var box = {
40042                 tag: 'div',
40043                 cls: this.hasFeedback ? 'has-feedback' : '',
40044                 cn: [
40045                     hiddenInput,
40046                     input,
40047                     {
40048                         tag: 'input',
40049                         cls: 'dial-code-holder',
40050                         disabled: true
40051                     }
40052                 ]
40053             };
40054             
40055             var container = {
40056                 cls: 'roo-select2-container input-group',
40057                 cn: [
40058                     flag_container,
40059                     box
40060                 ]
40061             };
40062             
40063             if (this.fieldLabel.length) {
40064                 var indicator = {
40065                     tag: 'i',
40066                     tooltip: 'This field is required'
40067                 };
40068                 
40069                 var label = {
40070                     tag: 'label',
40071                     'for':  id,
40072                     cls: 'control-label',
40073                     cn: []
40074                 };
40075                 
40076                 var label_text = {
40077                     tag: 'span',
40078                     html: this.fieldLabel
40079                 };
40080                 
40081                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40082                 label.cn = [
40083                     indicator,
40084                     label_text
40085                 ];
40086                 
40087                 if(this.indicatorpos == 'right') {
40088                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40089                     label.cn = [
40090                         label_text,
40091                         indicator
40092                     ];
40093                 }
40094                 
40095                 if(align == 'left') {
40096                     container = {
40097                         tag: 'div',
40098                         cn: [
40099                             container
40100                         ]
40101                     };
40102                     
40103                     if(this.labelWidth > 12){
40104                         label.style = "width: " + this.labelWidth + 'px';
40105                     }
40106                     if(this.labelWidth < 13 && this.labelmd == 0){
40107                         this.labelmd = this.labelWidth;
40108                     }
40109                     if(this.labellg > 0){
40110                         label.cls += ' col-lg-' + this.labellg;
40111                         input.cls += ' col-lg-' + (12 - this.labellg);
40112                     }
40113                     if(this.labelmd > 0){
40114                         label.cls += ' col-md-' + this.labelmd;
40115                         container.cls += ' col-md-' + (12 - this.labelmd);
40116                     }
40117                     if(this.labelsm > 0){
40118                         label.cls += ' col-sm-' + this.labelsm;
40119                         container.cls += ' col-sm-' + (12 - this.labelsm);
40120                     }
40121                     if(this.labelxs > 0){
40122                         label.cls += ' col-xs-' + this.labelxs;
40123                         container.cls += ' col-xs-' + (12 - this.labelxs);
40124                     }
40125                 }
40126             }
40127             
40128             cfg.cn = [
40129                 label,
40130                 container
40131             ];
40132             
40133             var settings = this;
40134             
40135             ['xs','sm','md','lg'].map(function(size){
40136                 if (settings[size]) {
40137                     cfg.cls += ' col-' + size + '-' + settings[size];
40138                 }
40139             });
40140             
40141             this.store = new Roo.data.Store({
40142                 proxy : new Roo.data.MemoryProxy({}),
40143                 reader : new Roo.data.JsonReader({
40144                     fields : [
40145                         {
40146                             'name' : 'name',
40147                             'type' : 'string'
40148                         },
40149                         {
40150                             'name' : 'iso2',
40151                             'type' : 'string'
40152                         },
40153                         {
40154                             'name' : 'dialCode',
40155                             'type' : 'string'
40156                         },
40157                         {
40158                             'name' : 'priority',
40159                             'type' : 'string'
40160                         },
40161                         {
40162                             'name' : 'areaCodes',
40163                             'type' : 'string'
40164                         }
40165                     ]
40166                 })
40167             });
40168             
40169             if(!this.preferedCountries) {
40170                 this.preferedCountries = [
40171                     'hk',
40172                     'gb',
40173                     'us'
40174                 ];
40175             }
40176             
40177             var p = this.preferedCountries.reverse();
40178             
40179             if(p) {
40180                 for (var i = 0; i < p.length; i++) {
40181                     for (var j = 0; j < this.allCountries.length; j++) {
40182                         if(this.allCountries[j].iso2 == p[i]) {
40183                             var t = this.allCountries[j];
40184                             this.allCountries.splice(j,1);
40185                             this.allCountries.unshift(t);
40186                         }
40187                     } 
40188                 }
40189             }
40190             
40191             this.store.proxy.data = {
40192                 success: true,
40193                 data: this.allCountries
40194             };
40195             
40196             return cfg;
40197         },
40198         
40199         initEvents : function()
40200         {
40201             this.createList();
40202             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40203             
40204             this.indicator = this.indicatorEl();
40205             this.flag = this.flagEl();
40206             this.dialCodeHolder = this.dialCodeHolderEl();
40207             
40208             this.trigger = this.el.select('div.flag-box',true).first();
40209             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40210             
40211             var _this = this;
40212             
40213             (function(){
40214                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40215                 _this.list.setWidth(lw);
40216             }).defer(100);
40217             
40218             this.list.on('mouseover', this.onViewOver, this);
40219             this.list.on('mousemove', this.onViewMove, this);
40220             this.inputEl().on("keyup", this.onKeyUp, this);
40221             this.inputEl().on("keypress", this.onKeyPress, this);
40222             
40223             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40224
40225             this.view = new Roo.View(this.list, this.tpl, {
40226                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40227             });
40228             
40229             this.view.on('click', this.onViewClick, this);
40230             this.setValue(this.defaultDialCode);
40231         },
40232         
40233         onTriggerClick : function(e)
40234         {
40235             Roo.log('trigger click');
40236             if(this.disabled){
40237                 return;
40238             }
40239             
40240             if(this.isExpanded()){
40241                 this.collapse();
40242                 this.hasFocus = false;
40243             }else {
40244                 this.store.load({});
40245                 this.hasFocus = true;
40246                 this.expand();
40247             }
40248         },
40249         
40250         isExpanded : function()
40251         {
40252             return this.list.isVisible();
40253         },
40254         
40255         collapse : function()
40256         {
40257             if(!this.isExpanded()){
40258                 return;
40259             }
40260             this.list.hide();
40261             Roo.get(document).un('mousedown', this.collapseIf, this);
40262             Roo.get(document).un('mousewheel', this.collapseIf, this);
40263             this.fireEvent('collapse', this);
40264             this.validate();
40265         },
40266         
40267         expand : function()
40268         {
40269             Roo.log('expand');
40270
40271             if(this.isExpanded() || !this.hasFocus){
40272                 return;
40273             }
40274             
40275             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40276             this.list.setWidth(lw);
40277             
40278             this.list.show();
40279             this.restrictHeight();
40280             
40281             Roo.get(document).on('mousedown', this.collapseIf, this);
40282             Roo.get(document).on('mousewheel', this.collapseIf, this);
40283             
40284             this.fireEvent('expand', this);
40285         },
40286         
40287         restrictHeight : function()
40288         {
40289             this.list.alignTo(this.inputEl(), this.listAlign);
40290             this.list.alignTo(this.inputEl(), this.listAlign);
40291         },
40292         
40293         onViewOver : function(e, t)
40294         {
40295             if(this.inKeyMode){
40296                 return;
40297             }
40298             var item = this.view.findItemFromChild(t);
40299             
40300             if(item){
40301                 var index = this.view.indexOf(item);
40302                 this.select(index, false);
40303             }
40304         },
40305
40306         // private
40307         onViewClick : function(view, doFocus, el, e)
40308         {
40309             var index = this.view.getSelectedIndexes()[0];
40310             
40311             var r = this.store.getAt(index);
40312             
40313             if(r){
40314                 this.onSelect(r, index);
40315             }
40316             if(doFocus !== false && !this.blockFocus){
40317                 this.inputEl().focus();
40318             }
40319         },
40320         
40321         onViewMove : function(e, t)
40322         {
40323             this.inKeyMode = false;
40324         },
40325         
40326         select : function(index, scrollIntoView)
40327         {
40328             this.selectedIndex = index;
40329             this.view.select(index);
40330             if(scrollIntoView !== false){
40331                 var el = this.view.getNode(index);
40332                 if(el){
40333                     this.list.scrollChildIntoView(el, false);
40334                 }
40335             }
40336         },
40337         
40338         createList : function()
40339         {
40340             this.list = Roo.get(document.body).createChild({
40341                 tag: 'ul',
40342                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40343                 style: 'display:none'
40344             });
40345             
40346             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40347         },
40348         
40349         collapseIf : function(e)
40350         {
40351             var in_combo  = e.within(this.el);
40352             var in_list =  e.within(this.list);
40353             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40354             
40355             if (in_combo || in_list || is_list) {
40356                 return;
40357             }
40358             this.collapse();
40359         },
40360         
40361         onSelect : function(record, index)
40362         {
40363             if(this.fireEvent('beforeselect', this, record, index) !== false){
40364                 
40365                 this.setFlagClass(record.data.iso2);
40366                 this.setDialCode(record.data.dialCode);
40367                 this.hasFocus = false;
40368                 this.collapse();
40369                 this.fireEvent('select', this, record, index);
40370             }
40371         },
40372         
40373         flagEl : function()
40374         {
40375             var flag = this.el.select('div.flag',true).first();
40376             if(!flag){
40377                 return false;
40378             }
40379             return flag;
40380         },
40381         
40382         dialCodeHolderEl : function()
40383         {
40384             var d = this.el.select('input.dial-code-holder',true).first();
40385             if(!d){
40386                 return false;
40387             }
40388             return d;
40389         },
40390         
40391         setDialCode : function(v)
40392         {
40393             this.dialCodeHolder.dom.value = '+'+v;
40394         },
40395         
40396         setFlagClass : function(n)
40397         {
40398             this.flag.dom.className = 'flag '+n;
40399         },
40400         
40401         getValue : function()
40402         {
40403             var v = this.inputEl().getValue();
40404             if(this.dialCodeHolder) {
40405                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40406             }
40407             return v;
40408         },
40409         
40410         setValue : function(v)
40411         {
40412             var d = this.getDialCode(v);
40413             
40414             //invalid dial code
40415             if(v.length == 0 || !d || d.length == 0) {
40416                 if(this.rendered){
40417                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40418                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40419                 }
40420                 return;
40421             }
40422             
40423             //valid dial code
40424             this.setFlagClass(this.dialCodeMapping[d].iso2);
40425             this.setDialCode(d);
40426             this.inputEl().dom.value = v.replace('+'+d,'');
40427             this.hiddenEl().dom.value = this.getValue();
40428             
40429             this.validate();
40430         },
40431         
40432         getDialCode : function(v)
40433         {
40434             v = v ||  '';
40435             
40436             if (v.length == 0) {
40437                 return this.dialCodeHolder.dom.value;
40438             }
40439             
40440             var dialCode = "";
40441             if (v.charAt(0) != "+") {
40442                 return false;
40443             }
40444             var numericChars = "";
40445             for (var i = 1; i < v.length; i++) {
40446               var c = v.charAt(i);
40447               if (!isNaN(c)) {
40448                 numericChars += c;
40449                 if (this.dialCodeMapping[numericChars]) {
40450                   dialCode = v.substr(1, i);
40451                 }
40452                 if (numericChars.length == 4) {
40453                   break;
40454                 }
40455               }
40456             }
40457             return dialCode;
40458         },
40459         
40460         reset : function()
40461         {
40462             this.setValue(this.defaultDialCode);
40463             this.validate();
40464         },
40465         
40466         hiddenEl : function()
40467         {
40468             return this.el.select('input.hidden-tel-input',true).first();
40469         },
40470         
40471         // after setting val
40472         onKeyUp : function(e){
40473             this.setValue(this.getValue());
40474         },
40475         
40476         onKeyPress : function(e){
40477             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40478                 e.stopEvent();
40479             }
40480         }
40481         
40482 });
40483 /**
40484  * @class Roo.bootstrap.MoneyField
40485  * @extends Roo.bootstrap.ComboBox
40486  * Bootstrap MoneyField class
40487  * 
40488  * @constructor
40489  * Create a new MoneyField.
40490  * @param {Object} config Configuration options
40491  */
40492
40493 Roo.bootstrap.MoneyField = function(config) {
40494     
40495     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40496     
40497 };
40498
40499 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40500     
40501     /**
40502      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40503      */
40504     allowDecimals : true,
40505     /**
40506      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40507      */
40508     decimalSeparator : ".",
40509     /**
40510      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40511      */
40512     decimalPrecision : 0,
40513     /**
40514      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40515      */
40516     allowNegative : true,
40517     /**
40518      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40519      */
40520     allowZero: true,
40521     /**
40522      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40523      */
40524     minValue : Number.NEGATIVE_INFINITY,
40525     /**
40526      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40527      */
40528     maxValue : Number.MAX_VALUE,
40529     /**
40530      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40531      */
40532     minText : "The minimum value for this field is {0}",
40533     /**
40534      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40535      */
40536     maxText : "The maximum value for this field is {0}",
40537     /**
40538      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40539      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40540      */
40541     nanText : "{0} is not a valid number",
40542     /**
40543      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40544      */
40545     castInt : true,
40546     /**
40547      * @cfg {String} defaults currency of the MoneyField
40548      * value should be in lkey
40549      */
40550     defaultCurrency : false,
40551     /**
40552      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40553      */
40554     thousandsDelimiter : false,
40555     /**
40556      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40557      */
40558     max_length: false,
40559     
40560     inputlg : 9,
40561     inputmd : 9,
40562     inputsm : 9,
40563     inputxs : 6,
40564     
40565     store : false,
40566     
40567     getAutoCreate : function()
40568     {
40569         var align = this.labelAlign || this.parentLabelAlign();
40570         
40571         var id = Roo.id();
40572
40573         var cfg = {
40574             cls: 'form-group',
40575             cn: []
40576         };
40577
40578         var input =  {
40579             tag: 'input',
40580             id : id,
40581             cls : 'form-control roo-money-amount-input',
40582             autocomplete: 'new-password'
40583         };
40584         
40585         var hiddenInput = {
40586             tag: 'input',
40587             type: 'hidden',
40588             id: Roo.id(),
40589             cls: 'hidden-number-input'
40590         };
40591         
40592         if(this.max_length) {
40593             input.maxlength = this.max_length; 
40594         }
40595         
40596         if (this.name) {
40597             hiddenInput.name = this.name;
40598         }
40599
40600         if (this.disabled) {
40601             input.disabled = true;
40602         }
40603
40604         var clg = 12 - this.inputlg;
40605         var cmd = 12 - this.inputmd;
40606         var csm = 12 - this.inputsm;
40607         var cxs = 12 - this.inputxs;
40608         
40609         var container = {
40610             tag : 'div',
40611             cls : 'row roo-money-field',
40612             cn : [
40613                 {
40614                     tag : 'div',
40615                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40616                     cn : [
40617                         {
40618                             tag : 'div',
40619                             cls: 'roo-select2-container input-group',
40620                             cn: [
40621                                 {
40622                                     tag : 'input',
40623                                     cls : 'form-control roo-money-currency-input',
40624                                     autocomplete: 'new-password',
40625                                     readOnly : 1,
40626                                     name : this.currencyName
40627                                 },
40628                                 {
40629                                     tag :'span',
40630                                     cls : 'input-group-addon',
40631                                     cn : [
40632                                         {
40633                                             tag: 'span',
40634                                             cls: 'caret'
40635                                         }
40636                                     ]
40637                                 }
40638                             ]
40639                         }
40640                     ]
40641                 },
40642                 {
40643                     tag : 'div',
40644                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40645                     cn : [
40646                         {
40647                             tag: 'div',
40648                             cls: this.hasFeedback ? 'has-feedback' : '',
40649                             cn: [
40650                                 input
40651                             ]
40652                         }
40653                     ]
40654                 }
40655             ]
40656             
40657         };
40658         
40659         if (this.fieldLabel.length) {
40660             var indicator = {
40661                 tag: 'i',
40662                 tooltip: 'This field is required'
40663             };
40664
40665             var label = {
40666                 tag: 'label',
40667                 'for':  id,
40668                 cls: 'control-label',
40669                 cn: []
40670             };
40671
40672             var label_text = {
40673                 tag: 'span',
40674                 html: this.fieldLabel
40675             };
40676
40677             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40678             label.cn = [
40679                 indicator,
40680                 label_text
40681             ];
40682
40683             if(this.indicatorpos == 'right') {
40684                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40685                 label.cn = [
40686                     label_text,
40687                     indicator
40688                 ];
40689             }
40690
40691             if(align == 'left') {
40692                 container = {
40693                     tag: 'div',
40694                     cn: [
40695                         container
40696                     ]
40697                 };
40698
40699                 if(this.labelWidth > 12){
40700                     label.style = "width: " + this.labelWidth + 'px';
40701                 }
40702                 if(this.labelWidth < 13 && this.labelmd == 0){
40703                     this.labelmd = this.labelWidth;
40704                 }
40705                 if(this.labellg > 0){
40706                     label.cls += ' col-lg-' + this.labellg;
40707                     input.cls += ' col-lg-' + (12 - this.labellg);
40708                 }
40709                 if(this.labelmd > 0){
40710                     label.cls += ' col-md-' + this.labelmd;
40711                     container.cls += ' col-md-' + (12 - this.labelmd);
40712                 }
40713                 if(this.labelsm > 0){
40714                     label.cls += ' col-sm-' + this.labelsm;
40715                     container.cls += ' col-sm-' + (12 - this.labelsm);
40716                 }
40717                 if(this.labelxs > 0){
40718                     label.cls += ' col-xs-' + this.labelxs;
40719                     container.cls += ' col-xs-' + (12 - this.labelxs);
40720                 }
40721             }
40722         }
40723
40724         cfg.cn = [
40725             label,
40726             container,
40727             hiddenInput
40728         ];
40729         
40730         var settings = this;
40731
40732         ['xs','sm','md','lg'].map(function(size){
40733             if (settings[size]) {
40734                 cfg.cls += ' col-' + size + '-' + settings[size];
40735             }
40736         });
40737         
40738         return cfg;
40739     },
40740     
40741     initEvents : function()
40742     {
40743         this.indicator = this.indicatorEl();
40744         
40745         this.initCurrencyEvent();
40746         
40747         this.initNumberEvent();
40748     },
40749     
40750     initCurrencyEvent : function()
40751     {
40752         if (!this.store) {
40753             throw "can not find store for combo";
40754         }
40755         
40756         this.store = Roo.factory(this.store, Roo.data);
40757         this.store.parent = this;
40758         
40759         this.createList();
40760         
40761         this.triggerEl = this.el.select('.input-group-addon', true).first();
40762         
40763         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40764         
40765         var _this = this;
40766         
40767         (function(){
40768             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40769             _this.list.setWidth(lw);
40770         }).defer(100);
40771         
40772         this.list.on('mouseover', this.onViewOver, this);
40773         this.list.on('mousemove', this.onViewMove, this);
40774         this.list.on('scroll', this.onViewScroll, this);
40775         
40776         if(!this.tpl){
40777             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40778         }
40779         
40780         this.view = new Roo.View(this.list, this.tpl, {
40781             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40782         });
40783         
40784         this.view.on('click', this.onViewClick, this);
40785         
40786         this.store.on('beforeload', this.onBeforeLoad, this);
40787         this.store.on('load', this.onLoad, this);
40788         this.store.on('loadexception', this.onLoadException, this);
40789         
40790         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40791             "up" : function(e){
40792                 this.inKeyMode = true;
40793                 this.selectPrev();
40794             },
40795
40796             "down" : function(e){
40797                 if(!this.isExpanded()){
40798                     this.onTriggerClick();
40799                 }else{
40800                     this.inKeyMode = true;
40801                     this.selectNext();
40802                 }
40803             },
40804
40805             "enter" : function(e){
40806                 this.collapse();
40807                 
40808                 if(this.fireEvent("specialkey", this, e)){
40809                     this.onViewClick(false);
40810                 }
40811                 
40812                 return true;
40813             },
40814
40815             "esc" : function(e){
40816                 this.collapse();
40817             },
40818
40819             "tab" : function(e){
40820                 this.collapse();
40821                 
40822                 if(this.fireEvent("specialkey", this, e)){
40823                     this.onViewClick(false);
40824                 }
40825                 
40826                 return true;
40827             },
40828
40829             scope : this,
40830
40831             doRelay : function(foo, bar, hname){
40832                 if(hname == 'down' || this.scope.isExpanded()){
40833                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40834                 }
40835                 return true;
40836             },
40837
40838             forceKeyDown: true
40839         });
40840         
40841         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40842         
40843     },
40844     
40845     initNumberEvent : function(e)
40846     {
40847         this.inputEl().on("keydown" , this.fireKey,  this);
40848         this.inputEl().on("focus", this.onFocus,  this);
40849         this.inputEl().on("blur", this.onBlur,  this);
40850         
40851         this.inputEl().relayEvent('keyup', this);
40852         
40853         if(this.indicator){
40854             this.indicator.addClass('invisible');
40855         }
40856  
40857         this.originalValue = this.getValue();
40858         
40859         if(this.validationEvent == 'keyup'){
40860             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40861             this.inputEl().on('keyup', this.filterValidation, this);
40862         }
40863         else if(this.validationEvent !== false){
40864             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40865         }
40866         
40867         if(this.selectOnFocus){
40868             this.on("focus", this.preFocus, this);
40869             
40870         }
40871         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40872             this.inputEl().on("keypress", this.filterKeys, this);
40873         } else {
40874             this.inputEl().relayEvent('keypress', this);
40875         }
40876         
40877         var allowed = "0123456789";
40878         
40879         if(this.allowDecimals){
40880             allowed += this.decimalSeparator;
40881         }
40882         
40883         if(this.allowNegative){
40884             allowed += "-";
40885         }
40886         
40887         if(this.thousandsDelimiter) {
40888             allowed += ",";
40889         }
40890         
40891         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40892         
40893         var keyPress = function(e){
40894             
40895             var k = e.getKey();
40896             
40897             var c = e.getCharCode();
40898             
40899             if(
40900                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40901                     allowed.indexOf(String.fromCharCode(c)) === -1
40902             ){
40903                 e.stopEvent();
40904                 return;
40905             }
40906             
40907             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40908                 return;
40909             }
40910             
40911             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40912                 e.stopEvent();
40913             }
40914         };
40915         
40916         this.inputEl().on("keypress", keyPress, this);
40917         
40918     },
40919     
40920     onTriggerClick : function(e)
40921     {   
40922         if(this.disabled){
40923             return;
40924         }
40925         
40926         this.page = 0;
40927         this.loadNext = false;
40928         
40929         if(this.isExpanded()){
40930             this.collapse();
40931             return;
40932         }
40933         
40934         this.hasFocus = true;
40935         
40936         if(this.triggerAction == 'all') {
40937             this.doQuery(this.allQuery, true);
40938             return;
40939         }
40940         
40941         this.doQuery(this.getRawValue());
40942     },
40943     
40944     getCurrency : function()
40945     {   
40946         var v = this.currencyEl().getValue();
40947         
40948         return v;
40949     },
40950     
40951     restrictHeight : function()
40952     {
40953         this.list.alignTo(this.currencyEl(), this.listAlign);
40954         this.list.alignTo(this.currencyEl(), this.listAlign);
40955     },
40956     
40957     onViewClick : function(view, doFocus, el, e)
40958     {
40959         var index = this.view.getSelectedIndexes()[0];
40960         
40961         var r = this.store.getAt(index);
40962         
40963         if(r){
40964             this.onSelect(r, index);
40965         }
40966     },
40967     
40968     onSelect : function(record, index){
40969         
40970         if(this.fireEvent('beforeselect', this, record, index) !== false){
40971         
40972             this.setFromCurrencyData(index > -1 ? record.data : false);
40973             
40974             this.collapse();
40975             
40976             this.fireEvent('select', this, record, index);
40977         }
40978     },
40979     
40980     setFromCurrencyData : function(o)
40981     {
40982         var currency = '';
40983         
40984         this.lastCurrency = o;
40985         
40986         if (this.currencyField) {
40987             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40988         } else {
40989             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40990         }
40991         
40992         this.lastSelectionText = currency;
40993         
40994         //setting default currency
40995         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40996             this.setCurrency(this.defaultCurrency);
40997             return;
40998         }
40999         
41000         this.setCurrency(currency);
41001     },
41002     
41003     setFromData : function(o)
41004     {
41005         var c = {};
41006         
41007         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41008         
41009         this.setFromCurrencyData(c);
41010         
41011         var value = '';
41012         
41013         if (this.name) {
41014             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41015         } else {
41016             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41017         }
41018         
41019         this.setValue(value);
41020         
41021     },
41022     
41023     setCurrency : function(v)
41024     {   
41025         this.currencyValue = v;
41026         
41027         if(this.rendered){
41028             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41029             this.validate();
41030         }
41031     },
41032     
41033     setValue : function(v)
41034     {
41035         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41036         
41037         this.value = v;
41038         
41039         if(this.rendered){
41040             
41041             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41042             
41043             this.inputEl().dom.value = (v == '') ? '' :
41044                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41045             
41046             if(!this.allowZero && v === '0') {
41047                 this.hiddenEl().dom.value = '';
41048                 this.inputEl().dom.value = '';
41049             }
41050             
41051             this.validate();
41052         }
41053     },
41054     
41055     getRawValue : function()
41056     {
41057         var v = this.inputEl().getValue();
41058         
41059         return v;
41060     },
41061     
41062     getValue : function()
41063     {
41064         return this.fixPrecision(this.parseValue(this.getRawValue()));
41065     },
41066     
41067     parseValue : function(value)
41068     {
41069         if(this.thousandsDelimiter) {
41070             value += "";
41071             r = new RegExp(",", "g");
41072             value = value.replace(r, "");
41073         }
41074         
41075         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41076         return isNaN(value) ? '' : value;
41077         
41078     },
41079     
41080     fixPrecision : function(value)
41081     {
41082         if(this.thousandsDelimiter) {
41083             value += "";
41084             r = new RegExp(",", "g");
41085             value = value.replace(r, "");
41086         }
41087         
41088         var nan = isNaN(value);
41089         
41090         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41091             return nan ? '' : value;
41092         }
41093         return parseFloat(value).toFixed(this.decimalPrecision);
41094     },
41095     
41096     decimalPrecisionFcn : function(v)
41097     {
41098         return Math.floor(v);
41099     },
41100     
41101     validateValue : function(value)
41102     {
41103         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41104             return false;
41105         }
41106         
41107         var num = this.parseValue(value);
41108         
41109         if(isNaN(num)){
41110             this.markInvalid(String.format(this.nanText, value));
41111             return false;
41112         }
41113         
41114         if(num < this.minValue){
41115             this.markInvalid(String.format(this.minText, this.minValue));
41116             return false;
41117         }
41118         
41119         if(num > this.maxValue){
41120             this.markInvalid(String.format(this.maxText, this.maxValue));
41121             return false;
41122         }
41123         
41124         return true;
41125     },
41126     
41127     validate : function()
41128     {
41129         if(this.disabled || this.allowBlank){
41130             this.markValid();
41131             return true;
41132         }
41133         
41134         var currency = this.getCurrency();
41135         
41136         if(this.validateValue(this.getRawValue()) && currency.length){
41137             this.markValid();
41138             return true;
41139         }
41140         
41141         this.markInvalid();
41142         return false;
41143     },
41144     
41145     getName: function()
41146     {
41147         return this.name;
41148     },
41149     
41150     beforeBlur : function()
41151     {
41152         if(!this.castInt){
41153             return;
41154         }
41155         
41156         var v = this.parseValue(this.getRawValue());
41157         
41158         if(v || v == 0){
41159             this.setValue(v);
41160         }
41161     },
41162     
41163     onBlur : function()
41164     {
41165         this.beforeBlur();
41166         
41167         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41168             //this.el.removeClass(this.focusClass);
41169         }
41170         
41171         this.hasFocus = false;
41172         
41173         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41174             this.validate();
41175         }
41176         
41177         var v = this.getValue();
41178         
41179         if(String(v) !== String(this.startValue)){
41180             this.fireEvent('change', this, v, this.startValue);
41181         }
41182         
41183         this.fireEvent("blur", this);
41184     },
41185     
41186     inputEl : function()
41187     {
41188         return this.el.select('.roo-money-amount-input', true).first();
41189     },
41190     
41191     currencyEl : function()
41192     {
41193         return this.el.select('.roo-money-currency-input', true).first();
41194     },
41195     
41196     hiddenEl : function()
41197     {
41198         return this.el.select('input.hidden-number-input',true).first();
41199     }
41200     
41201 });