Roo/bootstrap/NavItem.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 | success | info | warning | danger | link ) default 
585  * @cfg {String} size ( lg | sm | xs)
586  * @cfg {String} tag ( a | input | submit)
587  * @cfg {String} href empty or href
588  * @cfg {Boolean} disabled default false;
589  * @cfg {Boolean} isClose default false;
590  * @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)
591  * @cfg {String} badge text for badge
592  * @cfg {String} theme (default|glow)  
593  * @cfg {Boolean} inverse dark themed version
594  * @cfg {Boolean} toggle is it a slidy toggle button
595  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
596  * @cfg {String} ontext text for on slidy toggle state
597  * @cfg {String} offtext text for off slidy toggle state
598  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
599  * @cfg {Boolean} removeClass remove the standard class..
600  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
601  * 
602  * @constructor
603  * Create a new button
604  * @param {Object} config The config object
605  */
606
607
608 Roo.bootstrap.Button = function(config){
609     Roo.bootstrap.Button.superclass.constructor.call(this, config);
610     this.weightClass = ["btn-default", 
611                        "btn-primary", 
612                        "btn-success", 
613                        "btn-info", 
614                        "btn-warning",
615                        "btn-danger",
616                        "btn-link"
617                       ],  
618     this.addEvents({
619         // raw events
620         /**
621          * @event click
622          * When a butotn is pressed
623          * @param {Roo.bootstrap.Button} btn
624          * @param {Roo.EventObject} e
625          */
626         "click" : true,
627          /**
628          * @event toggle
629          * After the button has been toggles
630          * @param {Roo.bootstrap.Button} btn
631          * @param {Roo.EventObject} e
632          * @param {boolean} pressed (also available as button.pressed)
633          */
634         "toggle" : true
635     });
636 };
637
638 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
639     html: false,
640     active: false,
641     weight: '',
642     size: '',
643     tag: 'button',
644     href: '',
645     disabled: false,
646     isClose: false,
647     glyphicon: '',
648     badge: '',
649     theme: 'default',
650     inverse: false,
651     
652     toggle: false,
653     ontext: 'ON',
654     offtext: 'OFF',
655     defaulton: true,
656     preventDefault: true,
657     removeClass: false,
658     name: false,
659     target: false,
660      
661     pressed : null,
662      
663     
664     getAutoCreate : function(){
665         
666         var cfg = {
667             tag : 'button',
668             cls : 'roo-button',
669             html: ''
670         };
671         
672         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
673             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
674             this.tag = 'button';
675         } else {
676             cfg.tag = this.tag;
677         }
678         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
679         
680         if (this.toggle == true) {
681             cfg={
682                 tag: 'div',
683                 cls: 'slider-frame roo-button',
684                 cn: [
685                     {
686                         tag: 'span',
687                         'data-on-text':'ON',
688                         'data-off-text':'OFF',
689                         cls: 'slider-button',
690                         html: this.offtext
691                     }
692                 ]
693             };
694             
695             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696                 cfg.cls += ' '+this.weight;
697             }
698             
699             return cfg;
700         }
701         
702         if (this.isClose) {
703             cfg.cls += ' close';
704             
705             cfg["aria-hidden"] = true;
706             
707             cfg.html = "&times;";
708             
709             return cfg;
710         }
711         
712          
713         if (this.theme==='default') {
714             cfg.cls = 'btn roo-button';
715             
716             //if (this.parentType != 'Navbar') {
717             this.weight = this.weight.length ?  this.weight : 'default';
718             //}
719             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
720                 
721                 cfg.cls += ' btn-' + this.weight;
722             }
723         } else if (this.theme==='glow') {
724             
725             cfg.tag = 'a';
726             cfg.cls = 'btn-glow roo-button';
727             
728             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
729                 
730                 cfg.cls += ' ' + this.weight;
731             }
732         }
733    
734         
735         if (this.inverse) {
736             this.cls += ' inverse';
737         }
738         
739         
740         if (this.active || this.pressed === true) {
741             cfg.cls += ' active';
742         }
743         
744         if (this.disabled) {
745             cfg.disabled = 'disabled';
746         }
747         
748         if (this.items) {
749             Roo.log('changing to ul' );
750             cfg.tag = 'ul';
751             this.glyphicon = 'caret';
752         }
753         
754         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
755          
756         //gsRoo.log(this.parentType);
757         if (this.parentType === 'Navbar' && !this.parent().bar) {
758             Roo.log('changing to li?');
759             
760             cfg.tag = 'li';
761             
762             cfg.cls = '';
763             cfg.cn =  [{
764                 tag : 'a',
765                 cls : 'roo-button',
766                 html : this.html,
767                 href : this.href || '#'
768             }];
769             if (this.menu) {
770                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
771                 cfg.cls += ' dropdown';
772             }   
773             
774             delete cfg.html;
775             
776         }
777         
778        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
779         
780         if (this.glyphicon) {
781             cfg.html = ' ' + cfg.html;
782             
783             cfg.cn = [
784                 {
785                     tag: 'span',
786                     cls: 'glyphicon glyphicon-' + this.glyphicon
787                 }
788             ];
789         }
790         
791         if (this.badge) {
792             cfg.html += ' ';
793             
794             cfg.tag = 'a';
795             
796 //            cfg.cls='btn roo-button';
797             
798             cfg.href=this.href;
799             
800             var value = cfg.html;
801             
802             if(this.glyphicon){
803                 value = {
804                             tag: 'span',
805                             cls: 'glyphicon glyphicon-' + this.glyphicon,
806                             html: this.html
807                         };
808                 
809             }
810             
811             cfg.cn = [
812                 value,
813                 {
814                     tag: 'span',
815                     cls: 'badge',
816                     html: this.badge
817                 }
818             ];
819             
820             cfg.html='';
821         }
822         
823         if (this.menu) {
824             cfg.cls += ' dropdown';
825             cfg.html = typeof(cfg.html) != 'undefined' ?
826                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
827         }
828         
829         if (cfg.tag !== 'a' && this.href !== '') {
830             throw "Tag must be a to set href.";
831         } else if (this.href.length > 0) {
832             cfg.href = this.href;
833         }
834         
835         if(this.removeClass){
836             cfg.cls = '';
837         }
838         
839         if(this.target){
840             cfg.target = this.target;
841         }
842         
843         return cfg;
844     },
845     initEvents: function() {
846        // Roo.log('init events?');
847 //        Roo.log(this.el.dom);
848         // add the menu...
849         
850         if (typeof (this.menu) != 'undefined') {
851             this.menu.parentType = this.xtype;
852             this.menu.triggerEl = this.el;
853             this.addxtype(Roo.apply({}, this.menu));
854         }
855
856
857        if (this.el.hasClass('roo-button')) {
858             this.el.on('click', this.onClick, this);
859        } else {
860             this.el.select('.roo-button').on('click', this.onClick, this);
861        }
862        
863        if(this.removeClass){
864            this.el.on('click', this.onClick, this);
865        }
866        
867        this.el.enableDisplayMode();
868         
869     },
870     onClick : function(e)
871     {
872         if (this.disabled) {
873             return;
874         }
875         
876         Roo.log('button on click ');
877         if(this.preventDefault){
878             e.preventDefault();
879         }
880         
881         if (this.pressed === true || this.pressed === false) {
882             this.toggleActive(e);
883         }
884         
885         
886         this.fireEvent('click', this, e);
887     },
888     
889     /**
890      * Enables this button
891      */
892     enable : function()
893     {
894         this.disabled = false;
895         this.el.removeClass('disabled');
896     },
897     
898     /**
899      * Disable this button
900      */
901     disable : function()
902     {
903         this.disabled = true;
904         this.el.addClass('disabled');
905     },
906      /**
907      * sets the active state on/off, 
908      * @param {Boolean} state (optional) Force a particular state
909      */
910     setActive : function(v) {
911         
912         this.el[v ? 'addClass' : 'removeClass']('active');
913         this.pressed = v;
914     },
915      /**
916      * toggles the current active state 
917      */
918     toggleActive : function(e)
919     {
920         this.setActive(!this.pressed);
921         this.fireEvent('toggle', this, e, !this.pressed);
922     },
923      /**
924      * get the current active state
925      * @return {boolean} true if it's active
926      */
927     isActive : function()
928     {
929         return this.el.hasClass('active');
930     },
931     /**
932      * set the text of the first selected button
933      */
934     setText : function(str)
935     {
936         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
937     },
938     /**
939      * get the text of the first selected button
940      */
941     getText : function()
942     {
943         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
944     },
945     
946     setWeight : function(str)
947     {
948         this.el.removeClass(this.weightClass);
949         this.el.addClass('btn-' + str);        
950     }
951     
952     
953 });
954
955  /*
956  * - LGPL
957  *
958  * column
959  * 
960  */
961
962 /**
963  * @class Roo.bootstrap.Column
964  * @extends Roo.bootstrap.Component
965  * Bootstrap Column class
966  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
967  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
968  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
969  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
970  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
971  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
972  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
973  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
974  *
975  * 
976  * @cfg {Boolean} hidden (true|false) hide the element
977  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
978  * @cfg {String} fa (ban|check|...) font awesome icon
979  * @cfg {Number} fasize (1|2|....) font awsome size
980
981  * @cfg {String} icon (info-sign|check|...) glyphicon name
982
983  * @cfg {String} html content of column.
984  * 
985  * @constructor
986  * Create a new Column
987  * @param {Object} config The config object
988  */
989
990 Roo.bootstrap.Column = function(config){
991     Roo.bootstrap.Column.superclass.constructor.call(this, config);
992 };
993
994 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
995     
996     xs: false,
997     sm: false,
998     md: false,
999     lg: false,
1000     xsoff: false,
1001     smoff: false,
1002     mdoff: false,
1003     lgoff: false,
1004     html: '',
1005     offset: 0,
1006     alert: false,
1007     fa: false,
1008     icon : false,
1009     hidden : false,
1010     fasize : 1,
1011     
1012     getAutoCreate : function(){
1013         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1014         
1015         cfg = {
1016             tag: 'div',
1017             cls: 'column'
1018         };
1019         
1020         var settings=this;
1021         ['xs','sm','md','lg'].map(function(size){
1022             //Roo.log( size + ':' + settings[size]);
1023             
1024             if (settings[size+'off'] !== false) {
1025                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1026             }
1027             
1028             if (settings[size] === false) {
1029                 return;
1030             }
1031             
1032             if (!settings[size]) { // 0 = hidden
1033                 cfg.cls += ' hidden-' + size;
1034                 return;
1035             }
1036             cfg.cls += ' col-' + size + '-' + settings[size];
1037             
1038         });
1039         
1040         if (this.hidden) {
1041             cfg.cls += ' hidden';
1042         }
1043         
1044         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1045             cfg.cls +=' alert alert-' + this.alert;
1046         }
1047         
1048         
1049         if (this.html.length) {
1050             cfg.html = this.html;
1051         }
1052         if (this.fa) {
1053             var fasize = '';
1054             if (this.fasize > 1) {
1055                 fasize = ' fa-' + this.fasize + 'x';
1056             }
1057             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1058             
1059             
1060         }
1061         if (this.icon) {
1062             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1063         }
1064         
1065         return cfg;
1066     }
1067    
1068 });
1069
1070  
1071
1072  /*
1073  * - LGPL
1074  *
1075  * page container.
1076  * 
1077  */
1078
1079
1080 /**
1081  * @class Roo.bootstrap.Container
1082  * @extends Roo.bootstrap.Component
1083  * Bootstrap Container class
1084  * @cfg {Boolean} jumbotron is it a jumbotron element
1085  * @cfg {String} html content of element
1086  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1087  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1088  * @cfg {String} header content of header (for panel)
1089  * @cfg {String} footer content of footer (for panel)
1090  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1091  * @cfg {String} tag (header|aside|section) type of HTML tag.
1092  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1093  * @cfg {String} fa font awesome icon
1094  * @cfg {String} icon (info-sign|check|...) glyphicon name
1095  * @cfg {Boolean} hidden (true|false) hide the element
1096  * @cfg {Boolean} expandable (true|false) default false
1097  * @cfg {Boolean} expanded (true|false) default true
1098  * @cfg {String} rheader contet on the right of header
1099  * @cfg {Boolean} clickable (true|false) default false
1100
1101  *     
1102  * @constructor
1103  * Create a new Container
1104  * @param {Object} config The config object
1105  */
1106
1107 Roo.bootstrap.Container = function(config){
1108     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1109     
1110     this.addEvents({
1111         // raw events
1112          /**
1113          * @event expand
1114          * After the panel has been expand
1115          * 
1116          * @param {Roo.bootstrap.Container} this
1117          */
1118         "expand" : true,
1119         /**
1120          * @event collapse
1121          * After the panel has been collapsed
1122          * 
1123          * @param {Roo.bootstrap.Container} this
1124          */
1125         "collapse" : true,
1126         /**
1127          * @event click
1128          * When a element is chick
1129          * @param {Roo.bootstrap.Container} this
1130          * @param {Roo.EventObject} e
1131          */
1132         "click" : true
1133     });
1134 };
1135
1136 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1137     
1138     jumbotron : false,
1139     well: '',
1140     panel : '',
1141     header: '',
1142     footer : '',
1143     sticky: '',
1144     tag : false,
1145     alert : false,
1146     fa: false,
1147     icon : false,
1148     expandable : false,
1149     rheader : '',
1150     expanded : true,
1151     clickable: false,
1152   
1153      
1154     getChildContainer : function() {
1155         
1156         if(!this.el){
1157             return false;
1158         }
1159         
1160         if (this.panel.length) {
1161             return this.el.select('.panel-body',true).first();
1162         }
1163         
1164         return this.el;
1165     },
1166     
1167     
1168     getAutoCreate : function(){
1169         
1170         var cfg = {
1171             tag : this.tag || 'div',
1172             html : '',
1173             cls : ''
1174         };
1175         if (this.jumbotron) {
1176             cfg.cls = 'jumbotron';
1177         }
1178         
1179         
1180         
1181         // - this is applied by the parent..
1182         //if (this.cls) {
1183         //    cfg.cls = this.cls + '';
1184         //}
1185         
1186         if (this.sticky.length) {
1187             
1188             var bd = Roo.get(document.body);
1189             if (!bd.hasClass('bootstrap-sticky')) {
1190                 bd.addClass('bootstrap-sticky');
1191                 Roo.select('html',true).setStyle('height', '100%');
1192             }
1193              
1194             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1195         }
1196         
1197         
1198         if (this.well.length) {
1199             switch (this.well) {
1200                 case 'lg':
1201                 case 'sm':
1202                     cfg.cls +=' well well-' +this.well;
1203                     break;
1204                 default:
1205                     cfg.cls +=' well';
1206                     break;
1207             }
1208         }
1209         
1210         if (this.hidden) {
1211             cfg.cls += ' hidden';
1212         }
1213         
1214         
1215         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1216             cfg.cls +=' alert alert-' + this.alert;
1217         }
1218         
1219         var body = cfg;
1220         
1221         if (this.panel.length) {
1222             cfg.cls += ' panel panel-' + this.panel;
1223             cfg.cn = [];
1224             if (this.header.length) {
1225                 
1226                 var h = [];
1227                 
1228                 if(this.expandable){
1229                     
1230                     cfg.cls = cfg.cls + ' expandable';
1231                     
1232                     h.push({
1233                         tag: 'i',
1234                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1235                     });
1236                     
1237                 }
1238                 
1239                 h.push(
1240                     {
1241                         tag: 'span',
1242                         cls : 'panel-title',
1243                         html : (this.expandable ? '&nbsp;' : '') + this.header
1244                     },
1245                     {
1246                         tag: 'span',
1247                         cls: 'panel-header-right',
1248                         html: this.rheader
1249                     }
1250                 );
1251                 
1252                 cfg.cn.push({
1253                     cls : 'panel-heading',
1254                     style : this.expandable ? 'cursor: pointer' : '',
1255                     cn : h
1256                 });
1257                 
1258             }
1259             
1260             body = false;
1261             cfg.cn.push({
1262                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1263                 html : this.html
1264             });
1265             
1266             
1267             if (this.footer.length) {
1268                 cfg.cn.push({
1269                     cls : 'panel-footer',
1270                     html : this.footer
1271                     
1272                 });
1273             }
1274             
1275         }
1276         
1277         if (body) {
1278             body.html = this.html || cfg.html;
1279             // prefix with the icons..
1280             if (this.fa) {
1281                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1282             }
1283             if (this.icon) {
1284                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1285             }
1286             
1287             
1288         }
1289         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1290             cfg.cls =  'container';
1291         }
1292         
1293         return cfg;
1294     },
1295     
1296     initEvents: function() 
1297     {
1298         if(this.expandable){
1299             var headerEl = this.headerEl();
1300         
1301             if(headerEl){
1302                 headerEl.on('click', this.onToggleClick, this);
1303             }
1304         }
1305         
1306         if(this.clickable){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310     },
1311     
1312     onToggleClick : function()
1313     {
1314         var headerEl = this.headerEl();
1315         
1316         if(!headerEl){
1317             return;
1318         }
1319         
1320         if(this.expanded){
1321             this.collapse();
1322             return;
1323         }
1324         
1325         this.expand();
1326     },
1327     
1328     expand : function()
1329     {
1330         if(this.fireEvent('expand', this)) {
1331             
1332             this.expanded = true;
1333             
1334             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1335             
1336             this.el.select('.panel-body',true).first().removeClass('hide');
1337             
1338             var toggleEl = this.toggleEl();
1339
1340             if(!toggleEl){
1341                 return;
1342             }
1343
1344             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1345         }
1346         
1347     },
1348     
1349     collapse : function()
1350     {
1351         if(this.fireEvent('collapse', this)) {
1352             
1353             this.expanded = false;
1354             
1355             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1356             this.el.select('.panel-body',true).first().addClass('hide');
1357         
1358             var toggleEl = this.toggleEl();
1359
1360             if(!toggleEl){
1361                 return;
1362             }
1363
1364             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1365         }
1366     },
1367     
1368     toggleEl : function()
1369     {
1370         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1371             return;
1372         }
1373         
1374         return this.el.select('.panel-heading .fa',true).first();
1375     },
1376     
1377     headerEl : function()
1378     {
1379         if(!this.el || !this.panel.length || !this.header.length){
1380             return;
1381         }
1382         
1383         return this.el.select('.panel-heading',true).first()
1384     },
1385     
1386     bodyEl : function()
1387     {
1388         if(!this.el || !this.panel.length){
1389             return;
1390         }
1391         
1392         return this.el.select('.panel-body',true).first()
1393     },
1394     
1395     titleEl : function()
1396     {
1397         if(!this.el || !this.panel.length || !this.header.length){
1398             return;
1399         }
1400         
1401         return this.el.select('.panel-title',true).first();
1402     },
1403     
1404     setTitle : function(v)
1405     {
1406         var titleEl = this.titleEl();
1407         
1408         if(!titleEl){
1409             return;
1410         }
1411         
1412         titleEl.dom.innerHTML = v;
1413     },
1414     
1415     getTitle : function()
1416     {
1417         
1418         var titleEl = this.titleEl();
1419         
1420         if(!titleEl){
1421             return '';
1422         }
1423         
1424         return titleEl.dom.innerHTML;
1425     },
1426     
1427     setRightTitle : function(v)
1428     {
1429         var t = this.el.select('.panel-header-right',true).first();
1430         
1431         if(!t){
1432             return;
1433         }
1434         
1435         t.dom.innerHTML = v;
1436     },
1437     
1438     onClick : function(e)
1439     {
1440         e.preventDefault();
1441         
1442         this.fireEvent('click', this, e);
1443     }
1444 });
1445
1446  /*
1447  * - LGPL
1448  *
1449  * image
1450  * 
1451  */
1452
1453
1454 /**
1455  * @class Roo.bootstrap.Img
1456  * @extends Roo.bootstrap.Component
1457  * Bootstrap Img class
1458  * @cfg {Boolean} imgResponsive false | true
1459  * @cfg {String} border rounded | circle | thumbnail
1460  * @cfg {String} src image source
1461  * @cfg {String} alt image alternative text
1462  * @cfg {String} href a tag href
1463  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1464  * @cfg {String} xsUrl xs image source
1465  * @cfg {String} smUrl sm image source
1466  * @cfg {String} mdUrl md image source
1467  * @cfg {String} lgUrl lg image source
1468  * 
1469  * @constructor
1470  * Create a new Input
1471  * @param {Object} config The config object
1472  */
1473
1474 Roo.bootstrap.Img = function(config){
1475     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1476     
1477     this.addEvents({
1478         // img events
1479         /**
1480          * @event click
1481          * The img click event for the img.
1482          * @param {Roo.EventObject} e
1483          */
1484         "click" : true
1485     });
1486 };
1487
1488 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1489     
1490     imgResponsive: true,
1491     border: '',
1492     src: 'about:blank',
1493     href: false,
1494     target: false,
1495     xsUrl: '',
1496     smUrl: '',
1497     mdUrl: '',
1498     lgUrl: '',
1499
1500     getAutoCreate : function()
1501     {   
1502         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1503             return this.createSingleImg();
1504         }
1505         
1506         var cfg = {
1507             tag: 'div',
1508             cls: 'roo-image-responsive-group',
1509             cn: []
1510         };
1511         var _this = this;
1512         
1513         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1514             
1515             if(!_this[size + 'Url']){
1516                 return;
1517             }
1518             
1519             var img = {
1520                 tag: 'img',
1521                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1522                 html: _this.html || cfg.html,
1523                 src: _this[size + 'Url']
1524             };
1525             
1526             img.cls += ' roo-image-responsive-' + size;
1527             
1528             var s = ['xs', 'sm', 'md', 'lg'];
1529             
1530             s.splice(s.indexOf(size), 1);
1531             
1532             Roo.each(s, function(ss){
1533                 img.cls += ' hidden-' + ss;
1534             });
1535             
1536             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1537                 cfg.cls += ' img-' + _this.border;
1538             }
1539             
1540             if(_this.alt){
1541                 cfg.alt = _this.alt;
1542             }
1543             
1544             if(_this.href){
1545                 var a = {
1546                     tag: 'a',
1547                     href: _this.href,
1548                     cn: [
1549                         img
1550                     ]
1551                 };
1552
1553                 if(this.target){
1554                     a.target = _this.target;
1555                 }
1556             }
1557             
1558             cfg.cn.push((_this.href) ? a : img);
1559             
1560         });
1561         
1562         return cfg;
1563     },
1564     
1565     createSingleImg : function()
1566     {
1567         var cfg = {
1568             tag: 'img',
1569             cls: (this.imgResponsive) ? 'img-responsive' : '',
1570             html : null,
1571             src : 'about:blank'  // just incase src get's set to undefined?!?
1572         };
1573         
1574         cfg.html = this.html || cfg.html;
1575         
1576         cfg.src = this.src || cfg.src;
1577         
1578         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1579             cfg.cls += ' img-' + this.border;
1580         }
1581         
1582         if(this.alt){
1583             cfg.alt = this.alt;
1584         }
1585         
1586         if(this.href){
1587             var a = {
1588                 tag: 'a',
1589                 href: this.href,
1590                 cn: [
1591                     cfg
1592                 ]
1593             };
1594             
1595             if(this.target){
1596                 a.target = this.target;
1597             }
1598             
1599         }
1600         
1601         return (this.href) ? a : cfg;
1602     },
1603     
1604     initEvents: function() 
1605     {
1606         if(!this.href){
1607             this.el.on('click', this.onClick, this);
1608         }
1609         
1610     },
1611     
1612     onClick : function(e)
1613     {
1614         Roo.log('img onclick');
1615         this.fireEvent('click', this, e);
1616     },
1617     /**
1618      * Sets the url of the image - used to update it
1619      * @param {String} url the url of the image
1620      */
1621     
1622     setSrc : function(url)
1623     {
1624         this.src =  url;
1625         
1626         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1627             this.el.dom.src =  url;
1628             return;
1629         }
1630         
1631         this.el.select('img', true).first().dom.src =  url;
1632     }
1633     
1634     
1635    
1636 });
1637
1638  /*
1639  * - LGPL
1640  *
1641  * image
1642  * 
1643  */
1644
1645
1646 /**
1647  * @class Roo.bootstrap.Link
1648  * @extends Roo.bootstrap.Component
1649  * Bootstrap Link Class
1650  * @cfg {String} alt image alternative text
1651  * @cfg {String} href a tag href
1652  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1653  * @cfg {String} html the content of the link.
1654  * @cfg {String} anchor name for the anchor link
1655  * @cfg {String} fa - favicon
1656
1657  * @cfg {Boolean} preventDefault (true | false) default false
1658
1659  * 
1660  * @constructor
1661  * Create a new Input
1662  * @param {Object} config The config object
1663  */
1664
1665 Roo.bootstrap.Link = function(config){
1666     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1667     
1668     this.addEvents({
1669         // img events
1670         /**
1671          * @event click
1672          * The img click event for the img.
1673          * @param {Roo.EventObject} e
1674          */
1675         "click" : true
1676     });
1677 };
1678
1679 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1680     
1681     href: false,
1682     target: false,
1683     preventDefault: false,
1684     anchor : false,
1685     alt : false,
1686     fa: false,
1687
1688
1689     getAutoCreate : function()
1690     {
1691         var html = this.html || '';
1692         
1693         if (this.fa !== false) {
1694             html = '<i class="fa fa-' + this.fa + '"></i>';
1695         }
1696         var cfg = {
1697             tag: 'a'
1698         };
1699         // anchor's do not require html/href...
1700         if (this.anchor === false) {
1701             cfg.html = html;
1702             cfg.href = this.href || '#';
1703         } else {
1704             cfg.name = this.anchor;
1705             if (this.html !== false || this.fa !== false) {
1706                 cfg.html = html;
1707             }
1708             if (this.href !== false) {
1709                 cfg.href = this.href;
1710             }
1711         }
1712         
1713         if(this.alt !== false){
1714             cfg.alt = this.alt;
1715         }
1716         
1717         
1718         if(this.target !== false) {
1719             cfg.target = this.target;
1720         }
1721         
1722         return cfg;
1723     },
1724     
1725     initEvents: function() {
1726         
1727         if(!this.href || this.preventDefault){
1728             this.el.on('click', this.onClick, this);
1729         }
1730     },
1731     
1732     onClick : function(e)
1733     {
1734         if(this.preventDefault){
1735             e.preventDefault();
1736         }
1737         //Roo.log('img onclick');
1738         this.fireEvent('click', this, e);
1739     }
1740    
1741 });
1742
1743  /*
1744  * - LGPL
1745  *
1746  * header
1747  * 
1748  */
1749
1750 /**
1751  * @class Roo.bootstrap.Header
1752  * @extends Roo.bootstrap.Component
1753  * Bootstrap Header class
1754  * @cfg {String} html content of header
1755  * @cfg {Number} level (1|2|3|4|5|6) default 1
1756  * 
1757  * @constructor
1758  * Create a new Header
1759  * @param {Object} config The config object
1760  */
1761
1762
1763 Roo.bootstrap.Header  = function(config){
1764     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1765 };
1766
1767 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1768     
1769     //href : false,
1770     html : false,
1771     level : 1,
1772     
1773     
1774     
1775     getAutoCreate : function(){
1776         
1777         
1778         
1779         var cfg = {
1780             tag: 'h' + (1 *this.level),
1781             html: this.html || ''
1782         } ;
1783         
1784         return cfg;
1785     }
1786    
1787 });
1788
1789  
1790
1791  /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801  
1802 /**
1803  * @class Roo.bootstrap.MenuMgr
1804  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1805  * @singleton
1806  */
1807 Roo.bootstrap.MenuMgr = function(){
1808    var menus, active, groups = {}, attached = false, lastShow = new Date();
1809
1810    // private - called when first menu is created
1811    function init(){
1812        menus = {};
1813        active = new Roo.util.MixedCollection();
1814        Roo.get(document).addKeyListener(27, function(){
1815            if(active.length > 0){
1816                hideAll();
1817            }
1818        });
1819    }
1820
1821    // private
1822    function hideAll(){
1823        if(active && active.length > 0){
1824            var c = active.clone();
1825            c.each(function(m){
1826                m.hide();
1827            });
1828        }
1829    }
1830
1831    // private
1832    function onHide(m){
1833        active.remove(m);
1834        if(active.length < 1){
1835            Roo.get(document).un("mouseup", onMouseDown);
1836             
1837            attached = false;
1838        }
1839    }
1840
1841    // private
1842    function onShow(m){
1843        var last = active.last();
1844        lastShow = new Date();
1845        active.add(m);
1846        if(!attached){
1847           Roo.get(document).on("mouseup", onMouseDown);
1848            
1849            attached = true;
1850        }
1851        if(m.parentMenu){
1852           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1853           m.parentMenu.activeChild = m;
1854        }else if(last && last.isVisible()){
1855           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1856        }
1857    }
1858
1859    // private
1860    function onBeforeHide(m){
1861        if(m.activeChild){
1862            m.activeChild.hide();
1863        }
1864        if(m.autoHideTimer){
1865            clearTimeout(m.autoHideTimer);
1866            delete m.autoHideTimer;
1867        }
1868    }
1869
1870    // private
1871    function onBeforeShow(m){
1872        var pm = m.parentMenu;
1873        if(!pm && !m.allowOtherMenus){
1874            hideAll();
1875        }else if(pm && pm.activeChild && active != m){
1876            pm.activeChild.hide();
1877        }
1878    }
1879
1880    // private this should really trigger on mouseup..
1881    function onMouseDown(e){
1882         Roo.log("on Mouse Up");
1883         
1884         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1885             Roo.log("MenuManager hideAll");
1886             hideAll();
1887             e.stopEvent();
1888         }
1889         
1890         
1891    }
1892
1893    // private
1894    function onBeforeCheck(mi, state){
1895        if(state){
1896            var g = groups[mi.group];
1897            for(var i = 0, l = g.length; i < l; i++){
1898                if(g[i] != mi){
1899                    g[i].setChecked(false);
1900                }
1901            }
1902        }
1903    }
1904
1905    return {
1906
1907        /**
1908         * Hides all menus that are currently visible
1909         */
1910        hideAll : function(){
1911             hideAll();  
1912        },
1913
1914        // private
1915        register : function(menu){
1916            if(!menus){
1917                init();
1918            }
1919            menus[menu.id] = menu;
1920            menu.on("beforehide", onBeforeHide);
1921            menu.on("hide", onHide);
1922            menu.on("beforeshow", onBeforeShow);
1923            menu.on("show", onShow);
1924            var g = menu.group;
1925            if(g && menu.events["checkchange"]){
1926                if(!groups[g]){
1927                    groups[g] = [];
1928                }
1929                groups[g].push(menu);
1930                menu.on("checkchange", onCheck);
1931            }
1932        },
1933
1934         /**
1935          * Returns a {@link Roo.menu.Menu} object
1936          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1937          * be used to generate and return a new Menu instance.
1938          */
1939        get : function(menu){
1940            if(typeof menu == "string"){ // menu id
1941                return menus[menu];
1942            }else if(menu.events){  // menu instance
1943                return menu;
1944            }
1945            /*else if(typeof menu.length == 'number'){ // array of menu items?
1946                return new Roo.bootstrap.Menu({items:menu});
1947            }else{ // otherwise, must be a config
1948                return new Roo.bootstrap.Menu(menu);
1949            }
1950            */
1951            return false;
1952        },
1953
1954        // private
1955        unregister : function(menu){
1956            delete menus[menu.id];
1957            menu.un("beforehide", onBeforeHide);
1958            menu.un("hide", onHide);
1959            menu.un("beforeshow", onBeforeShow);
1960            menu.un("show", onShow);
1961            var g = menu.group;
1962            if(g && menu.events["checkchange"]){
1963                groups[g].remove(menu);
1964                menu.un("checkchange", onCheck);
1965            }
1966        },
1967
1968        // private
1969        registerCheckable : function(menuItem){
1970            var g = menuItem.group;
1971            if(g){
1972                if(!groups[g]){
1973                    groups[g] = [];
1974                }
1975                groups[g].push(menuItem);
1976                menuItem.on("beforecheckchange", onBeforeCheck);
1977            }
1978        },
1979
1980        // private
1981        unregisterCheckable : function(menuItem){
1982            var g = menuItem.group;
1983            if(g){
1984                groups[g].remove(menuItem);
1985                menuItem.un("beforecheckchange", onBeforeCheck);
1986            }
1987        }
1988    };
1989 }();/*
1990  * - LGPL
1991  *
1992  * menu
1993  * 
1994  */
1995
1996 /**
1997  * @class Roo.bootstrap.Menu
1998  * @extends Roo.bootstrap.Component
1999  * Bootstrap Menu class - container for MenuItems
2000  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2001  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2002  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2003  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2004  * 
2005  * @constructor
2006  * Create a new Menu
2007  * @param {Object} config The config object
2008  */
2009
2010
2011 Roo.bootstrap.Menu = function(config){
2012     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2013     if (this.registerMenu && this.type != 'treeview')  {
2014         Roo.bootstrap.MenuMgr.register(this);
2015     }
2016     
2017     
2018     this.addEvents({
2019         /**
2020          * @event beforeshow
2021          * Fires before this menu is displayed
2022          * @param {Roo.menu.Menu} this
2023          */
2024         beforeshow : true,
2025         /**
2026          * @event beforehide
2027          * Fires before this menu is hidden
2028          * @param {Roo.menu.Menu} this
2029          */
2030         beforehide : true,
2031         /**
2032          * @event show
2033          * Fires after this menu is displayed
2034          * @param {Roo.menu.Menu} this
2035          */
2036         show : true,
2037         /**
2038          * @event hide
2039          * Fires after this menu is hidden
2040          * @param {Roo.menu.Menu} this
2041          */
2042         hide : true,
2043         /**
2044          * @event click
2045          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2046          * @param {Roo.menu.Menu} this
2047          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2048          * @param {Roo.EventObject} e
2049          */
2050         click : true,
2051         /**
2052          * @event mouseover
2053          * Fires when the mouse is hovering over this menu
2054          * @param {Roo.menu.Menu} this
2055          * @param {Roo.EventObject} e
2056          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2057          */
2058         mouseover : true,
2059         /**
2060          * @event mouseout
2061          * Fires when the mouse exits this menu
2062          * @param {Roo.menu.Menu} this
2063          * @param {Roo.EventObject} e
2064          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2065          */
2066         mouseout : true,
2067         /**
2068          * @event itemclick
2069          * Fires when a menu item contained in this menu is clicked
2070          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2071          * @param {Roo.EventObject} e
2072          */
2073         itemclick: true
2074     });
2075     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2076 };
2077
2078 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2079     
2080    /// html : false,
2081     //align : '',
2082     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2083     type: false,
2084     /**
2085      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2086      */
2087     registerMenu : true,
2088     
2089     menuItems :false, // stores the menu items..
2090     
2091     hidden:true,
2092         
2093     parentMenu : false,
2094     
2095     stopEvent : true,
2096     
2097     isLink : false,
2098     
2099     getChildContainer : function() {
2100         return this.el;  
2101     },
2102     
2103     getAutoCreate : function(){
2104          
2105         //if (['right'].indexOf(this.align)!==-1) {
2106         //    cfg.cn[1].cls += ' pull-right'
2107         //}
2108         
2109         
2110         var cfg = {
2111             tag : 'ul',
2112             cls : 'dropdown-menu' ,
2113             style : 'z-index:1000'
2114             
2115         };
2116         
2117         if (this.type === 'submenu') {
2118             cfg.cls = 'submenu active';
2119         }
2120         if (this.type === 'treeview') {
2121             cfg.cls = 'treeview-menu';
2122         }
2123         
2124         return cfg;
2125     },
2126     initEvents : function() {
2127         
2128        // Roo.log("ADD event");
2129        // Roo.log(this.triggerEl.dom);
2130         
2131         this.triggerEl.on('click', this.onTriggerClick, this);
2132         
2133         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2134         
2135         
2136         if (this.triggerEl.hasClass('nav-item')) {
2137             // dropdown toggle on the 'a' in BS4?
2138             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2139         } else {
2140             this.triggerEl.addClass('dropdown-toggle');
2141         }
2142         if (Roo.isTouch) {
2143             this.el.on('touchstart'  , this.onTouch, this);
2144         }
2145         this.el.on('click' , this.onClick, this);
2146
2147         this.el.on("mouseover", this.onMouseOver, this);
2148         this.el.on("mouseout", this.onMouseOut, this);
2149         
2150     },
2151     
2152     findTargetItem : function(e)
2153     {
2154         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2155         if(!t){
2156             return false;
2157         }
2158         //Roo.log(t);         Roo.log(t.id);
2159         if(t && t.id){
2160             //Roo.log(this.menuitems);
2161             return this.menuitems.get(t.id);
2162             
2163             //return this.items.get(t.menuItemId);
2164         }
2165         
2166         return false;
2167     },
2168     
2169     onTouch : function(e) 
2170     {
2171         Roo.log("menu.onTouch");
2172         //e.stopEvent(); this make the user popdown broken
2173         this.onClick(e);
2174     },
2175     
2176     onClick : function(e)
2177     {
2178         Roo.log("menu.onClick");
2179         
2180         var t = this.findTargetItem(e);
2181         if(!t || t.isContainer){
2182             return;
2183         }
2184         Roo.log(e);
2185         /*
2186         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2187             if(t == this.activeItem && t.shouldDeactivate(e)){
2188                 this.activeItem.deactivate();
2189                 delete this.activeItem;
2190                 return;
2191             }
2192             if(t.canActivate){
2193                 this.setActiveItem(t, true);
2194             }
2195             return;
2196             
2197             
2198         }
2199         */
2200        
2201         Roo.log('pass click event');
2202         
2203         t.onClick(e);
2204         
2205         this.fireEvent("click", this, t, e);
2206         
2207         var _this = this;
2208         
2209         if(!t.href.length || t.href == '#'){
2210             (function() { _this.hide(); }).defer(100);
2211         }
2212         
2213     },
2214     
2215     onMouseOver : function(e){
2216         var t  = this.findTargetItem(e);
2217         //Roo.log(t);
2218         //if(t){
2219         //    if(t.canActivate && !t.disabled){
2220         //        this.setActiveItem(t, true);
2221         //    }
2222         //}
2223         
2224         this.fireEvent("mouseover", this, e, t);
2225     },
2226     isVisible : function(){
2227         return !this.hidden;
2228     },
2229      onMouseOut : function(e){
2230         var t  = this.findTargetItem(e);
2231         
2232         //if(t ){
2233         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2234         //        this.activeItem.deactivate();
2235         //        delete this.activeItem;
2236         //    }
2237         //}
2238         this.fireEvent("mouseout", this, e, t);
2239     },
2240     
2241     
2242     /**
2243      * Displays this menu relative to another element
2244      * @param {String/HTMLElement/Roo.Element} element The element to align to
2245      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2246      * the element (defaults to this.defaultAlign)
2247      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2248      */
2249     show : function(el, pos, parentMenu){
2250         this.parentMenu = parentMenu;
2251         if(!this.el){
2252             this.render();
2253         }
2254         this.fireEvent("beforeshow", this);
2255         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2256     },
2257      /**
2258      * Displays this menu at a specific xy position
2259      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2260      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2261      */
2262     showAt : function(xy, parentMenu, /* private: */_e){
2263         this.parentMenu = parentMenu;
2264         if(!this.el){
2265             this.render();
2266         }
2267         if(_e !== false){
2268             this.fireEvent("beforeshow", this);
2269             //xy = this.el.adjustForConstraints(xy);
2270         }
2271         
2272         //this.el.show();
2273         this.hideMenuItems();
2274         this.hidden = false;
2275         this.triggerEl.addClass('open');
2276         this.el.addClass('show');
2277         
2278         // reassign x when hitting right
2279         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2280             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2281         }
2282         
2283         // reassign y when hitting bottom
2284         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2285             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2286         }
2287         
2288         // but the list may align on trigger left or trigger top... should it be a properity?
2289         
2290         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2291             this.el.setXY(xy);
2292         }
2293         
2294         this.focus();
2295         this.fireEvent("show", this);
2296     },
2297     
2298     focus : function(){
2299         return;
2300         if(!this.hidden){
2301             this.doFocus.defer(50, this);
2302         }
2303     },
2304
2305     doFocus : function(){
2306         if(!this.hidden){
2307             this.focusEl.focus();
2308         }
2309     },
2310
2311     /**
2312      * Hides this menu and optionally all parent menus
2313      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2314      */
2315     hide : function(deep)
2316     {
2317         
2318         this.hideMenuItems();
2319         if(this.el && this.isVisible()){
2320             this.fireEvent("beforehide", this);
2321             if(this.activeItem){
2322                 this.activeItem.deactivate();
2323                 this.activeItem = null;
2324             }
2325             this.triggerEl.removeClass('open');;
2326             this.el.removeClass('show');
2327             this.hidden = true;
2328             this.fireEvent("hide", this);
2329         }
2330         if(deep === true && this.parentMenu){
2331             this.parentMenu.hide(true);
2332         }
2333     },
2334     
2335     onTriggerClick : function(e)
2336     {
2337         Roo.log('trigger click');
2338         
2339         var target = e.getTarget();
2340         
2341         Roo.log(target.nodeName.toLowerCase());
2342         
2343         if(target.nodeName.toLowerCase() === 'i'){
2344             e.preventDefault();
2345         }
2346         
2347     },
2348     
2349     onTriggerPress  : function(e)
2350     {
2351         Roo.log('trigger press');
2352         //Roo.log(e.getTarget());
2353        // Roo.log(this.triggerEl.dom);
2354        
2355         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2356         var pel = Roo.get(e.getTarget());
2357         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2358             Roo.log('is treeview or dropdown?');
2359             return;
2360         }
2361         
2362         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2363             return;
2364         }
2365         
2366         if (this.isVisible()) {
2367             Roo.log('hide');
2368             this.hide();
2369         } else {
2370             Roo.log('show');
2371             this.show(this.triggerEl, false, false);
2372         }
2373         
2374         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2375             e.stopEvent();
2376         }
2377         
2378     },
2379        
2380     
2381     hideMenuItems : function()
2382     {
2383         Roo.log("hide Menu Items");
2384         if (!this.el) { 
2385             return;
2386         }
2387         //$(backdrop).remove()
2388         this.el.select('.open',true).each(function(aa) {
2389             
2390             aa.removeClass('open');
2391           //var parent = getParent($(this))
2392           //var relatedTarget = { relatedTarget: this }
2393           
2394            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2395           //if (e.isDefaultPrevented()) return
2396            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2397         });
2398     },
2399     addxtypeChild : function (tree, cntr) {
2400         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2401           
2402         this.menuitems.add(comp);
2403         return comp;
2404
2405     },
2406     getEl : function()
2407     {
2408         Roo.log(this.el);
2409         return this.el;
2410     },
2411     
2412     clear : function()
2413     {
2414         this.getEl().dom.innerHTML = '';
2415         this.menuitems.clear();
2416     }
2417 });
2418
2419  
2420  /*
2421  * - LGPL
2422  *
2423  * menu item
2424  * 
2425  */
2426
2427
2428 /**
2429  * @class Roo.bootstrap.MenuItem
2430  * @extends Roo.bootstrap.Component
2431  * Bootstrap MenuItem class
2432  * @cfg {String} html the menu label
2433  * @cfg {String} href the link
2434  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2435  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2436  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2437  * @cfg {String} fa favicon to show on left of menu item.
2438  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2439  * 
2440  * 
2441  * @constructor
2442  * Create a new MenuItem
2443  * @param {Object} config The config object
2444  */
2445
2446
2447 Roo.bootstrap.MenuItem = function(config){
2448     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2449     this.addEvents({
2450         // raw events
2451         /**
2452          * @event click
2453          * The raw click event for the entire grid.
2454          * @param {Roo.bootstrap.MenuItem} this
2455          * @param {Roo.EventObject} e
2456          */
2457         "click" : true
2458     });
2459 };
2460
2461 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2462     
2463     href : false,
2464     html : false,
2465     preventDefault: false,
2466     isContainer : false,
2467     active : false,
2468     fa: false,
2469     
2470     getAutoCreate : function(){
2471         
2472         if(this.isContainer){
2473             return {
2474                 tag: 'li',
2475                 cls: 'dropdown-menu-item dropdown-item'
2476             };
2477         }
2478         var ctag = {
2479             tag: 'span',
2480             html: 'Link'
2481         };
2482         
2483         var anc = {
2484             tag : 'a',
2485             href : '#',
2486             cn : [  ]
2487         };
2488         
2489         if (this.fa !== false) {
2490             anc.cn.push({
2491                 tag : 'i',
2492                 cls : 'fa fa-' + this.fa
2493             });
2494         }
2495         
2496         anc.cn.push(ctag);
2497         
2498         
2499         var cfg= {
2500             tag: 'li',
2501             cls: 'dropdown-menu-item dropdown-item',
2502             cn: [ anc ]
2503         };
2504         if (this.parent().type == 'treeview') {
2505             cfg.cls = 'treeview-menu';
2506         }
2507         if (this.active) {
2508             cfg.cls += ' active';
2509         }
2510         
2511         
2512         
2513         anc.href = this.href || cfg.cn[0].href ;
2514         ctag.html = this.html || cfg.cn[0].html ;
2515         return cfg;
2516     },
2517     
2518     initEvents: function()
2519     {
2520         if (this.parent().type == 'treeview') {
2521             this.el.select('a').on('click', this.onClick, this);
2522         }
2523         
2524         if (this.menu) {
2525             this.menu.parentType = this.xtype;
2526             this.menu.triggerEl = this.el;
2527             this.menu = this.addxtype(Roo.apply({}, this.menu));
2528         }
2529         
2530     },
2531     onClick : function(e)
2532     {
2533         Roo.log('item on click ');
2534         
2535         if(this.preventDefault){
2536             e.preventDefault();
2537         }
2538         //this.parent().hideMenuItems();
2539         
2540         this.fireEvent('click', this, e);
2541     },
2542     getEl : function()
2543     {
2544         return this.el;
2545     } 
2546 });
2547
2548  
2549
2550  /*
2551  * - LGPL
2552  *
2553  * menu separator
2554  * 
2555  */
2556
2557
2558 /**
2559  * @class Roo.bootstrap.MenuSeparator
2560  * @extends Roo.bootstrap.Component
2561  * Bootstrap MenuSeparator class
2562  * 
2563  * @constructor
2564  * Create a new MenuItem
2565  * @param {Object} config The config object
2566  */
2567
2568
2569 Roo.bootstrap.MenuSeparator = function(config){
2570     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2571 };
2572
2573 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2574     
2575     getAutoCreate : function(){
2576         var cfg = {
2577             cls: 'divider',
2578             tag : 'li'
2579         };
2580         
2581         return cfg;
2582     }
2583    
2584 });
2585
2586  
2587
2588  
2589 /*
2590 * Licence: LGPL
2591 */
2592
2593 /**
2594  * @class Roo.bootstrap.Modal
2595  * @extends Roo.bootstrap.Component
2596  * Bootstrap Modal class
2597  * @cfg {String} title Title of dialog
2598  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2599  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2600  * @cfg {Boolean} specificTitle default false
2601  * @cfg {Array} buttons Array of buttons or standard button set..
2602  * @cfg {String} buttonPosition (left|right|center) default right
2603  * @cfg {Boolean} animate default true
2604  * @cfg {Boolean} allow_close default true
2605  * @cfg {Boolean} fitwindow default false
2606  * @cfg {String} size (sm|lg) default empty
2607  * @cfg {Number} max_width set the max width of modal
2608  *
2609  *
2610  * @constructor
2611  * Create a new Modal Dialog
2612  * @param {Object} config The config object
2613  */
2614
2615 Roo.bootstrap.Modal = function(config){
2616     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2617     this.addEvents({
2618         // raw events
2619         /**
2620          * @event btnclick
2621          * The raw btnclick event for the button
2622          * @param {Roo.EventObject} e
2623          */
2624         "btnclick" : true,
2625         /**
2626          * @event resize
2627          * Fire when dialog resize
2628          * @param {Roo.bootstrap.Modal} this
2629          * @param {Roo.EventObject} e
2630          */
2631         "resize" : true
2632     });
2633     this.buttons = this.buttons || [];
2634
2635     if (this.tmpl) {
2636         this.tmpl = Roo.factory(this.tmpl);
2637     }
2638
2639 };
2640
2641 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2642
2643     title : 'test dialog',
2644
2645     buttons : false,
2646
2647     // set on load...
2648
2649     html: false,
2650
2651     tmp: false,
2652
2653     specificTitle: false,
2654
2655     buttonPosition: 'right',
2656
2657     allow_close : true,
2658
2659     animate : true,
2660
2661     fitwindow: false,
2662     
2663      // private
2664     dialogEl: false,
2665     bodyEl:  false,
2666     footerEl:  false,
2667     titleEl:  false,
2668     closeEl:  false,
2669
2670     size: '',
2671     
2672     max_width: 0,
2673     
2674     max_height: 0,
2675     
2676     fit_content: false,
2677
2678     onRender : function(ct, position)
2679     {
2680         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2681
2682         if(!this.el){
2683             var cfg = Roo.apply({},  this.getAutoCreate());
2684             cfg.id = Roo.id();
2685             //if(!cfg.name){
2686             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2687             //}
2688             //if (!cfg.name.length) {
2689             //    delete cfg.name;
2690            // }
2691             if (this.cls) {
2692                 cfg.cls += ' ' + this.cls;
2693             }
2694             if (this.style) {
2695                 cfg.style = this.style;
2696             }
2697             this.el = Roo.get(document.body).createChild(cfg, position);
2698         }
2699         //var type = this.el.dom.type;
2700
2701
2702         if(this.tabIndex !== undefined){
2703             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2704         }
2705
2706         this.dialogEl = this.el.select('.modal-dialog',true).first();
2707         this.bodyEl = this.el.select('.modal-body',true).first();
2708         this.closeEl = this.el.select('.modal-header .close', true).first();
2709         this.headerEl = this.el.select('.modal-header',true).first();
2710         this.titleEl = this.el.select('.modal-title',true).first();
2711         this.footerEl = this.el.select('.modal-footer',true).first();
2712
2713         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2714         
2715         //this.el.addClass("x-dlg-modal");
2716
2717         if (this.buttons.length) {
2718             Roo.each(this.buttons, function(bb) {
2719                 var b = Roo.apply({}, bb);
2720                 b.xns = b.xns || Roo.bootstrap;
2721                 b.xtype = b.xtype || 'Button';
2722                 if (typeof(b.listeners) == 'undefined') {
2723                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2724                 }
2725
2726                 var btn = Roo.factory(b);
2727
2728                 btn.render(this.el.select('.modal-footer div').first());
2729
2730             },this);
2731         }
2732         // render the children.
2733         var nitems = [];
2734
2735         if(typeof(this.items) != 'undefined'){
2736             var items = this.items;
2737             delete this.items;
2738
2739             for(var i =0;i < items.length;i++) {
2740                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2741             }
2742         }
2743
2744         this.items = nitems;
2745
2746         // where are these used - they used to be body/close/footer
2747
2748
2749         this.initEvents();
2750         //this.el.addClass([this.fieldClass, this.cls]);
2751
2752     },
2753
2754     getAutoCreate : function()
2755     {
2756         var bdy = {
2757                 cls : 'modal-body',
2758                 html : this.html || ''
2759         };
2760
2761         var title = {
2762             tag: 'h4',
2763             cls : 'modal-title',
2764             html : this.title
2765         };
2766
2767         if(this.specificTitle){
2768             title = this.title;
2769
2770         };
2771
2772         var header = [];
2773         if (this.allow_close) {
2774             header.push({
2775                 tag: 'button',
2776                 cls : 'close',
2777                 html : '&times'
2778             });
2779         }
2780
2781         header.push(title);
2782
2783         var size = '';
2784
2785         if(this.size.length){
2786             size = 'modal-' + this.size;
2787         }
2788
2789         var modal = {
2790             cls: "modal",
2791              cn : [
2792                 {
2793                     cls: "modal-dialog " + size,
2794                     cn : [
2795                         {
2796                             cls : "modal-content",
2797                             cn : [
2798                                 {
2799                                     cls : 'modal-header',
2800                                     cn : header
2801                                 },
2802                                 bdy,
2803                                 {
2804                                     cls : 'modal-footer',
2805                                     cn : [
2806                                         {
2807                                             tag: 'div',
2808                                             cls: 'btn-' + this.buttonPosition
2809                                         }
2810                                     ]
2811
2812                                 }
2813
2814
2815                             ]
2816
2817                         }
2818                     ]
2819
2820                 }
2821             ]
2822         };
2823
2824         if(this.animate){
2825             modal.cls += ' fade';
2826         }
2827
2828         return modal;
2829
2830     },
2831     getChildContainer : function() {
2832
2833          return this.bodyEl;
2834
2835     },
2836     getButtonContainer : function() {
2837          return this.el.select('.modal-footer div',true).first();
2838
2839     },
2840     initEvents : function()
2841     {
2842         if (this.allow_close) {
2843             this.closeEl.on('click', this.hide, this);
2844         }
2845         Roo.EventManager.onWindowResize(this.resize, this, true);
2846
2847
2848     },
2849
2850     resize : function()
2851     {
2852         this.maskEl.setSize(
2853             Roo.lib.Dom.getViewWidth(true),
2854             Roo.lib.Dom.getViewHeight(true)
2855         );
2856         
2857         if (this.fitwindow) {
2858             this.setSize(
2859                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2860                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2861             );
2862             return;
2863         }
2864         
2865         if(this.max_width !== 0) {
2866             
2867             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2868             
2869             if(this.height) {
2870                 this.setSize(w, this.height);
2871                 return;
2872             }
2873             
2874             if(this.max_height) {
2875                 this.setSize(w,Math.min(
2876                     this.max_height,
2877                     Roo.lib.Dom.getViewportHeight(true) - 60
2878                 ));
2879                 
2880                 return;
2881             }
2882             
2883             if(!this.fit_content) {
2884                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2885                 return;
2886             }
2887             
2888             this.setSize(w, Math.min(
2889                 60 +
2890                 this.headerEl.getHeight() + 
2891                 this.footerEl.getHeight() + 
2892                 this.getChildHeight(this.bodyEl.dom.childNodes),
2893                 Roo.lib.Dom.getViewportHeight(true) - 60)
2894             );
2895         }
2896         
2897     },
2898
2899     setSize : function(w,h)
2900     {
2901         if (!w && !h) {
2902             return;
2903         }
2904         
2905         this.resizeTo(w,h);
2906     },
2907
2908     show : function() {
2909
2910         if (!this.rendered) {
2911             this.render();
2912         }
2913
2914         //this.el.setStyle('display', 'block');
2915         this.el.removeClass('hideing');        
2916         this.el.addClass('show');
2917  
2918         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2919             var _this = this;
2920             (function(){
2921                 this.el.addClass('in');
2922             }).defer(50, this);
2923         }else{
2924             this.el.addClass('in');
2925         }
2926
2927         // not sure how we can show data in here..
2928         //if (this.tmpl) {
2929         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2930         //}
2931
2932         Roo.get(document.body).addClass("x-body-masked");
2933         
2934         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2935         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2936         this.maskEl.addClass('show');
2937         
2938         this.resize();
2939         
2940         this.fireEvent('show', this);
2941
2942         // set zindex here - otherwise it appears to be ignored...
2943         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2944
2945         (function () {
2946             this.items.forEach( function(e) {
2947                 e.layout ? e.layout() : false;
2948
2949             });
2950         }).defer(100,this);
2951
2952     },
2953     hide : function()
2954     {
2955         if(this.fireEvent("beforehide", this) !== false){
2956             this.maskEl.removeClass('show');
2957             Roo.get(document.body).removeClass("x-body-masked");
2958             this.el.removeClass('in');
2959             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2960
2961             if(this.animate){ // why
2962                 this.el.addClass('hideing');
2963                 (function(){
2964                     if (!this.el.hasClass('hideing')) {
2965                         return; // it's been shown again...
2966                     }
2967                     this.el.removeClass('show');
2968                     this.el.removeClass('hideing');
2969                 }).defer(150,this);
2970                 
2971             }else{
2972                  this.el.removeClass('show');
2973             }
2974             this.fireEvent('hide', this);
2975         }
2976     },
2977     isVisible : function()
2978     {
2979         
2980         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2981         
2982     },
2983
2984     addButton : function(str, cb)
2985     {
2986
2987
2988         var b = Roo.apply({}, { html : str } );
2989         b.xns = b.xns || Roo.bootstrap;
2990         b.xtype = b.xtype || 'Button';
2991         if (typeof(b.listeners) == 'undefined') {
2992             b.listeners = { click : cb.createDelegate(this)  };
2993         }
2994
2995         var btn = Roo.factory(b);
2996
2997         btn.render(this.el.select('.modal-footer div').first());
2998
2999         return btn;
3000
3001     },
3002
3003     setDefaultButton : function(btn)
3004     {
3005         //this.el.select('.modal-footer').()
3006     },
3007     diff : false,
3008
3009     resizeTo: function(w,h)
3010     {
3011         // skip.. ?? why??
3012
3013         this.dialogEl.setWidth(w);
3014         if (this.diff === false) {
3015             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3016         }
3017
3018         this.bodyEl.setHeight(h - this.diff);
3019
3020         this.fireEvent('resize', this);
3021
3022     },
3023     setContentSize  : function(w, h)
3024     {
3025
3026     },
3027     onButtonClick: function(btn,e)
3028     {
3029         //Roo.log([a,b,c]);
3030         this.fireEvent('btnclick', btn.name, e);
3031     },
3032      /**
3033      * Set the title of the Dialog
3034      * @param {String} str new Title
3035      */
3036     setTitle: function(str) {
3037         this.titleEl.dom.innerHTML = str;
3038     },
3039     /**
3040      * Set the body of the Dialog
3041      * @param {String} str new Title
3042      */
3043     setBody: function(str) {
3044         this.bodyEl.dom.innerHTML = str;
3045     },
3046     /**
3047      * Set the body of the Dialog using the template
3048      * @param {Obj} data - apply this data to the template and replace the body contents.
3049      */
3050     applyBody: function(obj)
3051     {
3052         if (!this.tmpl) {
3053             Roo.log("Error - using apply Body without a template");
3054             //code
3055         }
3056         this.tmpl.overwrite(this.bodyEl, obj);
3057     },
3058     
3059     getChildHeight : function(child_nodes)
3060     {
3061         if(
3062             !child_nodes ||
3063             child_nodes.length == 0
3064         ) {
3065             return;
3066         }
3067         
3068         var child_height = 0;
3069         
3070         for(var i = 0; i < child_nodes.length; i++) {
3071             
3072             /*
3073             * for modal with tabs...
3074             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3075                 
3076                 var layout_childs = child_nodes[i].childNodes;
3077                 
3078                 for(var j = 0; j < layout_childs.length; j++) {
3079                     
3080                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3081                         
3082                         var layout_body_childs = layout_childs[j].childNodes;
3083                         
3084                         for(var k = 0; k < layout_body_childs.length; k++) {
3085                             
3086                             if(layout_body_childs[k].classList.contains('navbar')) {
3087                                 child_height += layout_body_childs[k].offsetHeight;
3088                                 continue;
3089                             }
3090                             
3091                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3092                                 
3093                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3094                                 
3095                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3096                                     
3097                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3098                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3099                                         continue;
3100                                     }
3101                                     
3102                                 }
3103                                 
3104                             }
3105                             
3106                         }
3107                     }
3108                 }
3109                 continue;
3110             }
3111             */
3112             
3113             child_height += child_nodes[i].offsetHeight;
3114             // Roo.log(child_nodes[i].offsetHeight);
3115         }
3116         
3117         return child_height;
3118     }
3119
3120 });
3121
3122
3123 Roo.apply(Roo.bootstrap.Modal,  {
3124     /**
3125          * Button config that displays a single OK button
3126          * @type Object
3127          */
3128         OK :  [{
3129             name : 'ok',
3130             weight : 'primary',
3131             html : 'OK'
3132         }],
3133         /**
3134          * Button config that displays Yes and No buttons
3135          * @type Object
3136          */
3137         YESNO : [
3138             {
3139                 name  : 'no',
3140                 html : 'No'
3141             },
3142             {
3143                 name  :'yes',
3144                 weight : 'primary',
3145                 html : 'Yes'
3146             }
3147         ],
3148
3149         /**
3150          * Button config that displays OK and Cancel buttons
3151          * @type Object
3152          */
3153         OKCANCEL : [
3154             {
3155                name : 'cancel',
3156                 html : 'Cancel'
3157             },
3158             {
3159                 name : 'ok',
3160                 weight : 'primary',
3161                 html : 'OK'
3162             }
3163         ],
3164         /**
3165          * Button config that displays Yes, No and Cancel buttons
3166          * @type Object
3167          */
3168         YESNOCANCEL : [
3169             {
3170                 name : 'yes',
3171                 weight : 'primary',
3172                 html : 'Yes'
3173             },
3174             {
3175                 name : 'no',
3176                 html : 'No'
3177             },
3178             {
3179                 name : 'cancel',
3180                 html : 'Cancel'
3181             }
3182         ],
3183         
3184         zIndex : 10001
3185 });
3186 /*
3187  * - LGPL
3188  *
3189  * messagebox - can be used as a replace
3190  * 
3191  */
3192 /**
3193  * @class Roo.MessageBox
3194  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3195  * Example usage:
3196  *<pre><code>
3197 // Basic alert:
3198 Roo.Msg.alert('Status', 'Changes saved successfully.');
3199
3200 // Prompt for user data:
3201 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3202     if (btn == 'ok'){
3203         // process text value...
3204     }
3205 });
3206
3207 // Show a dialog using config options:
3208 Roo.Msg.show({
3209    title:'Save Changes?',
3210    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3211    buttons: Roo.Msg.YESNOCANCEL,
3212    fn: processResult,
3213    animEl: 'elId'
3214 });
3215 </code></pre>
3216  * @singleton
3217  */
3218 Roo.bootstrap.MessageBox = function(){
3219     var dlg, opt, mask, waitTimer;
3220     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3221     var buttons, activeTextEl, bwidth;
3222
3223     
3224     // private
3225     var handleButton = function(button){
3226         dlg.hide();
3227         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3228     };
3229
3230     // private
3231     var handleHide = function(){
3232         if(opt && opt.cls){
3233             dlg.el.removeClass(opt.cls);
3234         }
3235         //if(waitTimer){
3236         //    Roo.TaskMgr.stop(waitTimer);
3237         //    waitTimer = null;
3238         //}
3239     };
3240
3241     // private
3242     var updateButtons = function(b){
3243         var width = 0;
3244         if(!b){
3245             buttons["ok"].hide();
3246             buttons["cancel"].hide();
3247             buttons["yes"].hide();
3248             buttons["no"].hide();
3249             //dlg.footer.dom.style.display = 'none';
3250             return width;
3251         }
3252         dlg.footerEl.dom.style.display = '';
3253         for(var k in buttons){
3254             if(typeof buttons[k] != "function"){
3255                 if(b[k]){
3256                     buttons[k].show();
3257                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3258                     width += buttons[k].el.getWidth()+15;
3259                 }else{
3260                     buttons[k].hide();
3261                 }
3262             }
3263         }
3264         return width;
3265     };
3266
3267     // private
3268     var handleEsc = function(d, k, e){
3269         if(opt && opt.closable !== false){
3270             dlg.hide();
3271         }
3272         if(e){
3273             e.stopEvent();
3274         }
3275     };
3276
3277     return {
3278         /**
3279          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3280          * @return {Roo.BasicDialog} The BasicDialog element
3281          */
3282         getDialog : function(){
3283            if(!dlg){
3284                 dlg = new Roo.bootstrap.Modal( {
3285                     //draggable: true,
3286                     //resizable:false,
3287                     //constraintoviewport:false,
3288                     //fixedcenter:true,
3289                     //collapsible : false,
3290                     //shim:true,
3291                     //modal: true,
3292                 //    width: 'auto',
3293                   //  height:100,
3294                     //buttonAlign:"center",
3295                     closeClick : function(){
3296                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3297                             handleButton("no");
3298                         }else{
3299                             handleButton("cancel");
3300                         }
3301                     }
3302                 });
3303                 dlg.render();
3304                 dlg.on("hide", handleHide);
3305                 mask = dlg.mask;
3306                 //dlg.addKeyListener(27, handleEsc);
3307                 buttons = {};
3308                 this.buttons = buttons;
3309                 var bt = this.buttonText;
3310                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3311                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3312                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3313                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3314                 //Roo.log(buttons);
3315                 bodyEl = dlg.bodyEl.createChild({
3316
3317                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3318                         '<textarea class="roo-mb-textarea"></textarea>' +
3319                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3320                 });
3321                 msgEl = bodyEl.dom.firstChild;
3322                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3323                 textboxEl.enableDisplayMode();
3324                 textboxEl.addKeyListener([10,13], function(){
3325                     if(dlg.isVisible() && opt && opt.buttons){
3326                         if(opt.buttons.ok){
3327                             handleButton("ok");
3328                         }else if(opt.buttons.yes){
3329                             handleButton("yes");
3330                         }
3331                     }
3332                 });
3333                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3334                 textareaEl.enableDisplayMode();
3335                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3336                 progressEl.enableDisplayMode();
3337                 
3338                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3339                 var pf = progressEl.dom.firstChild;
3340                 if (pf) {
3341                     pp = Roo.get(pf.firstChild);
3342                     pp.setHeight(pf.offsetHeight);
3343                 }
3344                 
3345             }
3346             return dlg;
3347         },
3348
3349         /**
3350          * Updates the message box body text
3351          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3352          * the XHTML-compliant non-breaking space character '&amp;#160;')
3353          * @return {Roo.MessageBox} This message box
3354          */
3355         updateText : function(text)
3356         {
3357             if(!dlg.isVisible() && !opt.width){
3358                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3359                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3360             }
3361             msgEl.innerHTML = text || '&#160;';
3362       
3363             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3364             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3365             var w = Math.max(
3366                     Math.min(opt.width || cw , this.maxWidth), 
3367                     Math.max(opt.minWidth || this.minWidth, bwidth)
3368             );
3369             if(opt.prompt){
3370                 activeTextEl.setWidth(w);
3371             }
3372             if(dlg.isVisible()){
3373                 dlg.fixedcenter = false;
3374             }
3375             // to big, make it scroll. = But as usual stupid IE does not support
3376             // !important..
3377             
3378             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3379                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3380                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3381             } else {
3382                 bodyEl.dom.style.height = '';
3383                 bodyEl.dom.style.overflowY = '';
3384             }
3385             if (cw > w) {
3386                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3387             } else {
3388                 bodyEl.dom.style.overflowX = '';
3389             }
3390             
3391             dlg.setContentSize(w, bodyEl.getHeight());
3392             if(dlg.isVisible()){
3393                 dlg.fixedcenter = true;
3394             }
3395             return this;
3396         },
3397
3398         /**
3399          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3400          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3401          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3402          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3403          * @return {Roo.MessageBox} This message box
3404          */
3405         updateProgress : function(value, text){
3406             if(text){
3407                 this.updateText(text);
3408             }
3409             
3410             if (pp) { // weird bug on my firefox - for some reason this is not defined
3411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3412                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3413             }
3414             return this;
3415         },        
3416
3417         /**
3418          * Returns true if the message box is currently displayed
3419          * @return {Boolean} True if the message box is visible, else false
3420          */
3421         isVisible : function(){
3422             return dlg && dlg.isVisible();  
3423         },
3424
3425         /**
3426          * Hides the message box if it is displayed
3427          */
3428         hide : function(){
3429             if(this.isVisible()){
3430                 dlg.hide();
3431             }  
3432         },
3433
3434         /**
3435          * Displays a new message box, or reinitializes an existing message box, based on the config options
3436          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3437          * The following config object properties are supported:
3438          * <pre>
3439 Property    Type             Description
3440 ----------  ---------------  ------------------------------------------------------------------------------------
3441 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3442                                    closes (defaults to undefined)
3443 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3444                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3445 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3446                                    progress and wait dialogs will ignore this property and always hide the
3447                                    close button as they can only be closed programmatically.
3448 cls               String           A custom CSS class to apply to the message box element
3449 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3450                                    displayed (defaults to 75)
3451 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3452                                    function will be btn (the name of the button that was clicked, if applicable,
3453                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3454                                    Progress and wait dialogs will ignore this option since they do not respond to
3455                                    user actions and can only be closed programmatically, so any required function
3456                                    should be called by the same code after it closes the dialog.
3457 icon              String           A CSS class that provides a background image to be used as an icon for
3458                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3459 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3460 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3461 modal             Boolean          False to allow user interaction with the page while the message box is
3462                                    displayed (defaults to true)
3463 msg               String           A string that will replace the existing message box body text (defaults
3464                                    to the XHTML-compliant non-breaking space character '&#160;')
3465 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3466 progress          Boolean          True to display a progress bar (defaults to false)
3467 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3468 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3469 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3470 title             String           The title text
3471 value             String           The string value to set into the active textbox element if displayed
3472 wait              Boolean          True to display a progress bar (defaults to false)
3473 width             Number           The width of the dialog in pixels
3474 </pre>
3475          *
3476          * Example usage:
3477          * <pre><code>
3478 Roo.Msg.show({
3479    title: 'Address',
3480    msg: 'Please enter your address:',
3481    width: 300,
3482    buttons: Roo.MessageBox.OKCANCEL,
3483    multiline: true,
3484    fn: saveAddress,
3485    animEl: 'addAddressBtn'
3486 });
3487 </code></pre>
3488          * @param {Object} config Configuration options
3489          * @return {Roo.MessageBox} This message box
3490          */
3491         show : function(options)
3492         {
3493             
3494             // this causes nightmares if you show one dialog after another
3495             // especially on callbacks..
3496              
3497             if(this.isVisible()){
3498                 
3499                 this.hide();
3500                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3501                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3502                 Roo.log("New Dialog Message:" +  options.msg )
3503                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3504                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3505                 
3506             }
3507             var d = this.getDialog();
3508             opt = options;
3509             d.setTitle(opt.title || "&#160;");
3510             d.closeEl.setDisplayed(opt.closable !== false);
3511             activeTextEl = textboxEl;
3512             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3513             if(opt.prompt){
3514                 if(opt.multiline){
3515                     textboxEl.hide();
3516                     textareaEl.show();
3517                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3518                         opt.multiline : this.defaultTextHeight);
3519                     activeTextEl = textareaEl;
3520                 }else{
3521                     textboxEl.show();
3522                     textareaEl.hide();
3523                 }
3524             }else{
3525                 textboxEl.hide();
3526                 textareaEl.hide();
3527             }
3528             progressEl.setDisplayed(opt.progress === true);
3529             this.updateProgress(0);
3530             activeTextEl.dom.value = opt.value || "";
3531             if(opt.prompt){
3532                 dlg.setDefaultButton(activeTextEl);
3533             }else{
3534                 var bs = opt.buttons;
3535                 var db = null;
3536                 if(bs && bs.ok){
3537                     db = buttons["ok"];
3538                 }else if(bs && bs.yes){
3539                     db = buttons["yes"];
3540                 }
3541                 dlg.setDefaultButton(db);
3542             }
3543             bwidth = updateButtons(opt.buttons);
3544             this.updateText(opt.msg);
3545             if(opt.cls){
3546                 d.el.addClass(opt.cls);
3547             }
3548             d.proxyDrag = opt.proxyDrag === true;
3549             d.modal = opt.modal !== false;
3550             d.mask = opt.modal !== false ? mask : false;
3551             if(!d.isVisible()){
3552                 // force it to the end of the z-index stack so it gets a cursor in FF
3553                 document.body.appendChild(dlg.el.dom);
3554                 d.animateTarget = null;
3555                 d.show(options.animEl);
3556             }
3557             return this;
3558         },
3559
3560         /**
3561          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3562          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3563          * and closing the message box when the process is complete.
3564          * @param {String} title The title bar text
3565          * @param {String} msg The message box body text
3566          * @return {Roo.MessageBox} This message box
3567          */
3568         progress : function(title, msg){
3569             this.show({
3570                 title : title,
3571                 msg : msg,
3572                 buttons: false,
3573                 progress:true,
3574                 closable:false,
3575                 minWidth: this.minProgressWidth,
3576                 modal : true
3577             });
3578             return this;
3579         },
3580
3581         /**
3582          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3583          * If a callback function is passed it will be called after the user clicks the button, and the
3584          * id of the button that was clicked will be passed as the only parameter to the callback
3585          * (could also be the top-right close button).
3586          * @param {String} title The title bar text
3587          * @param {String} msg The message box body text
3588          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3589          * @param {Object} scope (optional) The scope of the callback function
3590          * @return {Roo.MessageBox} This message box
3591          */
3592         alert : function(title, msg, fn, scope)
3593         {
3594             this.show({
3595                 title : title,
3596                 msg : msg,
3597                 buttons: this.OK,
3598                 fn: fn,
3599                 closable : false,
3600                 scope : scope,
3601                 modal : true
3602             });
3603             return this;
3604         },
3605
3606         /**
3607          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3608          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3609          * You are responsible for closing the message box when the process is complete.
3610          * @param {String} msg The message box body text
3611          * @param {String} title (optional) The title bar text
3612          * @return {Roo.MessageBox} This message box
3613          */
3614         wait : function(msg, title){
3615             this.show({
3616                 title : title,
3617                 msg : msg,
3618                 buttons: false,
3619                 closable:false,
3620                 progress:true,
3621                 modal:true,
3622                 width:300,
3623                 wait:true
3624             });
3625             waitTimer = Roo.TaskMgr.start({
3626                 run: function(i){
3627                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3628                 },
3629                 interval: 1000
3630             });
3631             return this;
3632         },
3633
3634         /**
3635          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3636          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3637          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3638          * @param {String} title The title bar text
3639          * @param {String} msg The message box body text
3640          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3641          * @param {Object} scope (optional) The scope of the callback function
3642          * @return {Roo.MessageBox} This message box
3643          */
3644         confirm : function(title, msg, fn, scope){
3645             this.show({
3646                 title : title,
3647                 msg : msg,
3648                 buttons: this.YESNO,
3649                 fn: fn,
3650                 scope : scope,
3651                 modal : true
3652             });
3653             return this;
3654         },
3655
3656         /**
3657          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3658          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3659          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3660          * (could also be the top-right close button) and the text that was entered will be passed as the two
3661          * parameters to the callback.
3662          * @param {String} title The title bar text
3663          * @param {String} msg The message box body text
3664          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3665          * @param {Object} scope (optional) The scope of the callback function
3666          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3667          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3668          * @return {Roo.MessageBox} This message box
3669          */
3670         prompt : function(title, msg, fn, scope, multiline){
3671             this.show({
3672                 title : title,
3673                 msg : msg,
3674                 buttons: this.OKCANCEL,
3675                 fn: fn,
3676                 minWidth:250,
3677                 scope : scope,
3678                 prompt:true,
3679                 multiline: multiline,
3680                 modal : true
3681             });
3682             return this;
3683         },
3684
3685         /**
3686          * Button config that displays a single OK button
3687          * @type Object
3688          */
3689         OK : {ok:true},
3690         /**
3691          * Button config that displays Yes and No buttons
3692          * @type Object
3693          */
3694         YESNO : {yes:true, no:true},
3695         /**
3696          * Button config that displays OK and Cancel buttons
3697          * @type Object
3698          */
3699         OKCANCEL : {ok:true, cancel:true},
3700         /**
3701          * Button config that displays Yes, No and Cancel buttons
3702          * @type Object
3703          */
3704         YESNOCANCEL : {yes:true, no:true, cancel:true},
3705
3706         /**
3707          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3708          * @type Number
3709          */
3710         defaultTextHeight : 75,
3711         /**
3712          * The maximum width in pixels of the message box (defaults to 600)
3713          * @type Number
3714          */
3715         maxWidth : 600,
3716         /**
3717          * The minimum width in pixels of the message box (defaults to 100)
3718          * @type Number
3719          */
3720         minWidth : 100,
3721         /**
3722          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3723          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3724          * @type Number
3725          */
3726         minProgressWidth : 250,
3727         /**
3728          * An object containing the default button text strings that can be overriden for localized language support.
3729          * Supported properties are: ok, cancel, yes and no.
3730          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3731          * @type Object
3732          */
3733         buttonText : {
3734             ok : "OK",
3735             cancel : "Cancel",
3736             yes : "Yes",
3737             no : "No"
3738         }
3739     };
3740 }();
3741
3742 /**
3743  * Shorthand for {@link Roo.MessageBox}
3744  */
3745 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3746 Roo.Msg = Roo.Msg || Roo.MessageBox;
3747 /*
3748  * - LGPL
3749  *
3750  * navbar
3751  * 
3752  */
3753
3754 /**
3755  * @class Roo.bootstrap.Navbar
3756  * @extends Roo.bootstrap.Component
3757  * Bootstrap Navbar class
3758
3759  * @constructor
3760  * Create a new Navbar
3761  * @param {Object} config The config object
3762  */
3763
3764
3765 Roo.bootstrap.Navbar = function(config){
3766     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3767     this.addEvents({
3768         // raw events
3769         /**
3770          * @event beforetoggle
3771          * Fire before toggle the menu
3772          * @param {Roo.EventObject} e
3773          */
3774         "beforetoggle" : true
3775     });
3776 };
3777
3778 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3779     
3780     
3781    
3782     // private
3783     navItems : false,
3784     loadMask : false,
3785     
3786     
3787     getAutoCreate : function(){
3788         
3789         
3790         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3791         
3792     },
3793     
3794     initEvents :function ()
3795     {
3796         //Roo.log(this.el.select('.navbar-toggle',true));
3797         this.el.select('.navbar-toggle',true).on('click', function() {
3798             if(this.fireEvent('beforetoggle', this) !== false){
3799                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3800             }
3801             
3802         }, this);
3803         
3804         var mark = {
3805             tag: "div",
3806             cls:"x-dlg-mask"
3807         };
3808         
3809         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3810         
3811         var size = this.el.getSize();
3812         this.maskEl.setSize(size.width, size.height);
3813         this.maskEl.enableDisplayMode("block");
3814         this.maskEl.hide();
3815         
3816         if(this.loadMask){
3817             this.maskEl.show();
3818         }
3819     },
3820     
3821     
3822     getChildContainer : function()
3823     {
3824         if (this.el.select('.collapse').getCount()) {
3825             return this.el.select('.collapse',true).first();
3826         }
3827         
3828         return this.el;
3829     },
3830     
3831     mask : function()
3832     {
3833         this.maskEl.show();
3834     },
3835     
3836     unmask : function()
3837     {
3838         this.maskEl.hide();
3839     } 
3840     
3841     
3842     
3843     
3844 });
3845
3846
3847
3848  
3849
3850  /*
3851  * - LGPL
3852  *
3853  * navbar
3854  * 
3855  */
3856
3857 /**
3858  * @class Roo.bootstrap.NavSimplebar
3859  * @extends Roo.bootstrap.Navbar
3860  * Bootstrap Sidebar class
3861  *
3862  * @cfg {Boolean} inverse is inverted color
3863  * 
3864  * @cfg {String} type (nav | pills | tabs)
3865  * @cfg {Boolean} arrangement stacked | justified
3866  * @cfg {String} align (left | right) alignment
3867  * 
3868  * @cfg {Boolean} main (true|false) main nav bar? default false
3869  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3870  * 
3871  * @cfg {String} tag (header|footer|nav|div) default is nav 
3872
3873  * 
3874  * 
3875  * 
3876  * @constructor
3877  * Create a new Sidebar
3878  * @param {Object} config The config object
3879  */
3880
3881
3882 Roo.bootstrap.NavSimplebar = function(config){
3883     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3884 };
3885
3886 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3887     
3888     inverse: false,
3889     
3890     type: false,
3891     arrangement: '',
3892     align : false,
3893     
3894     
3895     
3896     main : false,
3897     
3898     
3899     tag : false,
3900     
3901     
3902     getAutoCreate : function(){
3903         
3904         
3905         var cfg = {
3906             tag : this.tag || 'div',
3907             cls : 'navbar'
3908         };
3909           
3910         
3911         cfg.cn = [
3912             {
3913                 cls: 'nav',
3914                 tag : 'ul'
3915             }
3916         ];
3917         
3918          
3919         this.type = this.type || 'nav';
3920         if (['tabs','pills'].indexOf(this.type)!==-1) {
3921             cfg.cn[0].cls += ' nav-' + this.type
3922         
3923         
3924         } else {
3925             if (this.type!=='nav') {
3926                 Roo.log('nav type must be nav/tabs/pills')
3927             }
3928             cfg.cn[0].cls += ' navbar-nav'
3929         }
3930         
3931         
3932         
3933         
3934         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3935             cfg.cn[0].cls += ' nav-' + this.arrangement;
3936         }
3937         
3938         
3939         if (this.align === 'right') {
3940             cfg.cn[0].cls += ' navbar-right';
3941         }
3942         
3943         if (this.inverse) {
3944             cfg.cls += ' navbar-inverse';
3945             
3946         }
3947         
3948         
3949         return cfg;
3950     
3951         
3952     }
3953     
3954     
3955     
3956 });
3957
3958
3959
3960  
3961
3962  
3963        /*
3964  * - LGPL
3965  *
3966  * navbar
3967  * navbar-fixed-top
3968  * navbar-expand-md  fixed-top 
3969  */
3970
3971 /**
3972  * @class Roo.bootstrap.NavHeaderbar
3973  * @extends Roo.bootstrap.NavSimplebar
3974  * Bootstrap Sidebar class
3975  *
3976  * @cfg {String} brand what is brand
3977  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3978  * @cfg {String} brand_href href of the brand
3979  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3980  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3981  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3982  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3983  * 
3984  * @constructor
3985  * Create a new Sidebar
3986  * @param {Object} config The config object
3987  */
3988
3989
3990 Roo.bootstrap.NavHeaderbar = function(config){
3991     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3992       
3993 };
3994
3995 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3996     
3997     position: '',
3998     brand: '',
3999     brand_href: false,
4000     srButton : true,
4001     autohide : false,
4002     desktopCenter : false,
4003    
4004     
4005     getAutoCreate : function(){
4006         
4007         var   cfg = {
4008             tag: this.nav || 'nav',
4009             cls: 'navbar navbar-expand-md',
4010             role: 'navigation',
4011             cn: []
4012         };
4013         
4014         var cn = cfg.cn;
4015         if (this.desktopCenter) {
4016             cn.push({cls : 'container', cn : []});
4017             cn = cn[0].cn;
4018         }
4019         
4020         if(this.srButton){
4021             cn.push({
4022                 tag: 'div',
4023                 cls: 'navbar-header',
4024                 cn: [
4025                     {
4026                         tag: 'button',
4027                         type: 'button',
4028                         cls: 'navbar-toggle navbar-toggler',
4029                         'data-toggle': 'collapse',
4030                         cn: [
4031                             {
4032                                 tag: 'span',
4033                                 cls: 'sr-only',
4034                                 html: 'Toggle navigation'
4035                             },
4036                             {
4037                                 tag: 'span',
4038                                 cls: 'icon-bar navbar-toggler-icon'
4039                             },
4040                             {
4041                                 tag: 'span',
4042                                 cls: 'icon-bar'
4043                             },
4044                             {
4045                                 tag: 'span',
4046                                 cls: 'icon-bar'
4047                             }
4048                         ]
4049                     }
4050                 ]
4051             });
4052         }
4053         
4054         cn.push({
4055             tag: 'div',
4056             cls: 'collapse navbar-collapse',
4057             cn : []
4058         });
4059         
4060         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4061         
4062         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4063             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4064             
4065             // tag can override this..
4066             
4067             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4068         }
4069         
4070         if (this.brand !== '') {
4071             cn[0].cn.push({
4072                 tag: 'a',
4073                 href: this.brand_href ? this.brand_href : '#',
4074                 cls: 'navbar-brand',
4075                 cn: [
4076                 this.brand
4077                 ]
4078             });
4079         }
4080         
4081         if(this.main){
4082             cfg.cls += ' main-nav';
4083         }
4084         
4085         
4086         return cfg;
4087
4088         
4089     },
4090     getHeaderChildContainer : function()
4091     {
4092         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4093             return this.el.select('.navbar-header',true).first();
4094         }
4095         
4096         return this.getChildContainer();
4097     },
4098     
4099     
4100     initEvents : function()
4101     {
4102         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4103         
4104         if (this.autohide) {
4105             
4106             var prevScroll = 0;
4107             var ft = this.el;
4108             
4109             Roo.get(document).on('scroll',function(e) {
4110                 var ns = Roo.get(document).getScroll().top;
4111                 var os = prevScroll;
4112                 prevScroll = ns;
4113                 
4114                 if(ns > os){
4115                     ft.removeClass('slideDown');
4116                     ft.addClass('slideUp');
4117                     return;
4118                 }
4119                 ft.removeClass('slideUp');
4120                 ft.addClass('slideDown');
4121                  
4122               
4123           },this);
4124         }
4125     }    
4126     
4127 });
4128
4129
4130
4131  
4132
4133  /*
4134  * - LGPL
4135  *
4136  * navbar
4137  * 
4138  */
4139
4140 /**
4141  * @class Roo.bootstrap.NavSidebar
4142  * @extends Roo.bootstrap.Navbar
4143  * Bootstrap Sidebar class
4144  * 
4145  * @constructor
4146  * Create a new Sidebar
4147  * @param {Object} config The config object
4148  */
4149
4150
4151 Roo.bootstrap.NavSidebar = function(config){
4152     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4153 };
4154
4155 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4156     
4157     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4158     
4159     getAutoCreate : function(){
4160         
4161         
4162         return  {
4163             tag: 'div',
4164             cls: 'sidebar sidebar-nav'
4165         };
4166     
4167         
4168     }
4169     
4170     
4171     
4172 });
4173
4174
4175
4176  
4177
4178  /*
4179  * - LGPL
4180  *
4181  * nav group
4182  * 
4183  */
4184
4185 /**
4186  * @class Roo.bootstrap.NavGroup
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap NavGroup class
4189  * @cfg {String} align (left|right)
4190  * @cfg {Boolean} inverse
4191  * @cfg {String} type (nav|pills|tab) default nav
4192  * @cfg {String} navId - reference Id for navbar.
4193
4194  * 
4195  * @constructor
4196  * Create a new nav group
4197  * @param {Object} config The config object
4198  */
4199
4200 Roo.bootstrap.NavGroup = function(config){
4201     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4202     this.navItems = [];
4203    
4204     Roo.bootstrap.NavGroup.register(this);
4205      this.addEvents({
4206         /**
4207              * @event changed
4208              * Fires when the active item changes
4209              * @param {Roo.bootstrap.NavGroup} this
4210              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4211              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4212          */
4213         'changed': true
4214      });
4215     
4216 };
4217
4218 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4219     
4220     align: '',
4221     inverse: false,
4222     form: false,
4223     type: 'nav',
4224     navId : '',
4225     // private
4226     
4227     navItems : false, 
4228     
4229     getAutoCreate : function()
4230     {
4231         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4232         
4233         cfg = {
4234             tag : 'ul',
4235             cls: 'nav' 
4236         };
4237         
4238         if (['tabs','pills'].indexOf(this.type)!==-1) {
4239             cfg.cls += ' nav-' + this.type
4240         } else {
4241             if (this.type!=='nav') {
4242                 Roo.log('nav type must be nav/tabs/pills')
4243             }
4244             cfg.cls += ' navbar-nav'
4245         }
4246         
4247         if (this.parent() && this.parent().sidebar) {
4248             cfg = {
4249                 tag: 'ul',
4250                 cls: 'dashboard-menu sidebar-menu'
4251             };
4252             
4253             return cfg;
4254         }
4255         
4256         if (this.form === true) {
4257             cfg = {
4258                 tag: 'form',
4259                 cls: 'navbar-form'
4260             };
4261             
4262             if (this.align === 'right') {
4263                 cfg.cls += ' navbar-right ml-md-auto';
4264             } else {
4265                 cfg.cls += ' navbar-left';
4266             }
4267         }
4268         
4269         if (this.align === 'right') {
4270             cfg.cls += ' navbar-right ml-md-auto';
4271         } else {
4272             cfg.cls += ' mr-auto';
4273         }
4274         
4275         if (this.inverse) {
4276             cfg.cls += ' navbar-inverse';
4277             
4278         }
4279         
4280         
4281         return cfg;
4282     },
4283     /**
4284     * sets the active Navigation item
4285     * @param {Roo.bootstrap.NavItem} the new current navitem
4286     */
4287     setActiveItem : function(item)
4288     {
4289         var prev = false;
4290         Roo.each(this.navItems, function(v){
4291             if (v == item) {
4292                 return ;
4293             }
4294             if (v.isActive()) {
4295                 v.setActive(false, true);
4296                 prev = v;
4297                 
4298             }
4299             
4300         });
4301
4302         item.setActive(true, true);
4303         this.fireEvent('changed', this, item, prev);
4304         
4305         
4306     },
4307     /**
4308     * gets the active Navigation item
4309     * @return {Roo.bootstrap.NavItem} the current navitem
4310     */
4311     getActive : function()
4312     {
4313         
4314         var prev = false;
4315         Roo.each(this.navItems, function(v){
4316             
4317             if (v.isActive()) {
4318                 prev = v;
4319                 
4320             }
4321             
4322         });
4323         return prev;
4324     },
4325     
4326     indexOfNav : function()
4327     {
4328         
4329         var prev = false;
4330         Roo.each(this.navItems, function(v,i){
4331             
4332             if (v.isActive()) {
4333                 prev = i;
4334                 
4335             }
4336             
4337         });
4338         return prev;
4339     },
4340     /**
4341     * adds a Navigation item
4342     * @param {Roo.bootstrap.NavItem} the navitem to add
4343     */
4344     addItem : function(cfg)
4345     {
4346         var cn = new Roo.bootstrap.NavItem(cfg);
4347         this.register(cn);
4348         cn.parentId = this.id;
4349         cn.onRender(this.el, null);
4350         return cn;
4351     },
4352     /**
4353     * register a Navigation item
4354     * @param {Roo.bootstrap.NavItem} the navitem to add
4355     */
4356     register : function(item)
4357     {
4358         this.navItems.push( item);
4359         item.navId = this.navId;
4360     
4361     },
4362     
4363     /**
4364     * clear all the Navigation item
4365     */
4366    
4367     clearAll : function()
4368     {
4369         this.navItems = [];
4370         this.el.dom.innerHTML = '';
4371     },
4372     
4373     getNavItem: function(tabId)
4374     {
4375         var ret = false;
4376         Roo.each(this.navItems, function(e) {
4377             if (e.tabId == tabId) {
4378                ret =  e;
4379                return false;
4380             }
4381             return true;
4382             
4383         });
4384         return ret;
4385     },
4386     
4387     setActiveNext : function()
4388     {
4389         var i = this.indexOfNav(this.getActive());
4390         if (i > this.navItems.length) {
4391             return;
4392         }
4393         this.setActiveItem(this.navItems[i+1]);
4394     },
4395     setActivePrev : function()
4396     {
4397         var i = this.indexOfNav(this.getActive());
4398         if (i  < 1) {
4399             return;
4400         }
4401         this.setActiveItem(this.navItems[i-1]);
4402     },
4403     clearWasActive : function(except) {
4404         Roo.each(this.navItems, function(e) {
4405             if (e.tabId != except.tabId && e.was_active) {
4406                e.was_active = false;
4407                return false;
4408             }
4409             return true;
4410             
4411         });
4412     },
4413     getWasActive : function ()
4414     {
4415         var r = false;
4416         Roo.each(this.navItems, function(e) {
4417             if (e.was_active) {
4418                r = e;
4419                return false;
4420             }
4421             return true;
4422             
4423         });
4424         return r;
4425     }
4426     
4427     
4428 });
4429
4430  
4431 Roo.apply(Roo.bootstrap.NavGroup, {
4432     
4433     groups: {},
4434      /**
4435     * register a Navigation Group
4436     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4437     */
4438     register : function(navgrp)
4439     {
4440         this.groups[navgrp.navId] = navgrp;
4441         
4442     },
4443     /**
4444     * fetch a Navigation Group based on the navigation ID
4445     * @param {string} the navgroup to add
4446     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4447     */
4448     get: function(navId) {
4449         if (typeof(this.groups[navId]) == 'undefined') {
4450             return false;
4451             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4452         }
4453         return this.groups[navId] ;
4454     }
4455     
4456     
4457     
4458 });
4459
4460  /*
4461  * - LGPL
4462  *
4463  * row
4464  * 
4465  */
4466
4467 /**
4468  * @class Roo.bootstrap.NavItem
4469  * @extends Roo.bootstrap.Component
4470  * Bootstrap Navbar.NavItem class
4471  * @cfg {String} href  link to
4472  * @cfg {String} html content of button
4473  * @cfg {String} badge text inside badge
4474  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4475  * @cfg {String} glyphicon name of glyphicon
4476  * @cfg {String} icon name of font awesome icon
4477  * @cfg {Boolean} active Is item active
4478  * @cfg {Boolean} disabled Is item disabled
4479  
4480  * @cfg {Boolean} preventDefault (true | false) default false
4481  * @cfg {String} tabId the tab that this item activates.
4482  * @cfg {String} tagtype (a|span) render as a href or span?
4483  * @cfg {Boolean} animateRef (true|false) link to element default false  
4484   
4485  * @constructor
4486  * Create a new Navbar Item
4487  * @param {Object} config The config object
4488  */
4489 Roo.bootstrap.NavItem = function(config){
4490     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4491     this.addEvents({
4492         // raw events
4493         /**
4494          * @event click
4495          * The raw click event for the entire grid.
4496          * @param {Roo.EventObject} e
4497          */
4498         "click" : true,
4499          /**
4500             * @event changed
4501             * Fires when the active item active state changes
4502             * @param {Roo.bootstrap.NavItem} this
4503             * @param {boolean} state the new state
4504              
4505          */
4506         'changed': true,
4507         /**
4508             * @event scrollto
4509             * Fires when scroll to element
4510             * @param {Roo.bootstrap.NavItem} this
4511             * @param {Object} options
4512             * @param {Roo.EventObject} e
4513              
4514          */
4515         'scrollto': true
4516     });
4517    
4518 };
4519
4520 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4521     
4522     href: false,
4523     html: '',
4524     badge: '',
4525     icon: false,
4526     glyphicon: false,
4527     active: false,
4528     preventDefault : false,
4529     tabId : false,
4530     tagtype : 'a',
4531     disabled : false,
4532     animateRef : false,
4533     was_active : false,
4534     
4535     getAutoCreate : function(){
4536          
4537         var cfg = {
4538             tag: 'li',
4539             cls: 'nav-item'
4540             
4541         };
4542         
4543         if (this.active) {
4544             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4545         }
4546         if (this.disabled) {
4547             cfg.cls += ' disabled';
4548         }
4549         
4550         if (this.href || this.html || this.glyphicon || this.icon) {
4551             cfg.cn = [
4552                 {
4553                     tag: this.tagtype,
4554                     href : this.href || "#",
4555                     html: this.html || ''
4556                 }
4557             ];
4558             if (this.tagtype == 'a') {
4559                 cfg.cn[0].cls = 'nav-link';
4560             }
4561             if (this.icon) {
4562                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4563             }
4564
4565             if(this.glyphicon) {
4566                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4567             }
4568             
4569             if (this.menu) {
4570                 
4571                 cfg.cn[0].html += " <span class='caret'></span>";
4572              
4573             }
4574             
4575             if (this.badge !== '') {
4576                  
4577                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4578             }
4579         }
4580         
4581         
4582         
4583         return cfg;
4584     },
4585     initEvents: function() 
4586     {
4587         if (typeof (this.menu) != 'undefined') {
4588             this.menu.parentType = this.xtype;
4589             this.menu.triggerEl = this.el;
4590             this.menu = this.addxtype(Roo.apply({}, this.menu));
4591         }
4592         
4593         this.el.select('a',true).on('click', this.onClick, this);
4594         
4595         if(this.tagtype == 'span'){
4596             this.el.select('span',true).on('click', this.onClick, this);
4597         }
4598        
4599         // at this point parent should be available..
4600         this.parent().register(this);
4601     },
4602     
4603     onClick : function(e)
4604     {
4605         if (e.getTarget('.dropdown-menu-item')) {
4606             // did you click on a menu itemm.... - then don't trigger onclick..
4607             return;
4608         }
4609         
4610         if(
4611                 this.preventDefault || 
4612                 this.href == '#' 
4613         ){
4614             Roo.log("NavItem - prevent Default?");
4615             e.preventDefault();
4616         }
4617         
4618         if (this.disabled) {
4619             return;
4620         }
4621         
4622         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4623         if (tg && tg.transition) {
4624             Roo.log("waiting for the transitionend");
4625             return;
4626         }
4627         
4628         
4629         
4630         //Roo.log("fire event clicked");
4631         if(this.fireEvent('click', this, e) === false){
4632             return;
4633         };
4634         
4635         if(this.tagtype == 'span'){
4636             return;
4637         }
4638         
4639         //Roo.log(this.href);
4640         var ael = this.el.select('a',true).first();
4641         //Roo.log(ael);
4642         
4643         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4644             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4645             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4646                 return; // ignore... - it's a 'hash' to another page.
4647             }
4648             Roo.log("NavItem - prevent Default?");
4649             e.preventDefault();
4650             this.scrollToElement(e);
4651         }
4652         
4653         
4654         var p =  this.parent();
4655    
4656         if (['tabs','pills'].indexOf(p.type)!==-1) {
4657             if (typeof(p.setActiveItem) !== 'undefined') {
4658                 p.setActiveItem(this);
4659             }
4660         }
4661         
4662         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4663         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4664             // remove the collapsed menu expand...
4665             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4666         }
4667     },
4668     
4669     isActive: function () {
4670         return this.active
4671     },
4672     setActive : function(state, fire, is_was_active)
4673     {
4674         if (this.active && !state && this.navId) {
4675             this.was_active = true;
4676             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4677             if (nv) {
4678                 nv.clearWasActive(this);
4679             }
4680             
4681         }
4682         this.active = state;
4683         
4684         if (!state ) {
4685             this.el.removeClass('active');
4686         } else if (!this.el.hasClass('active')) {
4687             this.el.addClass('active');
4688         }
4689         if (fire) {
4690             this.fireEvent('changed', this, state);
4691         }
4692         
4693         // show a panel if it's registered and related..
4694         
4695         if (!this.navId || !this.tabId || !state || is_was_active) {
4696             return;
4697         }
4698         
4699         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4700         if (!tg) {
4701             return;
4702         }
4703         var pan = tg.getPanelByName(this.tabId);
4704         if (!pan) {
4705             return;
4706         }
4707         // if we can not flip to new panel - go back to old nav highlight..
4708         if (false == tg.showPanel(pan)) {
4709             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4710             if (nv) {
4711                 var onav = nv.getWasActive();
4712                 if (onav) {
4713                     onav.setActive(true, false, true);
4714                 }
4715             }
4716             
4717         }
4718         
4719         
4720         
4721     },
4722      // this should not be here...
4723     setDisabled : function(state)
4724     {
4725         this.disabled = state;
4726         if (!state ) {
4727             this.el.removeClass('disabled');
4728         } else if (!this.el.hasClass('disabled')) {
4729             this.el.addClass('disabled');
4730         }
4731         
4732     },
4733     
4734     /**
4735      * Fetch the element to display the tooltip on.
4736      * @return {Roo.Element} defaults to this.el
4737      */
4738     tooltipEl : function()
4739     {
4740         return this.el.select('' + this.tagtype + '', true).first();
4741     },
4742     
4743     scrollToElement : function(e)
4744     {
4745         var c = document.body;
4746         
4747         /*
4748          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4749          */
4750         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4751             c = document.documentElement;
4752         }
4753         
4754         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4755         
4756         if(!target){
4757             return;
4758         }
4759
4760         var o = target.calcOffsetsTo(c);
4761         
4762         var options = {
4763             target : target,
4764             value : o[1]
4765         };
4766         
4767         this.fireEvent('scrollto', this, options, e);
4768         
4769         Roo.get(c).scrollTo('top', options.value, true);
4770         
4771         return;
4772     }
4773 });
4774  
4775
4776  /*
4777  * - LGPL
4778  *
4779  * sidebar item
4780  *
4781  *  li
4782  *    <span> icon </span>
4783  *    <span> text </span>
4784  *    <span>badge </span>
4785  */
4786
4787 /**
4788  * @class Roo.bootstrap.NavSidebarItem
4789  * @extends Roo.bootstrap.NavItem
4790  * Bootstrap Navbar.NavSidebarItem class
4791  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4792  * {Boolean} open is the menu open
4793  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4794  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4795  * {String} buttonSize (sm|md|lg)the extra classes for the button
4796  * {Boolean} showArrow show arrow next to the text (default true)
4797  * @constructor
4798  * Create a new Navbar Button
4799  * @param {Object} config The config object
4800  */
4801 Roo.bootstrap.NavSidebarItem = function(config){
4802     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4803     this.addEvents({
4804         // raw events
4805         /**
4806          * @event click
4807          * The raw click event for the entire grid.
4808          * @param {Roo.EventObject} e
4809          */
4810         "click" : true,
4811          /**
4812             * @event changed
4813             * Fires when the active item active state changes
4814             * @param {Roo.bootstrap.NavSidebarItem} this
4815             * @param {boolean} state the new state
4816              
4817          */
4818         'changed': true
4819     });
4820    
4821 };
4822
4823 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4824     
4825     badgeWeight : 'default',
4826     
4827     open: false,
4828     
4829     buttonView : false,
4830     
4831     buttonWeight : 'default',
4832     
4833     buttonSize : 'md',
4834     
4835     showArrow : true,
4836     
4837     getAutoCreate : function(){
4838         
4839         
4840         var a = {
4841                 tag: 'a',
4842                 href : this.href || '#',
4843                 cls: '',
4844                 html : '',
4845                 cn : []
4846         };
4847         
4848         if(this.buttonView){
4849             a = {
4850                 tag: 'button',
4851                 href : this.href || '#',
4852                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4853                 html : this.html,
4854                 cn : []
4855             };
4856         }
4857         
4858         var cfg = {
4859             tag: 'li',
4860             cls: '',
4861             cn: [ a ]
4862         };
4863         
4864         if (this.active) {
4865             cfg.cls += ' active';
4866         }
4867         
4868         if (this.disabled) {
4869             cfg.cls += ' disabled';
4870         }
4871         if (this.open) {
4872             cfg.cls += ' open x-open';
4873         }
4874         // left icon..
4875         if (this.glyphicon || this.icon) {
4876             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4877             a.cn.push({ tag : 'i', cls : c }) ;
4878         }
4879         
4880         if(!this.buttonView){
4881             var span = {
4882                 tag: 'span',
4883                 html : this.html || ''
4884             };
4885
4886             a.cn.push(span);
4887             
4888         }
4889         
4890         if (this.badge !== '') {
4891             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4892         }
4893         
4894         if (this.menu) {
4895             
4896             if(this.showArrow){
4897                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4898             }
4899             
4900             a.cls += ' dropdown-toggle treeview' ;
4901         }
4902         
4903         return cfg;
4904     },
4905     
4906     initEvents : function()
4907     { 
4908         if (typeof (this.menu) != 'undefined') {
4909             this.menu.parentType = this.xtype;
4910             this.menu.triggerEl = this.el;
4911             this.menu = this.addxtype(Roo.apply({}, this.menu));
4912         }
4913         
4914         this.el.on('click', this.onClick, this);
4915         
4916         if(this.badge !== ''){
4917             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4918         }
4919         
4920     },
4921     
4922     onClick : function(e)
4923     {
4924         if(this.disabled){
4925             e.preventDefault();
4926             return;
4927         }
4928         
4929         if(this.preventDefault){
4930             e.preventDefault();
4931         }
4932         
4933         this.fireEvent('click', this);
4934     },
4935     
4936     disable : function()
4937     {
4938         this.setDisabled(true);
4939     },
4940     
4941     enable : function()
4942     {
4943         this.setDisabled(false);
4944     },
4945     
4946     setDisabled : function(state)
4947     {
4948         if(this.disabled == state){
4949             return;
4950         }
4951         
4952         this.disabled = state;
4953         
4954         if (state) {
4955             this.el.addClass('disabled');
4956             return;
4957         }
4958         
4959         this.el.removeClass('disabled');
4960         
4961         return;
4962     },
4963     
4964     setActive : function(state)
4965     {
4966         if(this.active == state){
4967             return;
4968         }
4969         
4970         this.active = state;
4971         
4972         if (state) {
4973             this.el.addClass('active');
4974             return;
4975         }
4976         
4977         this.el.removeClass('active');
4978         
4979         return;
4980     },
4981     
4982     isActive: function () 
4983     {
4984         return this.active;
4985     },
4986     
4987     setBadge : function(str)
4988     {
4989         if(!this.badgeEl){
4990             return;
4991         }
4992         
4993         this.badgeEl.dom.innerHTML = str;
4994     }
4995     
4996    
4997      
4998  
4999 });
5000  
5001
5002  /*
5003  * - LGPL
5004  *
5005  * row
5006  * 
5007  */
5008
5009 /**
5010  * @class Roo.bootstrap.Row
5011  * @extends Roo.bootstrap.Component
5012  * Bootstrap Row class (contains columns...)
5013  * 
5014  * @constructor
5015  * Create a new Row
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Row = function(config){
5020     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5021 };
5022
5023 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5024     
5025     getAutoCreate : function(){
5026        return {
5027             cls: 'row clearfix'
5028        };
5029     }
5030     
5031     
5032 });
5033
5034  
5035
5036  /*
5037  * - LGPL
5038  *
5039  * element
5040  * 
5041  */
5042
5043 /**
5044  * @class Roo.bootstrap.Element
5045  * @extends Roo.bootstrap.Component
5046  * Bootstrap Element class
5047  * @cfg {String} html contents of the element
5048  * @cfg {String} tag tag of the element
5049  * @cfg {String} cls class of the element
5050  * @cfg {Boolean} preventDefault (true|false) default false
5051  * @cfg {Boolean} clickable (true|false) default false
5052  * 
5053  * @constructor
5054  * Create a new Element
5055  * @param {Object} config The config object
5056  */
5057
5058 Roo.bootstrap.Element = function(config){
5059     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5060     
5061     this.addEvents({
5062         // raw events
5063         /**
5064          * @event click
5065          * When a element is chick
5066          * @param {Roo.bootstrap.Element} this
5067          * @param {Roo.EventObject} e
5068          */
5069         "click" : true
5070     });
5071 };
5072
5073 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5074     
5075     tag: 'div',
5076     cls: '',
5077     html: '',
5078     preventDefault: false, 
5079     clickable: false,
5080     
5081     getAutoCreate : function(){
5082         
5083         var cfg = {
5084             tag: this.tag,
5085             // cls: this.cls, double assign in parent class Component.js :: onRender
5086             html: this.html
5087         };
5088         
5089         return cfg;
5090     },
5091     
5092     initEvents: function() 
5093     {
5094         Roo.bootstrap.Element.superclass.initEvents.call(this);
5095         
5096         if(this.clickable){
5097             this.el.on('click', this.onClick, this);
5098         }
5099         
5100     },
5101     
5102     onClick : function(e)
5103     {
5104         if(this.preventDefault){
5105             e.preventDefault();
5106         }
5107         
5108         this.fireEvent('click', this, e);
5109     },
5110     
5111     getValue : function()
5112     {
5113         return this.el.dom.innerHTML;
5114     },
5115     
5116     setValue : function(value)
5117     {
5118         this.el.dom.innerHTML = value;
5119     }
5120    
5121 });
5122
5123  
5124
5125  /*
5126  * - LGPL
5127  *
5128  * pagination
5129  * 
5130  */
5131
5132 /**
5133  * @class Roo.bootstrap.Pagination
5134  * @extends Roo.bootstrap.Component
5135  * Bootstrap Pagination class
5136  * @cfg {String} size xs | sm | md | lg
5137  * @cfg {Boolean} inverse false | true
5138  * 
5139  * @constructor
5140  * Create a new Pagination
5141  * @param {Object} config The config object
5142  */
5143
5144 Roo.bootstrap.Pagination = function(config){
5145     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5146 };
5147
5148 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5149     
5150     cls: false,
5151     size: false,
5152     inverse: false,
5153     
5154     getAutoCreate : function(){
5155         var cfg = {
5156             tag: 'ul',
5157                 cls: 'pagination'
5158         };
5159         if (this.inverse) {
5160             cfg.cls += ' inverse';
5161         }
5162         if (this.html) {
5163             cfg.html=this.html;
5164         }
5165         if (this.cls) {
5166             cfg.cls += " " + this.cls;
5167         }
5168         return cfg;
5169     }
5170    
5171 });
5172
5173  
5174
5175  /*
5176  * - LGPL
5177  *
5178  * Pagination item
5179  * 
5180  */
5181
5182
5183 /**
5184  * @class Roo.bootstrap.PaginationItem
5185  * @extends Roo.bootstrap.Component
5186  * Bootstrap PaginationItem class
5187  * @cfg {String} html text
5188  * @cfg {String} href the link
5189  * @cfg {Boolean} preventDefault (true | false) default true
5190  * @cfg {Boolean} active (true | false) default false
5191  * @cfg {Boolean} disabled default false
5192  * 
5193  * 
5194  * @constructor
5195  * Create a new PaginationItem
5196  * @param {Object} config The config object
5197  */
5198
5199
5200 Roo.bootstrap.PaginationItem = function(config){
5201     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5202     this.addEvents({
5203         // raw events
5204         /**
5205          * @event click
5206          * The raw click event for the entire grid.
5207          * @param {Roo.EventObject} e
5208          */
5209         "click" : true
5210     });
5211 };
5212
5213 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5214     
5215     href : false,
5216     html : false,
5217     preventDefault: true,
5218     active : false,
5219     cls : false,
5220     disabled: false,
5221     
5222     getAutoCreate : function(){
5223         var cfg= {
5224             tag: 'li',
5225             cn: [
5226                 {
5227                     tag : 'a',
5228                     href : this.href ? this.href : '#',
5229                     html : this.html ? this.html : ''
5230                 }
5231             ]
5232         };
5233         
5234         if(this.cls){
5235             cfg.cls = this.cls;
5236         }
5237         
5238         if(this.disabled){
5239             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5240         }
5241         
5242         if(this.active){
5243             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5244         }
5245         
5246         return cfg;
5247     },
5248     
5249     initEvents: function() {
5250         
5251         this.el.on('click', this.onClick, this);
5252         
5253     },
5254     onClick : function(e)
5255     {
5256         Roo.log('PaginationItem on click ');
5257         if(this.preventDefault){
5258             e.preventDefault();
5259         }
5260         
5261         if(this.disabled){
5262             return;
5263         }
5264         
5265         this.fireEvent('click', this, e);
5266     }
5267    
5268 });
5269
5270  
5271
5272  /*
5273  * - LGPL
5274  *
5275  * slider
5276  * 
5277  */
5278
5279
5280 /**
5281  * @class Roo.bootstrap.Slider
5282  * @extends Roo.bootstrap.Component
5283  * Bootstrap Slider class
5284  *    
5285  * @constructor
5286  * Create a new Slider
5287  * @param {Object} config The config object
5288  */
5289
5290 Roo.bootstrap.Slider = function(config){
5291     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5292 };
5293
5294 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5295     
5296     getAutoCreate : function(){
5297         
5298         var cfg = {
5299             tag: 'div',
5300             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5301             cn: [
5302                 {
5303                     tag: 'a',
5304                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5305                 }
5306             ]
5307         };
5308         
5309         return cfg;
5310     }
5311    
5312 });
5313
5314  /*
5315  * Based on:
5316  * Ext JS Library 1.1.1
5317  * Copyright(c) 2006-2007, Ext JS, LLC.
5318  *
5319  * Originally Released Under LGPL - original licence link has changed is not relivant.
5320  *
5321  * Fork - LGPL
5322  * <script type="text/javascript">
5323  */
5324  
5325
5326 /**
5327  * @class Roo.grid.ColumnModel
5328  * @extends Roo.util.Observable
5329  * This is the default implementation of a ColumnModel used by the Grid. It defines
5330  * the columns in the grid.
5331  * <br>Usage:<br>
5332  <pre><code>
5333  var colModel = new Roo.grid.ColumnModel([
5334         {header: "Ticker", width: 60, sortable: true, locked: true},
5335         {header: "Company Name", width: 150, sortable: true},
5336         {header: "Market Cap.", width: 100, sortable: true},
5337         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5338         {header: "Employees", width: 100, sortable: true, resizable: false}
5339  ]);
5340  </code></pre>
5341  * <p>
5342  
5343  * The config options listed for this class are options which may appear in each
5344  * individual column definition.
5345  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5346  * @constructor
5347  * @param {Object} config An Array of column config objects. See this class's
5348  * config objects for details.
5349 */
5350 Roo.grid.ColumnModel = function(config){
5351         /**
5352      * The config passed into the constructor
5353      */
5354     this.config = config;
5355     this.lookup = {};
5356
5357     // if no id, create one
5358     // if the column does not have a dataIndex mapping,
5359     // map it to the order it is in the config
5360     for(var i = 0, len = config.length; i < len; i++){
5361         var c = config[i];
5362         if(typeof c.dataIndex == "undefined"){
5363             c.dataIndex = i;
5364         }
5365         if(typeof c.renderer == "string"){
5366             c.renderer = Roo.util.Format[c.renderer];
5367         }
5368         if(typeof c.id == "undefined"){
5369             c.id = Roo.id();
5370         }
5371         if(c.editor && c.editor.xtype){
5372             c.editor  = Roo.factory(c.editor, Roo.grid);
5373         }
5374         if(c.editor && c.editor.isFormField){
5375             c.editor = new Roo.grid.GridEditor(c.editor);
5376         }
5377         this.lookup[c.id] = c;
5378     }
5379
5380     /**
5381      * The width of columns which have no width specified (defaults to 100)
5382      * @type Number
5383      */
5384     this.defaultWidth = 100;
5385
5386     /**
5387      * Default sortable of columns which have no sortable specified (defaults to false)
5388      * @type Boolean
5389      */
5390     this.defaultSortable = false;
5391
5392     this.addEvents({
5393         /**
5394              * @event widthchange
5395              * Fires when the width of a column changes.
5396              * @param {ColumnModel} this
5397              * @param {Number} columnIndex The column index
5398              * @param {Number} newWidth The new width
5399              */
5400             "widthchange": true,
5401         /**
5402              * @event headerchange
5403              * Fires when the text of a header changes.
5404              * @param {ColumnModel} this
5405              * @param {Number} columnIndex The column index
5406              * @param {Number} newText The new header text
5407              */
5408             "headerchange": true,
5409         /**
5410              * @event hiddenchange
5411              * Fires when a column is hidden or "unhidden".
5412              * @param {ColumnModel} this
5413              * @param {Number} columnIndex The column index
5414              * @param {Boolean} hidden true if hidden, false otherwise
5415              */
5416             "hiddenchange": true,
5417             /**
5418          * @event columnmoved
5419          * Fires when a column is moved.
5420          * @param {ColumnModel} this
5421          * @param {Number} oldIndex
5422          * @param {Number} newIndex
5423          */
5424         "columnmoved" : true,
5425         /**
5426          * @event columlockchange
5427          * Fires when a column's locked state is changed
5428          * @param {ColumnModel} this
5429          * @param {Number} colIndex
5430          * @param {Boolean} locked true if locked
5431          */
5432         "columnlockchange" : true
5433     });
5434     Roo.grid.ColumnModel.superclass.constructor.call(this);
5435 };
5436 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5437     /**
5438      * @cfg {String} header The header text to display in the Grid view.
5439      */
5440     /**
5441      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5442      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5443      * specified, the column's index is used as an index into the Record's data Array.
5444      */
5445     /**
5446      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5447      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5448      */
5449     /**
5450      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5451      * Defaults to the value of the {@link #defaultSortable} property.
5452      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5453      */
5454     /**
5455      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5456      */
5457     /**
5458      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5459      */
5460     /**
5461      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5462      */
5463     /**
5464      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5465      */
5466     /**
5467      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5468      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5469      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5470      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5471      */
5472        /**
5473      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5474      */
5475     /**
5476      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5477      */
5478     /**
5479      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5480      */
5481     /**
5482      * @cfg {String} cursor (Optional)
5483      */
5484     /**
5485      * @cfg {String} tooltip (Optional)
5486      */
5487     /**
5488      * @cfg {Number} xs (Optional)
5489      */
5490     /**
5491      * @cfg {Number} sm (Optional)
5492      */
5493     /**
5494      * @cfg {Number} md (Optional)
5495      */
5496     /**
5497      * @cfg {Number} lg (Optional)
5498      */
5499     /**
5500      * Returns the id of the column at the specified index.
5501      * @param {Number} index The column index
5502      * @return {String} the id
5503      */
5504     getColumnId : function(index){
5505         return this.config[index].id;
5506     },
5507
5508     /**
5509      * Returns the column for a specified id.
5510      * @param {String} id The column id
5511      * @return {Object} the column
5512      */
5513     getColumnById : function(id){
5514         return this.lookup[id];
5515     },
5516
5517     
5518     /**
5519      * Returns the column for a specified dataIndex.
5520      * @param {String} dataIndex The column dataIndex
5521      * @return {Object|Boolean} the column or false if not found
5522      */
5523     getColumnByDataIndex: function(dataIndex){
5524         var index = this.findColumnIndex(dataIndex);
5525         return index > -1 ? this.config[index] : false;
5526     },
5527     
5528     /**
5529      * Returns the index for a specified column id.
5530      * @param {String} id The column id
5531      * @return {Number} the index, or -1 if not found
5532      */
5533     getIndexById : function(id){
5534         for(var i = 0, len = this.config.length; i < len; i++){
5535             if(this.config[i].id == id){
5536                 return i;
5537             }
5538         }
5539         return -1;
5540     },
5541     
5542     /**
5543      * Returns the index for a specified column dataIndex.
5544      * @param {String} dataIndex The column dataIndex
5545      * @return {Number} the index, or -1 if not found
5546      */
5547     
5548     findColumnIndex : function(dataIndex){
5549         for(var i = 0, len = this.config.length; i < len; i++){
5550             if(this.config[i].dataIndex == dataIndex){
5551                 return i;
5552             }
5553         }
5554         return -1;
5555     },
5556     
5557     
5558     moveColumn : function(oldIndex, newIndex){
5559         var c = this.config[oldIndex];
5560         this.config.splice(oldIndex, 1);
5561         this.config.splice(newIndex, 0, c);
5562         this.dataMap = null;
5563         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5564     },
5565
5566     isLocked : function(colIndex){
5567         return this.config[colIndex].locked === true;
5568     },
5569
5570     setLocked : function(colIndex, value, suppressEvent){
5571         if(this.isLocked(colIndex) == value){
5572             return;
5573         }
5574         this.config[colIndex].locked = value;
5575         if(!suppressEvent){
5576             this.fireEvent("columnlockchange", this, colIndex, value);
5577         }
5578     },
5579
5580     getTotalLockedWidth : function(){
5581         var totalWidth = 0;
5582         for(var i = 0; i < this.config.length; i++){
5583             if(this.isLocked(i) && !this.isHidden(i)){
5584                 this.totalWidth += this.getColumnWidth(i);
5585             }
5586         }
5587         return totalWidth;
5588     },
5589
5590     getLockedCount : function(){
5591         for(var i = 0, len = this.config.length; i < len; i++){
5592             if(!this.isLocked(i)){
5593                 return i;
5594             }
5595         }
5596         
5597         return this.config.length;
5598     },
5599
5600     /**
5601      * Returns the number of columns.
5602      * @return {Number}
5603      */
5604     getColumnCount : function(visibleOnly){
5605         if(visibleOnly === true){
5606             var c = 0;
5607             for(var i = 0, len = this.config.length; i < len; i++){
5608                 if(!this.isHidden(i)){
5609                     c++;
5610                 }
5611             }
5612             return c;
5613         }
5614         return this.config.length;
5615     },
5616
5617     /**
5618      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5619      * @param {Function} fn
5620      * @param {Object} scope (optional)
5621      * @return {Array} result
5622      */
5623     getColumnsBy : function(fn, scope){
5624         var r = [];
5625         for(var i = 0, len = this.config.length; i < len; i++){
5626             var c = this.config[i];
5627             if(fn.call(scope||this, c, i) === true){
5628                 r[r.length] = c;
5629             }
5630         }
5631         return r;
5632     },
5633
5634     /**
5635      * Returns true if the specified column is sortable.
5636      * @param {Number} col The column index
5637      * @return {Boolean}
5638      */
5639     isSortable : function(col){
5640         if(typeof this.config[col].sortable == "undefined"){
5641             return this.defaultSortable;
5642         }
5643         return this.config[col].sortable;
5644     },
5645
5646     /**
5647      * Returns the rendering (formatting) function defined for the column.
5648      * @param {Number} col The column index.
5649      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5650      */
5651     getRenderer : function(col){
5652         if(!this.config[col].renderer){
5653             return Roo.grid.ColumnModel.defaultRenderer;
5654         }
5655         return this.config[col].renderer;
5656     },
5657
5658     /**
5659      * Sets the rendering (formatting) function for a column.
5660      * @param {Number} col The column index
5661      * @param {Function} fn The function to use to process the cell's raw data
5662      * to return HTML markup for the grid view. The render function is called with
5663      * the following parameters:<ul>
5664      * <li>Data value.</li>
5665      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5666      * <li>css A CSS style string to apply to the table cell.</li>
5667      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5668      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5669      * <li>Row index</li>
5670      * <li>Column index</li>
5671      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5672      */
5673     setRenderer : function(col, fn){
5674         this.config[col].renderer = fn;
5675     },
5676
5677     /**
5678      * Returns the width for the specified column.
5679      * @param {Number} col The column index
5680      * @return {Number}
5681      */
5682     getColumnWidth : function(col){
5683         return this.config[col].width * 1 || this.defaultWidth;
5684     },
5685
5686     /**
5687      * Sets the width for a column.
5688      * @param {Number} col The column index
5689      * @param {Number} width The new width
5690      */
5691     setColumnWidth : function(col, width, suppressEvent){
5692         this.config[col].width = width;
5693         this.totalWidth = null;
5694         if(!suppressEvent){
5695              this.fireEvent("widthchange", this, col, width);
5696         }
5697     },
5698
5699     /**
5700      * Returns the total width of all columns.
5701      * @param {Boolean} includeHidden True to include hidden column widths
5702      * @return {Number}
5703      */
5704     getTotalWidth : function(includeHidden){
5705         if(!this.totalWidth){
5706             this.totalWidth = 0;
5707             for(var i = 0, len = this.config.length; i < len; i++){
5708                 if(includeHidden || !this.isHidden(i)){
5709                     this.totalWidth += this.getColumnWidth(i);
5710                 }
5711             }
5712         }
5713         return this.totalWidth;
5714     },
5715
5716     /**
5717      * Returns the header for the specified column.
5718      * @param {Number} col The column index
5719      * @return {String}
5720      */
5721     getColumnHeader : function(col){
5722         return this.config[col].header;
5723     },
5724
5725     /**
5726      * Sets the header for a column.
5727      * @param {Number} col The column index
5728      * @param {String} header The new header
5729      */
5730     setColumnHeader : function(col, header){
5731         this.config[col].header = header;
5732         this.fireEvent("headerchange", this, col, header);
5733     },
5734
5735     /**
5736      * Returns the tooltip for the specified column.
5737      * @param {Number} col The column index
5738      * @return {String}
5739      */
5740     getColumnTooltip : function(col){
5741             return this.config[col].tooltip;
5742     },
5743     /**
5744      * Sets the tooltip for a column.
5745      * @param {Number} col The column index
5746      * @param {String} tooltip The new tooltip
5747      */
5748     setColumnTooltip : function(col, tooltip){
5749             this.config[col].tooltip = tooltip;
5750     },
5751
5752     /**
5753      * Returns the dataIndex for the specified column.
5754      * @param {Number} col The column index
5755      * @return {Number}
5756      */
5757     getDataIndex : function(col){
5758         return this.config[col].dataIndex;
5759     },
5760
5761     /**
5762      * Sets the dataIndex for a column.
5763      * @param {Number} col The column index
5764      * @param {Number} dataIndex The new dataIndex
5765      */
5766     setDataIndex : function(col, dataIndex){
5767         this.config[col].dataIndex = dataIndex;
5768     },
5769
5770     
5771     
5772     /**
5773      * Returns true if the cell is editable.
5774      * @param {Number} colIndex The column index
5775      * @param {Number} rowIndex The row index - this is nto actually used..?
5776      * @return {Boolean}
5777      */
5778     isCellEditable : function(colIndex, rowIndex){
5779         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5780     },
5781
5782     /**
5783      * Returns the editor defined for the cell/column.
5784      * return false or null to disable editing.
5785      * @param {Number} colIndex The column index
5786      * @param {Number} rowIndex The row index
5787      * @return {Object}
5788      */
5789     getCellEditor : function(colIndex, rowIndex){
5790         return this.config[colIndex].editor;
5791     },
5792
5793     /**
5794      * Sets if a column is editable.
5795      * @param {Number} col The column index
5796      * @param {Boolean} editable True if the column is editable
5797      */
5798     setEditable : function(col, editable){
5799         this.config[col].editable = editable;
5800     },
5801
5802
5803     /**
5804      * Returns true if the column is hidden.
5805      * @param {Number} colIndex The column index
5806      * @return {Boolean}
5807      */
5808     isHidden : function(colIndex){
5809         return this.config[colIndex].hidden;
5810     },
5811
5812
5813     /**
5814      * Returns true if the column width cannot be changed
5815      */
5816     isFixed : function(colIndex){
5817         return this.config[colIndex].fixed;
5818     },
5819
5820     /**
5821      * Returns true if the column can be resized
5822      * @return {Boolean}
5823      */
5824     isResizable : function(colIndex){
5825         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5826     },
5827     /**
5828      * Sets if a column is hidden.
5829      * @param {Number} colIndex The column index
5830      * @param {Boolean} hidden True if the column is hidden
5831      */
5832     setHidden : function(colIndex, hidden){
5833         this.config[colIndex].hidden = hidden;
5834         this.totalWidth = null;
5835         this.fireEvent("hiddenchange", this, colIndex, hidden);
5836     },
5837
5838     /**
5839      * Sets the editor for a column.
5840      * @param {Number} col The column index
5841      * @param {Object} editor The editor object
5842      */
5843     setEditor : function(col, editor){
5844         this.config[col].editor = editor;
5845     }
5846 });
5847
5848 Roo.grid.ColumnModel.defaultRenderer = function(value)
5849 {
5850     if(typeof value == "object") {
5851         return value;
5852     }
5853         if(typeof value == "string" && value.length < 1){
5854             return "&#160;";
5855         }
5856     
5857         return String.format("{0}", value);
5858 };
5859
5860 // Alias for backwards compatibility
5861 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5862 /*
5863  * Based on:
5864  * Ext JS Library 1.1.1
5865  * Copyright(c) 2006-2007, Ext JS, LLC.
5866  *
5867  * Originally Released Under LGPL - original licence link has changed is not relivant.
5868  *
5869  * Fork - LGPL
5870  * <script type="text/javascript">
5871  */
5872  
5873 /**
5874  * @class Roo.LoadMask
5875  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5876  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5877  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5878  * element's UpdateManager load indicator and will be destroyed after the initial load.
5879  * @constructor
5880  * Create a new LoadMask
5881  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5882  * @param {Object} config The config object
5883  */
5884 Roo.LoadMask = function(el, config){
5885     this.el = Roo.get(el);
5886     Roo.apply(this, config);
5887     if(this.store){
5888         this.store.on('beforeload', this.onBeforeLoad, this);
5889         this.store.on('load', this.onLoad, this);
5890         this.store.on('loadexception', this.onLoadException, this);
5891         this.removeMask = false;
5892     }else{
5893         var um = this.el.getUpdateManager();
5894         um.showLoadIndicator = false; // disable the default indicator
5895         um.on('beforeupdate', this.onBeforeLoad, this);
5896         um.on('update', this.onLoad, this);
5897         um.on('failure', this.onLoad, this);
5898         this.removeMask = true;
5899     }
5900 };
5901
5902 Roo.LoadMask.prototype = {
5903     /**
5904      * @cfg {Boolean} removeMask
5905      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5906      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5907      */
5908     /**
5909      * @cfg {String} msg
5910      * The text to display in a centered loading message box (defaults to 'Loading...')
5911      */
5912     msg : 'Loading...',
5913     /**
5914      * @cfg {String} msgCls
5915      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5916      */
5917     msgCls : 'x-mask-loading',
5918
5919     /**
5920      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5921      * @type Boolean
5922      */
5923     disabled: false,
5924
5925     /**
5926      * Disables the mask to prevent it from being displayed
5927      */
5928     disable : function(){
5929        this.disabled = true;
5930     },
5931
5932     /**
5933      * Enables the mask so that it can be displayed
5934      */
5935     enable : function(){
5936         this.disabled = false;
5937     },
5938     
5939     onLoadException : function()
5940     {
5941         Roo.log(arguments);
5942         
5943         if (typeof(arguments[3]) != 'undefined') {
5944             Roo.MessageBox.alert("Error loading",arguments[3]);
5945         } 
5946         /*
5947         try {
5948             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5949                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5950             }   
5951         } catch(e) {
5952             
5953         }
5954         */
5955     
5956         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5957     },
5958     // private
5959     onLoad : function()
5960     {
5961         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5962     },
5963
5964     // private
5965     onBeforeLoad : function(){
5966         if(!this.disabled){
5967             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5968         }
5969     },
5970
5971     // private
5972     destroy : function(){
5973         if(this.store){
5974             this.store.un('beforeload', this.onBeforeLoad, this);
5975             this.store.un('load', this.onLoad, this);
5976             this.store.un('loadexception', this.onLoadException, this);
5977         }else{
5978             var um = this.el.getUpdateManager();
5979             um.un('beforeupdate', this.onBeforeLoad, this);
5980             um.un('update', this.onLoad, this);
5981             um.un('failure', this.onLoad, this);
5982         }
5983     }
5984 };/*
5985  * - LGPL
5986  *
5987  * table
5988  * 
5989  */
5990
5991 /**
5992  * @class Roo.bootstrap.Table
5993  * @extends Roo.bootstrap.Component
5994  * Bootstrap Table class
5995  * @cfg {String} cls table class
5996  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5997  * @cfg {String} bgcolor Specifies the background color for a table
5998  * @cfg {Number} border Specifies whether the table cells should have borders or not
5999  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6000  * @cfg {Number} cellspacing Specifies the space between cells
6001  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6002  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6003  * @cfg {String} sortable Specifies that the table should be sortable
6004  * @cfg {String} summary Specifies a summary of the content of a table
6005  * @cfg {Number} width Specifies the width of a table
6006  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6007  * 
6008  * @cfg {boolean} striped Should the rows be alternative striped
6009  * @cfg {boolean} bordered Add borders to the table
6010  * @cfg {boolean} hover Add hover highlighting
6011  * @cfg {boolean} condensed Format condensed
6012  * @cfg {boolean} responsive Format condensed
6013  * @cfg {Boolean} loadMask (true|false) default false
6014  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6015  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6016  * @cfg {Boolean} rowSelection (true|false) default false
6017  * @cfg {Boolean} cellSelection (true|false) default false
6018  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6019  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6020  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6021  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6022  
6023  * 
6024  * @constructor
6025  * Create a new Table
6026  * @param {Object} config The config object
6027  */
6028
6029 Roo.bootstrap.Table = function(config){
6030     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6031     
6032   
6033     
6034     // BC...
6035     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6036     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6037     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6038     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6039     
6040     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6041     if (this.sm) {
6042         this.sm.grid = this;
6043         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6044         this.sm = this.selModel;
6045         this.sm.xmodule = this.xmodule || false;
6046     }
6047     
6048     if (this.cm && typeof(this.cm.config) == 'undefined') {
6049         this.colModel = new Roo.grid.ColumnModel(this.cm);
6050         this.cm = this.colModel;
6051         this.cm.xmodule = this.xmodule || false;
6052     }
6053     if (this.store) {
6054         this.store= Roo.factory(this.store, Roo.data);
6055         this.ds = this.store;
6056         this.ds.xmodule = this.xmodule || false;
6057          
6058     }
6059     if (this.footer && this.store) {
6060         this.footer.dataSource = this.ds;
6061         this.footer = Roo.factory(this.footer);
6062     }
6063     
6064     /** @private */
6065     this.addEvents({
6066         /**
6067          * @event cellclick
6068          * Fires when a cell is clicked
6069          * @param {Roo.bootstrap.Table} this
6070          * @param {Roo.Element} el
6071          * @param {Number} rowIndex
6072          * @param {Number} columnIndex
6073          * @param {Roo.EventObject} e
6074          */
6075         "cellclick" : true,
6076         /**
6077          * @event celldblclick
6078          * Fires when a cell is double clicked
6079          * @param {Roo.bootstrap.Table} this
6080          * @param {Roo.Element} el
6081          * @param {Number} rowIndex
6082          * @param {Number} columnIndex
6083          * @param {Roo.EventObject} e
6084          */
6085         "celldblclick" : true,
6086         /**
6087          * @event rowclick
6088          * Fires when a row is clicked
6089          * @param {Roo.bootstrap.Table} this
6090          * @param {Roo.Element} el
6091          * @param {Number} rowIndex
6092          * @param {Roo.EventObject} e
6093          */
6094         "rowclick" : true,
6095         /**
6096          * @event rowdblclick
6097          * Fires when a row is double clicked
6098          * @param {Roo.bootstrap.Table} this
6099          * @param {Roo.Element} el
6100          * @param {Number} rowIndex
6101          * @param {Roo.EventObject} e
6102          */
6103         "rowdblclick" : true,
6104         /**
6105          * @event mouseover
6106          * Fires when a mouseover occur
6107          * @param {Roo.bootstrap.Table} this
6108          * @param {Roo.Element} el
6109          * @param {Number} rowIndex
6110          * @param {Number} columnIndex
6111          * @param {Roo.EventObject} e
6112          */
6113         "mouseover" : true,
6114         /**
6115          * @event mouseout
6116          * Fires when a mouseout occur
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         "mouseout" : true,
6124         /**
6125          * @event rowclass
6126          * Fires when a row is rendered, so you can change add a style to it.
6127          * @param {Roo.bootstrap.Table} this
6128          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6129          */
6130         'rowclass' : true,
6131           /**
6132          * @event rowsrendered
6133          * Fires when all the  rows have been rendered
6134          * @param {Roo.bootstrap.Table} this
6135          */
6136         'rowsrendered' : true,
6137         /**
6138          * @event contextmenu
6139          * The raw contextmenu event for the entire grid.
6140          * @param {Roo.EventObject} e
6141          */
6142         "contextmenu" : true,
6143         /**
6144          * @event rowcontextmenu
6145          * Fires when a row is right clicked
6146          * @param {Roo.bootstrap.Table} this
6147          * @param {Number} rowIndex
6148          * @param {Roo.EventObject} e
6149          */
6150         "rowcontextmenu" : true,
6151         /**
6152          * @event cellcontextmenu
6153          * Fires when a cell is right clicked
6154          * @param {Roo.bootstrap.Table} this
6155          * @param {Number} rowIndex
6156          * @param {Number} cellIndex
6157          * @param {Roo.EventObject} e
6158          */
6159          "cellcontextmenu" : true,
6160          /**
6161          * @event headercontextmenu
6162          * Fires when a header is right clicked
6163          * @param {Roo.bootstrap.Table} this
6164          * @param {Number} columnIndex
6165          * @param {Roo.EventObject} e
6166          */
6167         "headercontextmenu" : true
6168     });
6169 };
6170
6171 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6172     
6173     cls: false,
6174     align: false,
6175     bgcolor: false,
6176     border: false,
6177     cellpadding: false,
6178     cellspacing: false,
6179     frame: false,
6180     rules: false,
6181     sortable: false,
6182     summary: false,
6183     width: false,
6184     striped : false,
6185     scrollBody : false,
6186     bordered: false,
6187     hover:  false,
6188     condensed : false,
6189     responsive : false,
6190     sm : false,
6191     cm : false,
6192     store : false,
6193     loadMask : false,
6194     footerShow : true,
6195     headerShow : true,
6196   
6197     rowSelection : false,
6198     cellSelection : false,
6199     layout : false,
6200     
6201     // Roo.Element - the tbody
6202     mainBody: false,
6203     // Roo.Element - thead element
6204     mainHead: false,
6205     
6206     container: false, // used by gridpanel...
6207     
6208     lazyLoad : false,
6209     
6210     CSS : Roo.util.CSS,
6211     
6212     auto_hide_footer : false,
6213     
6214     getAutoCreate : function()
6215     {
6216         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6217         
6218         cfg = {
6219             tag: 'table',
6220             cls : 'table',
6221             cn : []
6222         };
6223         if (this.scrollBody) {
6224             cfg.cls += ' table-body-fixed';
6225         }    
6226         if (this.striped) {
6227             cfg.cls += ' table-striped';
6228         }
6229         
6230         if (this.hover) {
6231             cfg.cls += ' table-hover';
6232         }
6233         if (this.bordered) {
6234             cfg.cls += ' table-bordered';
6235         }
6236         if (this.condensed) {
6237             cfg.cls += ' table-condensed';
6238         }
6239         if (this.responsive) {
6240             cfg.cls += ' table-responsive';
6241         }
6242         
6243         if (this.cls) {
6244             cfg.cls+=  ' ' +this.cls;
6245         }
6246         
6247         // this lot should be simplifed...
6248         var _t = this;
6249         var cp = [
6250             'align',
6251             'bgcolor',
6252             'border',
6253             'cellpadding',
6254             'cellspacing',
6255             'frame',
6256             'rules',
6257             'sortable',
6258             'summary',
6259             'width'
6260         ].forEach(function(k) {
6261             if (_t[k]) {
6262                 cfg[k] = _t[k];
6263             }
6264         });
6265         
6266         
6267         if (this.layout) {
6268             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6269         }
6270         
6271         if(this.store || this.cm){
6272             if(this.headerShow){
6273                 cfg.cn.push(this.renderHeader());
6274             }
6275             
6276             cfg.cn.push(this.renderBody());
6277             
6278             if(this.footerShow){
6279                 cfg.cn.push(this.renderFooter());
6280             }
6281             // where does this come from?
6282             //cfg.cls+=  ' TableGrid';
6283         }
6284         
6285         return { cn : [ cfg ] };
6286     },
6287     
6288     initEvents : function()
6289     {   
6290         if(!this.store || !this.cm){
6291             return;
6292         }
6293         if (this.selModel) {
6294             this.selModel.initEvents();
6295         }
6296         
6297         
6298         //Roo.log('initEvents with ds!!!!');
6299         
6300         this.mainBody = this.el.select('tbody', true).first();
6301         this.mainHead = this.el.select('thead', true).first();
6302         this.mainFoot = this.el.select('tfoot', true).first();
6303         
6304         
6305         
6306         var _this = this;
6307         
6308         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6309             e.on('click', _this.sort, _this);
6310         });
6311         
6312         this.mainBody.on("click", this.onClick, this);
6313         this.mainBody.on("dblclick", this.onDblClick, this);
6314         
6315         // why is this done????? = it breaks dialogs??
6316         //this.parent().el.setStyle('position', 'relative');
6317         
6318         
6319         if (this.footer) {
6320             this.footer.parentId = this.id;
6321             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6322             
6323             if(this.lazyLoad){
6324                 this.el.select('tfoot tr td').first().addClass('hide');
6325             }
6326         } 
6327         
6328         if(this.loadMask) {
6329             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6330         }
6331         
6332         this.store.on('load', this.onLoad, this);
6333         this.store.on('beforeload', this.onBeforeLoad, this);
6334         this.store.on('update', this.onUpdate, this);
6335         this.store.on('add', this.onAdd, this);
6336         this.store.on("clear", this.clear, this);
6337         
6338         this.el.on("contextmenu", this.onContextMenu, this);
6339         
6340         this.mainBody.on('scroll', this.onBodyScroll, this);
6341         
6342         this.cm.on("headerchange", this.onHeaderChange, this);
6343         
6344         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6345         
6346     },
6347     
6348     onContextMenu : function(e, t)
6349     {
6350         this.processEvent("contextmenu", e);
6351     },
6352     
6353     processEvent : function(name, e)
6354     {
6355         if (name != 'touchstart' ) {
6356             this.fireEvent(name, e);    
6357         }
6358         
6359         var t = e.getTarget();
6360         
6361         var cell = Roo.get(t);
6362         
6363         if(!cell){
6364             return;
6365         }
6366         
6367         if(cell.findParent('tfoot', false, true)){
6368             return;
6369         }
6370         
6371         if(cell.findParent('thead', false, true)){
6372             
6373             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6374                 cell = Roo.get(t).findParent('th', false, true);
6375                 if (!cell) {
6376                     Roo.log("failed to find th in thead?");
6377                     Roo.log(e.getTarget());
6378                     return;
6379                 }
6380             }
6381             
6382             var cellIndex = cell.dom.cellIndex;
6383             
6384             var ename = name == 'touchstart' ? 'click' : name;
6385             this.fireEvent("header" + ename, this, cellIndex, e);
6386             
6387             return;
6388         }
6389         
6390         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6391             cell = Roo.get(t).findParent('td', false, true);
6392             if (!cell) {
6393                 Roo.log("failed to find th in tbody?");
6394                 Roo.log(e.getTarget());
6395                 return;
6396             }
6397         }
6398         
6399         var row = cell.findParent('tr', false, true);
6400         var cellIndex = cell.dom.cellIndex;
6401         var rowIndex = row.dom.rowIndex - 1;
6402         
6403         if(row !== false){
6404             
6405             this.fireEvent("row" + name, this, rowIndex, e);
6406             
6407             if(cell !== false){
6408             
6409                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6410             }
6411         }
6412         
6413     },
6414     
6415     onMouseover : function(e, el)
6416     {
6417         var cell = Roo.get(el);
6418         
6419         if(!cell){
6420             return;
6421         }
6422         
6423         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6424             cell = cell.findParent('td', false, true);
6425         }
6426         
6427         var row = cell.findParent('tr', false, true);
6428         var cellIndex = cell.dom.cellIndex;
6429         var rowIndex = row.dom.rowIndex - 1; // start from 0
6430         
6431         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6432         
6433     },
6434     
6435     onMouseout : function(e, el)
6436     {
6437         var cell = Roo.get(el);
6438         
6439         if(!cell){
6440             return;
6441         }
6442         
6443         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6444             cell = cell.findParent('td', false, true);
6445         }
6446         
6447         var row = cell.findParent('tr', false, true);
6448         var cellIndex = cell.dom.cellIndex;
6449         var rowIndex = row.dom.rowIndex - 1; // start from 0
6450         
6451         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6452         
6453     },
6454     
6455     onClick : function(e, el)
6456     {
6457         var cell = Roo.get(el);
6458         
6459         if(!cell || (!this.cellSelection && !this.rowSelection)){
6460             return;
6461         }
6462         
6463         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6464             cell = cell.findParent('td', false, true);
6465         }
6466         
6467         if(!cell || typeof(cell) == 'undefined'){
6468             return;
6469         }
6470         
6471         var row = cell.findParent('tr', false, true);
6472         
6473         if(!row || typeof(row) == 'undefined'){
6474             return;
6475         }
6476         
6477         var cellIndex = cell.dom.cellIndex;
6478         var rowIndex = this.getRowIndex(row);
6479         
6480         // why??? - should these not be based on SelectionModel?
6481         if(this.cellSelection){
6482             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6483         }
6484         
6485         if(this.rowSelection){
6486             this.fireEvent('rowclick', this, row, rowIndex, e);
6487         }
6488         
6489         
6490     },
6491         
6492     onDblClick : function(e,el)
6493     {
6494         var cell = Roo.get(el);
6495         
6496         if(!cell || (!this.cellSelection && !this.rowSelection)){
6497             return;
6498         }
6499         
6500         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6501             cell = cell.findParent('td', false, true);
6502         }
6503         
6504         if(!cell || typeof(cell) == 'undefined'){
6505             return;
6506         }
6507         
6508         var row = cell.findParent('tr', false, true);
6509         
6510         if(!row || typeof(row) == 'undefined'){
6511             return;
6512         }
6513         
6514         var cellIndex = cell.dom.cellIndex;
6515         var rowIndex = this.getRowIndex(row);
6516         
6517         if(this.cellSelection){
6518             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6519         }
6520         
6521         if(this.rowSelection){
6522             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6523         }
6524     },
6525     
6526     sort : function(e,el)
6527     {
6528         var col = Roo.get(el);
6529         
6530         if(!col.hasClass('sortable')){
6531             return;
6532         }
6533         
6534         var sort = col.attr('sort');
6535         var dir = 'ASC';
6536         
6537         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6538             dir = 'DESC';
6539         }
6540         
6541         this.store.sortInfo = {field : sort, direction : dir};
6542         
6543         if (this.footer) {
6544             Roo.log("calling footer first");
6545             this.footer.onClick('first');
6546         } else {
6547         
6548             this.store.load({ params : { start : 0 } });
6549         }
6550     },
6551     
6552     renderHeader : function()
6553     {
6554         var header = {
6555             tag: 'thead',
6556             cn : []
6557         };
6558         
6559         var cm = this.cm;
6560         this.totalWidth = 0;
6561         
6562         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6563             
6564             var config = cm.config[i];
6565             
6566             var c = {
6567                 tag: 'th',
6568                 cls : 'x-hcol-' + i,
6569                 style : '',
6570                 html: cm.getColumnHeader(i)
6571             };
6572             
6573             var hh = '';
6574             
6575             if(typeof(config.sortable) != 'undefined' && config.sortable){
6576                 c.cls = 'sortable';
6577                 c.html = '<i class="glyphicon"></i>' + c.html;
6578             }
6579             
6580             if(typeof(config.lgHeader) != 'undefined'){
6581                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6582             }
6583             
6584             if(typeof(config.mdHeader) != 'undefined'){
6585                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6586             }
6587             
6588             if(typeof(config.smHeader) != 'undefined'){
6589                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6590             }
6591             
6592             if(typeof(config.xsHeader) != 'undefined'){
6593                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6594             }
6595             
6596             if(hh.length){
6597                 c.html = hh;
6598             }
6599             
6600             if(typeof(config.tooltip) != 'undefined'){
6601                 c.tooltip = config.tooltip;
6602             }
6603             
6604             if(typeof(config.colspan) != 'undefined'){
6605                 c.colspan = config.colspan;
6606             }
6607             
6608             if(typeof(config.hidden) != 'undefined' && config.hidden){
6609                 c.style += ' display:none;';
6610             }
6611             
6612             if(typeof(config.dataIndex) != 'undefined'){
6613                 c.sort = config.dataIndex;
6614             }
6615             
6616            
6617             
6618             if(typeof(config.align) != 'undefined' && config.align.length){
6619                 c.style += ' text-align:' + config.align + ';';
6620             }
6621             
6622             if(typeof(config.width) != 'undefined'){
6623                 c.style += ' width:' + config.width + 'px;';
6624                 this.totalWidth += config.width;
6625             } else {
6626                 this.totalWidth += 100; // assume minimum of 100 per column?
6627             }
6628             
6629             if(typeof(config.cls) != 'undefined'){
6630                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6631             }
6632             
6633             ['xs','sm','md','lg'].map(function(size){
6634                 
6635                 if(typeof(config[size]) == 'undefined'){
6636                     return;
6637                 }
6638                 
6639                 if (!config[size]) { // 0 = hidden
6640                     c.cls += ' hidden-' + size;
6641                     return;
6642                 }
6643                 
6644                 c.cls += ' col-' + size + '-' + config[size];
6645
6646             });
6647             
6648             header.cn.push(c)
6649         }
6650         
6651         return header;
6652     },
6653     
6654     renderBody : function()
6655     {
6656         var body = {
6657             tag: 'tbody',
6658             cn : [
6659                 {
6660                     tag: 'tr',
6661                     cn : [
6662                         {
6663                             tag : 'td',
6664                             colspan :  this.cm.getColumnCount()
6665                         }
6666                     ]
6667                 }
6668             ]
6669         };
6670         
6671         return body;
6672     },
6673     
6674     renderFooter : function()
6675     {
6676         var footer = {
6677             tag: 'tfoot',
6678             cn : [
6679                 {
6680                     tag: 'tr',
6681                     cn : [
6682                         {
6683                             tag : 'td',
6684                             colspan :  this.cm.getColumnCount()
6685                         }
6686                     ]
6687                 }
6688             ]
6689         };
6690         
6691         return footer;
6692     },
6693     
6694     
6695     
6696     onLoad : function()
6697     {
6698 //        Roo.log('ds onload');
6699         this.clear();
6700         
6701         var _this = this;
6702         var cm = this.cm;
6703         var ds = this.store;
6704         
6705         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6706             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6707             if (_this.store.sortInfo) {
6708                     
6709                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6710                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6711                 }
6712                 
6713                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6714                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6715                 }
6716             }
6717         });
6718         
6719         var tbody =  this.mainBody;
6720               
6721         if(ds.getCount() > 0){
6722             ds.data.each(function(d,rowIndex){
6723                 var row =  this.renderRow(cm, ds, rowIndex);
6724                 
6725                 tbody.createChild(row);
6726                 
6727                 var _this = this;
6728                 
6729                 if(row.cellObjects.length){
6730                     Roo.each(row.cellObjects, function(r){
6731                         _this.renderCellObject(r);
6732                     })
6733                 }
6734                 
6735             }, this);
6736         }
6737         
6738         var tfoot = this.el.select('tfoot', true).first();
6739         
6740         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6741             
6742             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6743             
6744             var total = this.ds.getTotalCount();
6745             
6746             if(this.footer.pageSize < total){
6747                 this.mainFoot.show();
6748             }
6749         }
6750         
6751         Roo.each(this.el.select('tbody td', true).elements, function(e){
6752             e.on('mouseover', _this.onMouseover, _this);
6753         });
6754         
6755         Roo.each(this.el.select('tbody td', true).elements, function(e){
6756             e.on('mouseout', _this.onMouseout, _this);
6757         });
6758         this.fireEvent('rowsrendered', this);
6759         
6760         this.autoSize();
6761     },
6762     
6763     
6764     onUpdate : function(ds,record)
6765     {
6766         this.refreshRow(record);
6767         this.autoSize();
6768     },
6769     
6770     onRemove : function(ds, record, index, isUpdate){
6771         if(isUpdate !== true){
6772             this.fireEvent("beforerowremoved", this, index, record);
6773         }
6774         var bt = this.mainBody.dom;
6775         
6776         var rows = this.el.select('tbody > tr', true).elements;
6777         
6778         if(typeof(rows[index]) != 'undefined'){
6779             bt.removeChild(rows[index].dom);
6780         }
6781         
6782 //        if(bt.rows[index]){
6783 //            bt.removeChild(bt.rows[index]);
6784 //        }
6785         
6786         if(isUpdate !== true){
6787             //this.stripeRows(index);
6788             //this.syncRowHeights(index, index);
6789             //this.layout();
6790             this.fireEvent("rowremoved", this, index, record);
6791         }
6792     },
6793     
6794     onAdd : function(ds, records, rowIndex)
6795     {
6796         //Roo.log('on Add called');
6797         // - note this does not handle multiple adding very well..
6798         var bt = this.mainBody.dom;
6799         for (var i =0 ; i < records.length;i++) {
6800             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6801             //Roo.log(records[i]);
6802             //Roo.log(this.store.getAt(rowIndex+i));
6803             this.insertRow(this.store, rowIndex + i, false);
6804             return;
6805         }
6806         
6807     },
6808     
6809     
6810     refreshRow : function(record){
6811         var ds = this.store, index;
6812         if(typeof record == 'number'){
6813             index = record;
6814             record = ds.getAt(index);
6815         }else{
6816             index = ds.indexOf(record);
6817         }
6818         this.insertRow(ds, index, true);
6819         this.autoSize();
6820         this.onRemove(ds, record, index+1, true);
6821         this.autoSize();
6822         //this.syncRowHeights(index, index);
6823         //this.layout();
6824         this.fireEvent("rowupdated", this, index, record);
6825     },
6826     
6827     insertRow : function(dm, rowIndex, isUpdate){
6828         
6829         if(!isUpdate){
6830             this.fireEvent("beforerowsinserted", this, rowIndex);
6831         }
6832             //var s = this.getScrollState();
6833         var row = this.renderRow(this.cm, this.store, rowIndex);
6834         // insert before rowIndex..
6835         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6836         
6837         var _this = this;
6838                 
6839         if(row.cellObjects.length){
6840             Roo.each(row.cellObjects, function(r){
6841                 _this.renderCellObject(r);
6842             })
6843         }
6844             
6845         if(!isUpdate){
6846             this.fireEvent("rowsinserted", this, rowIndex);
6847             //this.syncRowHeights(firstRow, lastRow);
6848             //this.stripeRows(firstRow);
6849             //this.layout();
6850         }
6851         
6852     },
6853     
6854     
6855     getRowDom : function(rowIndex)
6856     {
6857         var rows = this.el.select('tbody > tr', true).elements;
6858         
6859         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6860         
6861     },
6862     // returns the object tree for a tr..
6863   
6864     
6865     renderRow : function(cm, ds, rowIndex) 
6866     {
6867         var d = ds.getAt(rowIndex);
6868         
6869         var row = {
6870             tag : 'tr',
6871             cls : 'x-row-' + rowIndex,
6872             cn : []
6873         };
6874             
6875         var cellObjects = [];
6876         
6877         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6878             var config = cm.config[i];
6879             
6880             var renderer = cm.getRenderer(i);
6881             var value = '';
6882             var id = false;
6883             
6884             if(typeof(renderer) !== 'undefined'){
6885                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6886             }
6887             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6888             // and are rendered into the cells after the row is rendered - using the id for the element.
6889             
6890             if(typeof(value) === 'object'){
6891                 id = Roo.id();
6892                 cellObjects.push({
6893                     container : id,
6894                     cfg : value 
6895                 })
6896             }
6897             
6898             var rowcfg = {
6899                 record: d,
6900                 rowIndex : rowIndex,
6901                 colIndex : i,
6902                 rowClass : ''
6903             };
6904
6905             this.fireEvent('rowclass', this, rowcfg);
6906             
6907             var td = {
6908                 tag: 'td',
6909                 cls : rowcfg.rowClass + ' x-col-' + i,
6910                 style: '',
6911                 html: (typeof(value) === 'object') ? '' : value
6912             };
6913             
6914             if (id) {
6915                 td.id = id;
6916             }
6917             
6918             if(typeof(config.colspan) != 'undefined'){
6919                 td.colspan = config.colspan;
6920             }
6921             
6922             if(typeof(config.hidden) != 'undefined' && config.hidden){
6923                 td.style += ' display:none;';
6924             }
6925             
6926             if(typeof(config.align) != 'undefined' && config.align.length){
6927                 td.style += ' text-align:' + config.align + ';';
6928             }
6929             if(typeof(config.valign) != 'undefined' && config.valign.length){
6930                 td.style += ' vertical-align:' + config.valign + ';';
6931             }
6932             
6933             if(typeof(config.width) != 'undefined'){
6934                 td.style += ' width:' +  config.width + 'px;';
6935             }
6936             
6937             if(typeof(config.cursor) != 'undefined'){
6938                 td.style += ' cursor:' +  config.cursor + ';';
6939             }
6940             
6941             if(typeof(config.cls) != 'undefined'){
6942                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6943             }
6944             
6945             ['xs','sm','md','lg'].map(function(size){
6946                 
6947                 if(typeof(config[size]) == 'undefined'){
6948                     return;
6949                 }
6950                 
6951                 if (!config[size]) { // 0 = hidden
6952                     td.cls += ' hidden-' + size;
6953                     return;
6954                 }
6955                 
6956                 td.cls += ' col-' + size + '-' + config[size];
6957
6958             });
6959             
6960             row.cn.push(td);
6961            
6962         }
6963         
6964         row.cellObjects = cellObjects;
6965         
6966         return row;
6967           
6968     },
6969     
6970     
6971     
6972     onBeforeLoad : function()
6973     {
6974         
6975     },
6976      /**
6977      * Remove all rows
6978      */
6979     clear : function()
6980     {
6981         this.el.select('tbody', true).first().dom.innerHTML = '';
6982     },
6983     /**
6984      * Show or hide a row.
6985      * @param {Number} rowIndex to show or hide
6986      * @param {Boolean} state hide
6987      */
6988     setRowVisibility : function(rowIndex, state)
6989     {
6990         var bt = this.mainBody.dom;
6991         
6992         var rows = this.el.select('tbody > tr', true).elements;
6993         
6994         if(typeof(rows[rowIndex]) == 'undefined'){
6995             return;
6996         }
6997         rows[rowIndex].dom.style.display = state ? '' : 'none';
6998     },
6999     
7000     
7001     getSelectionModel : function(){
7002         if(!this.selModel){
7003             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7004         }
7005         return this.selModel;
7006     },
7007     /*
7008      * Render the Roo.bootstrap object from renderder
7009      */
7010     renderCellObject : function(r)
7011     {
7012         var _this = this;
7013         
7014         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7015         
7016         var t = r.cfg.render(r.container);
7017         
7018         if(r.cfg.cn){
7019             Roo.each(r.cfg.cn, function(c){
7020                 var child = {
7021                     container: t.getChildContainer(),
7022                     cfg: c
7023                 };
7024                 _this.renderCellObject(child);
7025             })
7026         }
7027     },
7028     
7029     getRowIndex : function(row)
7030     {
7031         var rowIndex = -1;
7032         
7033         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7034             if(el != row){
7035                 return;
7036             }
7037             
7038             rowIndex = index;
7039         });
7040         
7041         return rowIndex;
7042     },
7043      /**
7044      * Returns the grid's underlying element = used by panel.Grid
7045      * @return {Element} The element
7046      */
7047     getGridEl : function(){
7048         return this.el;
7049     },
7050      /**
7051      * Forces a resize - used by panel.Grid
7052      * @return {Element} The element
7053      */
7054     autoSize : function()
7055     {
7056         //var ctr = Roo.get(this.container.dom.parentElement);
7057         var ctr = Roo.get(this.el.dom);
7058         
7059         var thd = this.getGridEl().select('thead',true).first();
7060         var tbd = this.getGridEl().select('tbody', true).first();
7061         var tfd = this.getGridEl().select('tfoot', true).first();
7062         
7063         var cw = ctr.getWidth();
7064         
7065         if (tbd) {
7066             
7067             tbd.setSize(ctr.getWidth(),
7068                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7069             );
7070             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7071             cw -= barsize;
7072         }
7073         cw = Math.max(cw, this.totalWidth);
7074         this.getGridEl().select('tr',true).setWidth(cw);
7075         // resize 'expandable coloumn?
7076         
7077         return; // we doe not have a view in this design..
7078         
7079     },
7080     onBodyScroll: function()
7081     {
7082         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7083         if(this.mainHead){
7084             this.mainHead.setStyle({
7085                 'position' : 'relative',
7086                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7087             });
7088         }
7089         
7090         if(this.lazyLoad){
7091             
7092             var scrollHeight = this.mainBody.dom.scrollHeight;
7093             
7094             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7095             
7096             var height = this.mainBody.getHeight();
7097             
7098             if(scrollHeight - height == scrollTop) {
7099                 
7100                 var total = this.ds.getTotalCount();
7101                 
7102                 if(this.footer.cursor + this.footer.pageSize < total){
7103                     
7104                     this.footer.ds.load({
7105                         params : {
7106                             start : this.footer.cursor + this.footer.pageSize,
7107                             limit : this.footer.pageSize
7108                         },
7109                         add : true
7110                     });
7111                 }
7112             }
7113             
7114         }
7115     },
7116     
7117     onHeaderChange : function()
7118     {
7119         var header = this.renderHeader();
7120         var table = this.el.select('table', true).first();
7121         
7122         this.mainHead.remove();
7123         this.mainHead = table.createChild(header, this.mainBody, false);
7124     },
7125     
7126     onHiddenChange : function(colModel, colIndex, hidden)
7127     {
7128         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7129         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7130         
7131         this.CSS.updateRule(thSelector, "display", "");
7132         this.CSS.updateRule(tdSelector, "display", "");
7133         
7134         if(hidden){
7135             this.CSS.updateRule(thSelector, "display", "none");
7136             this.CSS.updateRule(tdSelector, "display", "none");
7137         }
7138         
7139         this.onHeaderChange();
7140         this.onLoad();
7141     },
7142     
7143     setColumnWidth: function(col_index, width)
7144     {
7145         // width = "md-2 xs-2..."
7146         if(!this.colModel.config[col_index]) {
7147             return;
7148         }
7149         
7150         var w = width.split(" ");
7151         
7152         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7153         
7154         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7155         
7156         
7157         for(var j = 0; j < w.length; j++) {
7158             
7159             if(!w[j]) {
7160                 continue;
7161             }
7162             
7163             var size_cls = w[j].split("-");
7164             
7165             if(!Number.isInteger(size_cls[1] * 1)) {
7166                 continue;
7167             }
7168             
7169             if(!this.colModel.config[col_index][size_cls[0]]) {
7170                 continue;
7171             }
7172             
7173             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7174                 continue;
7175             }
7176             
7177             h_row[0].classList.replace(
7178                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7179                 "col-"+size_cls[0]+"-"+size_cls[1]
7180             );
7181             
7182             for(var i = 0; i < rows.length; i++) {
7183                 
7184                 var size_cls = w[j].split("-");
7185                 
7186                 if(!Number.isInteger(size_cls[1] * 1)) {
7187                     continue;
7188                 }
7189                 
7190                 if(!this.colModel.config[col_index][size_cls[0]]) {
7191                     continue;
7192                 }
7193                 
7194                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7195                     continue;
7196                 }
7197                 
7198                 rows[i].classList.replace(
7199                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7200                     "col-"+size_cls[0]+"-"+size_cls[1]
7201                 );
7202             }
7203             
7204             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7205         }
7206     }
7207 });
7208
7209  
7210
7211  /*
7212  * - LGPL
7213  *
7214  * table cell
7215  * 
7216  */
7217
7218 /**
7219  * @class Roo.bootstrap.TableCell
7220  * @extends Roo.bootstrap.Component
7221  * Bootstrap TableCell class
7222  * @cfg {String} html cell contain text
7223  * @cfg {String} cls cell class
7224  * @cfg {String} tag cell tag (td|th) default td
7225  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7226  * @cfg {String} align Aligns the content in a cell
7227  * @cfg {String} axis Categorizes cells
7228  * @cfg {String} bgcolor Specifies the background color of a cell
7229  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7230  * @cfg {Number} colspan Specifies the number of columns a cell should span
7231  * @cfg {String} headers Specifies one or more header cells a cell is related to
7232  * @cfg {Number} height Sets the height of a cell
7233  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7234  * @cfg {Number} rowspan Sets the number of rows a cell should span
7235  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7236  * @cfg {String} valign Vertical aligns the content in a cell
7237  * @cfg {Number} width Specifies the width of a cell
7238  * 
7239  * @constructor
7240  * Create a new TableCell
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.TableCell = function(config){
7245     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7249     
7250     html: false,
7251     cls: false,
7252     tag: false,
7253     abbr: false,
7254     align: false,
7255     axis: false,
7256     bgcolor: false,
7257     charoff: false,
7258     colspan: false,
7259     headers: false,
7260     height: false,
7261     nowrap: false,
7262     rowspan: false,
7263     scope: false,
7264     valign: false,
7265     width: false,
7266     
7267     
7268     getAutoCreate : function(){
7269         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7270         
7271         cfg = {
7272             tag: 'td'
7273         };
7274         
7275         if(this.tag){
7276             cfg.tag = this.tag;
7277         }
7278         
7279         if (this.html) {
7280             cfg.html=this.html
7281         }
7282         if (this.cls) {
7283             cfg.cls=this.cls
7284         }
7285         if (this.abbr) {
7286             cfg.abbr=this.abbr
7287         }
7288         if (this.align) {
7289             cfg.align=this.align
7290         }
7291         if (this.axis) {
7292             cfg.axis=this.axis
7293         }
7294         if (this.bgcolor) {
7295             cfg.bgcolor=this.bgcolor
7296         }
7297         if (this.charoff) {
7298             cfg.charoff=this.charoff
7299         }
7300         if (this.colspan) {
7301             cfg.colspan=this.colspan
7302         }
7303         if (this.headers) {
7304             cfg.headers=this.headers
7305         }
7306         if (this.height) {
7307             cfg.height=this.height
7308         }
7309         if (this.nowrap) {
7310             cfg.nowrap=this.nowrap
7311         }
7312         if (this.rowspan) {
7313             cfg.rowspan=this.rowspan
7314         }
7315         if (this.scope) {
7316             cfg.scope=this.scope
7317         }
7318         if (this.valign) {
7319             cfg.valign=this.valign
7320         }
7321         if (this.width) {
7322             cfg.width=this.width
7323         }
7324         
7325         
7326         return cfg;
7327     }
7328    
7329 });
7330
7331  
7332
7333  /*
7334  * - LGPL
7335  *
7336  * table row
7337  * 
7338  */
7339
7340 /**
7341  * @class Roo.bootstrap.TableRow
7342  * @extends Roo.bootstrap.Component
7343  * Bootstrap TableRow class
7344  * @cfg {String} cls row class
7345  * @cfg {String} align Aligns the content in a table row
7346  * @cfg {String} bgcolor Specifies a background color for a table row
7347  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7348  * @cfg {String} valign Vertical aligns the content in a table row
7349  * 
7350  * @constructor
7351  * Create a new TableRow
7352  * @param {Object} config The config object
7353  */
7354
7355 Roo.bootstrap.TableRow = function(config){
7356     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7357 };
7358
7359 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7360     
7361     cls: false,
7362     align: false,
7363     bgcolor: false,
7364     charoff: false,
7365     valign: false,
7366     
7367     getAutoCreate : function(){
7368         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7369         
7370         cfg = {
7371             tag: 'tr'
7372         };
7373             
7374         if(this.cls){
7375             cfg.cls = this.cls;
7376         }
7377         if(this.align){
7378             cfg.align = this.align;
7379         }
7380         if(this.bgcolor){
7381             cfg.bgcolor = this.bgcolor;
7382         }
7383         if(this.charoff){
7384             cfg.charoff = this.charoff;
7385         }
7386         if(this.valign){
7387             cfg.valign = this.valign;
7388         }
7389         
7390         return cfg;
7391     }
7392    
7393 });
7394
7395  
7396
7397  /*
7398  * - LGPL
7399  *
7400  * table body
7401  * 
7402  */
7403
7404 /**
7405  * @class Roo.bootstrap.TableBody
7406  * @extends Roo.bootstrap.Component
7407  * Bootstrap TableBody class
7408  * @cfg {String} cls element class
7409  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7410  * @cfg {String} align Aligns the content inside the element
7411  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7412  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7413  * 
7414  * @constructor
7415  * Create a new TableBody
7416  * @param {Object} config The config object
7417  */
7418
7419 Roo.bootstrap.TableBody = function(config){
7420     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7421 };
7422
7423 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7424     
7425     cls: false,
7426     tag: false,
7427     align: false,
7428     charoff: false,
7429     valign: false,
7430     
7431     getAutoCreate : function(){
7432         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7433         
7434         cfg = {
7435             tag: 'tbody'
7436         };
7437             
7438         if (this.cls) {
7439             cfg.cls=this.cls
7440         }
7441         if(this.tag){
7442             cfg.tag = this.tag;
7443         }
7444         
7445         if(this.align){
7446             cfg.align = this.align;
7447         }
7448         if(this.charoff){
7449             cfg.charoff = this.charoff;
7450         }
7451         if(this.valign){
7452             cfg.valign = this.valign;
7453         }
7454         
7455         return cfg;
7456     }
7457     
7458     
7459 //    initEvents : function()
7460 //    {
7461 //        
7462 //        if(!this.store){
7463 //            return;
7464 //        }
7465 //        
7466 //        this.store = Roo.factory(this.store, Roo.data);
7467 //        this.store.on('load', this.onLoad, this);
7468 //        
7469 //        this.store.load();
7470 //        
7471 //    },
7472 //    
7473 //    onLoad: function () 
7474 //    {   
7475 //        this.fireEvent('load', this);
7476 //    }
7477 //    
7478 //   
7479 });
7480
7481  
7482
7483  /*
7484  * Based on:
7485  * Ext JS Library 1.1.1
7486  * Copyright(c) 2006-2007, Ext JS, LLC.
7487  *
7488  * Originally Released Under LGPL - original licence link has changed is not relivant.
7489  *
7490  * Fork - LGPL
7491  * <script type="text/javascript">
7492  */
7493
7494 // as we use this in bootstrap.
7495 Roo.namespace('Roo.form');
7496  /**
7497  * @class Roo.form.Action
7498  * Internal Class used to handle form actions
7499  * @constructor
7500  * @param {Roo.form.BasicForm} el The form element or its id
7501  * @param {Object} config Configuration options
7502  */
7503
7504  
7505  
7506 // define the action interface
7507 Roo.form.Action = function(form, options){
7508     this.form = form;
7509     this.options = options || {};
7510 };
7511 /**
7512  * Client Validation Failed
7513  * @const 
7514  */
7515 Roo.form.Action.CLIENT_INVALID = 'client';
7516 /**
7517  * Server Validation Failed
7518  * @const 
7519  */
7520 Roo.form.Action.SERVER_INVALID = 'server';
7521  /**
7522  * Connect to Server Failed
7523  * @const 
7524  */
7525 Roo.form.Action.CONNECT_FAILURE = 'connect';
7526 /**
7527  * Reading Data from Server Failed
7528  * @const 
7529  */
7530 Roo.form.Action.LOAD_FAILURE = 'load';
7531
7532 Roo.form.Action.prototype = {
7533     type : 'default',
7534     failureType : undefined,
7535     response : undefined,
7536     result : undefined,
7537
7538     // interface method
7539     run : function(options){
7540
7541     },
7542
7543     // interface method
7544     success : function(response){
7545
7546     },
7547
7548     // interface method
7549     handleResponse : function(response){
7550
7551     },
7552
7553     // default connection failure
7554     failure : function(response){
7555         
7556         this.response = response;
7557         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7558         this.form.afterAction(this, false);
7559     },
7560
7561     processResponse : function(response){
7562         this.response = response;
7563         if(!response.responseText){
7564             return true;
7565         }
7566         this.result = this.handleResponse(response);
7567         return this.result;
7568     },
7569
7570     // utility functions used internally
7571     getUrl : function(appendParams){
7572         var url = this.options.url || this.form.url || this.form.el.dom.action;
7573         if(appendParams){
7574             var p = this.getParams();
7575             if(p){
7576                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7577             }
7578         }
7579         return url;
7580     },
7581
7582     getMethod : function(){
7583         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7584     },
7585
7586     getParams : function(){
7587         var bp = this.form.baseParams;
7588         var p = this.options.params;
7589         if(p){
7590             if(typeof p == "object"){
7591                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7592             }else if(typeof p == 'string' && bp){
7593                 p += '&' + Roo.urlEncode(bp);
7594             }
7595         }else if(bp){
7596             p = Roo.urlEncode(bp);
7597         }
7598         return p;
7599     },
7600
7601     createCallback : function(){
7602         return {
7603             success: this.success,
7604             failure: this.failure,
7605             scope: this,
7606             timeout: (this.form.timeout*1000),
7607             upload: this.form.fileUpload ? this.success : undefined
7608         };
7609     }
7610 };
7611
7612 Roo.form.Action.Submit = function(form, options){
7613     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7614 };
7615
7616 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7617     type : 'submit',
7618
7619     haveProgress : false,
7620     uploadComplete : false,
7621     
7622     // uploadProgress indicator.
7623     uploadProgress : function()
7624     {
7625         if (!this.form.progressUrl) {
7626             return;
7627         }
7628         
7629         if (!this.haveProgress) {
7630             Roo.MessageBox.progress("Uploading", "Uploading");
7631         }
7632         if (this.uploadComplete) {
7633            Roo.MessageBox.hide();
7634            return;
7635         }
7636         
7637         this.haveProgress = true;
7638    
7639         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7640         
7641         var c = new Roo.data.Connection();
7642         c.request({
7643             url : this.form.progressUrl,
7644             params: {
7645                 id : uid
7646             },
7647             method: 'GET',
7648             success : function(req){
7649                //console.log(data);
7650                 var rdata = false;
7651                 var edata;
7652                 try  {
7653                    rdata = Roo.decode(req.responseText)
7654                 } catch (e) {
7655                     Roo.log("Invalid data from server..");
7656                     Roo.log(edata);
7657                     return;
7658                 }
7659                 if (!rdata || !rdata.success) {
7660                     Roo.log(rdata);
7661                     Roo.MessageBox.alert(Roo.encode(rdata));
7662                     return;
7663                 }
7664                 var data = rdata.data;
7665                 
7666                 if (this.uploadComplete) {
7667                    Roo.MessageBox.hide();
7668                    return;
7669                 }
7670                    
7671                 if (data){
7672                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7673                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7674                     );
7675                 }
7676                 this.uploadProgress.defer(2000,this);
7677             },
7678        
7679             failure: function(data) {
7680                 Roo.log('progress url failed ');
7681                 Roo.log(data);
7682             },
7683             scope : this
7684         });
7685            
7686     },
7687     
7688     
7689     run : function()
7690     {
7691         // run get Values on the form, so it syncs any secondary forms.
7692         this.form.getValues();
7693         
7694         var o = this.options;
7695         var method = this.getMethod();
7696         var isPost = method == 'POST';
7697         if(o.clientValidation === false || this.form.isValid()){
7698             
7699             if (this.form.progressUrl) {
7700                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7701                     (new Date() * 1) + '' + Math.random());
7702                     
7703             } 
7704             
7705             
7706             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7707                 form:this.form.el.dom,
7708                 url:this.getUrl(!isPost),
7709                 method: method,
7710                 params:isPost ? this.getParams() : null,
7711                 isUpload: this.form.fileUpload
7712             }));
7713             
7714             this.uploadProgress();
7715
7716         }else if (o.clientValidation !== false){ // client validation failed
7717             this.failureType = Roo.form.Action.CLIENT_INVALID;
7718             this.form.afterAction(this, false);
7719         }
7720     },
7721
7722     success : function(response)
7723     {
7724         this.uploadComplete= true;
7725         if (this.haveProgress) {
7726             Roo.MessageBox.hide();
7727         }
7728         
7729         
7730         var result = this.processResponse(response);
7731         if(result === true || result.success){
7732             this.form.afterAction(this, true);
7733             return;
7734         }
7735         if(result.errors){
7736             this.form.markInvalid(result.errors);
7737             this.failureType = Roo.form.Action.SERVER_INVALID;
7738         }
7739         this.form.afterAction(this, false);
7740     },
7741     failure : function(response)
7742     {
7743         this.uploadComplete= true;
7744         if (this.haveProgress) {
7745             Roo.MessageBox.hide();
7746         }
7747         
7748         this.response = response;
7749         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7750         this.form.afterAction(this, false);
7751     },
7752     
7753     handleResponse : function(response){
7754         if(this.form.errorReader){
7755             var rs = this.form.errorReader.read(response);
7756             var errors = [];
7757             if(rs.records){
7758                 for(var i = 0, len = rs.records.length; i < len; i++) {
7759                     var r = rs.records[i];
7760                     errors[i] = r.data;
7761                 }
7762             }
7763             if(errors.length < 1){
7764                 errors = null;
7765             }
7766             return {
7767                 success : rs.success,
7768                 errors : errors
7769             };
7770         }
7771         var ret = false;
7772         try {
7773             ret = Roo.decode(response.responseText);
7774         } catch (e) {
7775             ret = {
7776                 success: false,
7777                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7778                 errors : []
7779             };
7780         }
7781         return ret;
7782         
7783     }
7784 });
7785
7786
7787 Roo.form.Action.Load = function(form, options){
7788     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7789     this.reader = this.form.reader;
7790 };
7791
7792 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7793     type : 'load',
7794
7795     run : function(){
7796         
7797         Roo.Ajax.request(Roo.apply(
7798                 this.createCallback(), {
7799                     method:this.getMethod(),
7800                     url:this.getUrl(false),
7801                     params:this.getParams()
7802         }));
7803     },
7804
7805     success : function(response){
7806         
7807         var result = this.processResponse(response);
7808         if(result === true || !result.success || !result.data){
7809             this.failureType = Roo.form.Action.LOAD_FAILURE;
7810             this.form.afterAction(this, false);
7811             return;
7812         }
7813         this.form.clearInvalid();
7814         this.form.setValues(result.data);
7815         this.form.afterAction(this, true);
7816     },
7817
7818     handleResponse : function(response){
7819         if(this.form.reader){
7820             var rs = this.form.reader.read(response);
7821             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7822             return {
7823                 success : rs.success,
7824                 data : data
7825             };
7826         }
7827         return Roo.decode(response.responseText);
7828     }
7829 });
7830
7831 Roo.form.Action.ACTION_TYPES = {
7832     'load' : Roo.form.Action.Load,
7833     'submit' : Roo.form.Action.Submit
7834 };/*
7835  * - LGPL
7836  *
7837  * form
7838  *
7839  */
7840
7841 /**
7842  * @class Roo.bootstrap.Form
7843  * @extends Roo.bootstrap.Component
7844  * Bootstrap Form class
7845  * @cfg {String} method  GET | POST (default POST)
7846  * @cfg {String} labelAlign top | left (default top)
7847  * @cfg {String} align left  | right - for navbars
7848  * @cfg {Boolean} loadMask load mask when submit (default true)
7849
7850  *
7851  * @constructor
7852  * Create a new Form
7853  * @param {Object} config The config object
7854  */
7855
7856
7857 Roo.bootstrap.Form = function(config){
7858     
7859     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7860     
7861     Roo.bootstrap.Form.popover.apply();
7862     
7863     this.addEvents({
7864         /**
7865          * @event clientvalidation
7866          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7867          * @param {Form} this
7868          * @param {Boolean} valid true if the form has passed client-side validation
7869          */
7870         clientvalidation: true,
7871         /**
7872          * @event beforeaction
7873          * Fires before any action is performed. Return false to cancel the action.
7874          * @param {Form} this
7875          * @param {Action} action The action to be performed
7876          */
7877         beforeaction: true,
7878         /**
7879          * @event actionfailed
7880          * Fires when an action fails.
7881          * @param {Form} this
7882          * @param {Action} action The action that failed
7883          */
7884         actionfailed : true,
7885         /**
7886          * @event actioncomplete
7887          * Fires when an action is completed.
7888          * @param {Form} this
7889          * @param {Action} action The action that completed
7890          */
7891         actioncomplete : true
7892     });
7893 };
7894
7895 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7896
7897      /**
7898      * @cfg {String} method
7899      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7900      */
7901     method : 'POST',
7902     /**
7903      * @cfg {String} url
7904      * The URL to use for form actions if one isn't supplied in the action options.
7905      */
7906     /**
7907      * @cfg {Boolean} fileUpload
7908      * Set to true if this form is a file upload.
7909      */
7910
7911     /**
7912      * @cfg {Object} baseParams
7913      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7914      */
7915
7916     /**
7917      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7918      */
7919     timeout: 30,
7920     /**
7921      * @cfg {Sting} align (left|right) for navbar forms
7922      */
7923     align : 'left',
7924
7925     // private
7926     activeAction : null,
7927
7928     /**
7929      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7930      * element by passing it or its id or mask the form itself by passing in true.
7931      * @type Mixed
7932      */
7933     waitMsgTarget : false,
7934
7935     loadMask : true,
7936     
7937     /**
7938      * @cfg {Boolean} errorMask (true|false) default false
7939      */
7940     errorMask : false,
7941     
7942     /**
7943      * @cfg {Number} maskOffset Default 100
7944      */
7945     maskOffset : 100,
7946     
7947     /**
7948      * @cfg {Boolean} maskBody
7949      */
7950     maskBody : false,
7951
7952     getAutoCreate : function(){
7953
7954         var cfg = {
7955             tag: 'form',
7956             method : this.method || 'POST',
7957             id : this.id || Roo.id(),
7958             cls : ''
7959         };
7960         if (this.parent().xtype.match(/^Nav/)) {
7961             cfg.cls = 'navbar-form navbar-' + this.align;
7962
7963         }
7964
7965         if (this.labelAlign == 'left' ) {
7966             cfg.cls += ' form-horizontal';
7967         }
7968
7969
7970         return cfg;
7971     },
7972     initEvents : function()
7973     {
7974         this.el.on('submit', this.onSubmit, this);
7975         // this was added as random key presses on the form where triggering form submit.
7976         this.el.on('keypress', function(e) {
7977             if (e.getCharCode() != 13) {
7978                 return true;
7979             }
7980             // we might need to allow it for textareas.. and some other items.
7981             // check e.getTarget().
7982
7983             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7984                 return true;
7985             }
7986
7987             Roo.log("keypress blocked");
7988
7989             e.preventDefault();
7990             return false;
7991         });
7992         
7993     },
7994     // private
7995     onSubmit : function(e){
7996         e.stopEvent();
7997     },
7998
7999      /**
8000      * Returns true if client-side validation on the form is successful.
8001      * @return Boolean
8002      */
8003     isValid : function(){
8004         var items = this.getItems();
8005         var valid = true;
8006         var target = false;
8007         
8008         items.each(function(f){
8009             
8010             if(f.validate()){
8011                 return;
8012             }
8013             
8014             Roo.log('invalid field: ' + f.name);
8015             
8016             valid = false;
8017
8018             if(!target && f.el.isVisible(true)){
8019                 target = f;
8020             }
8021            
8022         });
8023         
8024         if(this.errorMask && !valid){
8025             Roo.bootstrap.Form.popover.mask(this, target);
8026         }
8027         
8028         return valid;
8029     },
8030     
8031     /**
8032      * Returns true if any fields in this form have changed since their original load.
8033      * @return Boolean
8034      */
8035     isDirty : function(){
8036         var dirty = false;
8037         var items = this.getItems();
8038         items.each(function(f){
8039            if(f.isDirty()){
8040                dirty = true;
8041                return false;
8042            }
8043            return true;
8044         });
8045         return dirty;
8046     },
8047      /**
8048      * Performs a predefined action (submit or load) or custom actions you define on this form.
8049      * @param {String} actionName The name of the action type
8050      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8051      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8052      * accept other config options):
8053      * <pre>
8054 Property          Type             Description
8055 ----------------  ---------------  ----------------------------------------------------------------------------------
8056 url               String           The url for the action (defaults to the form's url)
8057 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8058 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8059 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8060                                    validate the form on the client (defaults to false)
8061      * </pre>
8062      * @return {BasicForm} this
8063      */
8064     doAction : function(action, options){
8065         if(typeof action == 'string'){
8066             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8067         }
8068         if(this.fireEvent('beforeaction', this, action) !== false){
8069             this.beforeAction(action);
8070             action.run.defer(100, action);
8071         }
8072         return this;
8073     },
8074
8075     // private
8076     beforeAction : function(action){
8077         var o = action.options;
8078         
8079         if(this.loadMask){
8080             
8081             if(this.maskBody){
8082                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8083             } else {
8084                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8085             }
8086         }
8087         // not really supported yet.. ??
8088
8089         //if(this.waitMsgTarget === true){
8090         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8091         //}else if(this.waitMsgTarget){
8092         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8093         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8094         //}else {
8095         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8096        // }
8097
8098     },
8099
8100     // private
8101     afterAction : function(action, success){
8102         this.activeAction = null;
8103         var o = action.options;
8104
8105         if(this.loadMask){
8106             
8107             if(this.maskBody){
8108                 Roo.get(document.body).unmask();
8109             } else {
8110                 this.el.unmask();
8111             }
8112         }
8113         
8114         //if(this.waitMsgTarget === true){
8115 //            this.el.unmask();
8116         //}else if(this.waitMsgTarget){
8117         //    this.waitMsgTarget.unmask();
8118         //}else{
8119         //    Roo.MessageBox.updateProgress(1);
8120         //    Roo.MessageBox.hide();
8121        // }
8122         //
8123         if(success){
8124             if(o.reset){
8125                 this.reset();
8126             }
8127             Roo.callback(o.success, o.scope, [this, action]);
8128             this.fireEvent('actioncomplete', this, action);
8129
8130         }else{
8131
8132             // failure condition..
8133             // we have a scenario where updates need confirming.
8134             // eg. if a locking scenario exists..
8135             // we look for { errors : { needs_confirm : true }} in the response.
8136             if (
8137                 (typeof(action.result) != 'undefined')  &&
8138                 (typeof(action.result.errors) != 'undefined')  &&
8139                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8140            ){
8141                 var _t = this;
8142                 Roo.log("not supported yet");
8143                  /*
8144
8145                 Roo.MessageBox.confirm(
8146                     "Change requires confirmation",
8147                     action.result.errorMsg,
8148                     function(r) {
8149                         if (r != 'yes') {
8150                             return;
8151                         }
8152                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8153                     }
8154
8155                 );
8156                 */
8157
8158
8159                 return;
8160             }
8161
8162             Roo.callback(o.failure, o.scope, [this, action]);
8163             // show an error message if no failed handler is set..
8164             if (!this.hasListener('actionfailed')) {
8165                 Roo.log("need to add dialog support");
8166                 /*
8167                 Roo.MessageBox.alert("Error",
8168                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8169                         action.result.errorMsg :
8170                         "Saving Failed, please check your entries or try again"
8171                 );
8172                 */
8173             }
8174
8175             this.fireEvent('actionfailed', this, action);
8176         }
8177
8178     },
8179     /**
8180      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8181      * @param {String} id The value to search for
8182      * @return Field
8183      */
8184     findField : function(id){
8185         var items = this.getItems();
8186         var field = items.get(id);
8187         if(!field){
8188              items.each(function(f){
8189                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8190                     field = f;
8191                     return false;
8192                 }
8193                 return true;
8194             });
8195         }
8196         return field || null;
8197     },
8198      /**
8199      * Mark fields in this form invalid in bulk.
8200      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8201      * @return {BasicForm} this
8202      */
8203     markInvalid : function(errors){
8204         if(errors instanceof Array){
8205             for(var i = 0, len = errors.length; i < len; i++){
8206                 var fieldError = errors[i];
8207                 var f = this.findField(fieldError.id);
8208                 if(f){
8209                     f.markInvalid(fieldError.msg);
8210                 }
8211             }
8212         }else{
8213             var field, id;
8214             for(id in errors){
8215                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8216                     field.markInvalid(errors[id]);
8217                 }
8218             }
8219         }
8220         //Roo.each(this.childForms || [], function (f) {
8221         //    f.markInvalid(errors);
8222         //});
8223
8224         return this;
8225     },
8226
8227     /**
8228      * Set values for fields in this form in bulk.
8229      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8230      * @return {BasicForm} this
8231      */
8232     setValues : function(values){
8233         if(values instanceof Array){ // array of objects
8234             for(var i = 0, len = values.length; i < len; i++){
8235                 var v = values[i];
8236                 var f = this.findField(v.id);
8237                 if(f){
8238                     f.setValue(v.value);
8239                     if(this.trackResetOnLoad){
8240                         f.originalValue = f.getValue();
8241                     }
8242                 }
8243             }
8244         }else{ // object hash
8245             var field, id;
8246             for(id in values){
8247                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8248
8249                     if (field.setFromData &&
8250                         field.valueField &&
8251                         field.displayField &&
8252                         // combos' with local stores can
8253                         // be queried via setValue()
8254                         // to set their value..
8255                         (field.store && !field.store.isLocal)
8256                         ) {
8257                         // it's a combo
8258                         var sd = { };
8259                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8260                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8261                         field.setFromData(sd);
8262
8263                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8264                         
8265                         field.setFromData(values);
8266                         
8267                     } else {
8268                         field.setValue(values[id]);
8269                     }
8270
8271
8272                     if(this.trackResetOnLoad){
8273                         field.originalValue = field.getValue();
8274                     }
8275                 }
8276             }
8277         }
8278
8279         //Roo.each(this.childForms || [], function (f) {
8280         //    f.setValues(values);
8281         //});
8282
8283         return this;
8284     },
8285
8286     /**
8287      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8288      * they are returned as an array.
8289      * @param {Boolean} asString
8290      * @return {Object}
8291      */
8292     getValues : function(asString){
8293         //if (this.childForms) {
8294             // copy values from the child forms
8295         //    Roo.each(this.childForms, function (f) {
8296         //        this.setValues(f.getValues());
8297         //    }, this);
8298         //}
8299
8300
8301
8302         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8303         if(asString === true){
8304             return fs;
8305         }
8306         return Roo.urlDecode(fs);
8307     },
8308
8309     /**
8310      * Returns the fields in this form as an object with key/value pairs.
8311      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8312      * @return {Object}
8313      */
8314     getFieldValues : function(with_hidden)
8315     {
8316         var items = this.getItems();
8317         var ret = {};
8318         items.each(function(f){
8319             
8320             if (!f.getName()) {
8321                 return;
8322             }
8323             
8324             var v = f.getValue();
8325             
8326             if (f.inputType =='radio') {
8327                 if (typeof(ret[f.getName()]) == 'undefined') {
8328                     ret[f.getName()] = ''; // empty..
8329                 }
8330
8331                 if (!f.el.dom.checked) {
8332                     return;
8333
8334                 }
8335                 v = f.el.dom.value;
8336
8337             }
8338             
8339             if(f.xtype == 'MoneyField'){
8340                 ret[f.currencyName] = f.getCurrency();
8341             }
8342
8343             // not sure if this supported any more..
8344             if ((typeof(v) == 'object') && f.getRawValue) {
8345                 v = f.getRawValue() ; // dates..
8346             }
8347             // combo boxes where name != hiddenName...
8348             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8349                 ret[f.name] = f.getRawValue();
8350             }
8351             ret[f.getName()] = v;
8352         });
8353
8354         return ret;
8355     },
8356
8357     /**
8358      * Clears all invalid messages in this form.
8359      * @return {BasicForm} this
8360      */
8361     clearInvalid : function(){
8362         var items = this.getItems();
8363
8364         items.each(function(f){
8365            f.clearInvalid();
8366         });
8367
8368         return this;
8369     },
8370
8371     /**
8372      * Resets this form.
8373      * @return {BasicForm} this
8374      */
8375     reset : function(){
8376         var items = this.getItems();
8377         items.each(function(f){
8378             f.reset();
8379         });
8380
8381         Roo.each(this.childForms || [], function (f) {
8382             f.reset();
8383         });
8384
8385
8386         return this;
8387     },
8388     
8389     getItems : function()
8390     {
8391         var r=new Roo.util.MixedCollection(false, function(o){
8392             return o.id || (o.id = Roo.id());
8393         });
8394         var iter = function(el) {
8395             if (el.inputEl) {
8396                 r.add(el);
8397             }
8398             if (!el.items) {
8399                 return;
8400             }
8401             Roo.each(el.items,function(e) {
8402                 iter(e);
8403             });
8404         };
8405
8406         iter(this);
8407         return r;
8408     },
8409     
8410     hideFields : function(items)
8411     {
8412         Roo.each(items, function(i){
8413             
8414             var f = this.findField(i);
8415             
8416             if(!f){
8417                 return;
8418             }
8419             
8420             f.hide();
8421             
8422         }, this);
8423     },
8424     
8425     showFields : function(items)
8426     {
8427         Roo.each(items, function(i){
8428             
8429             var f = this.findField(i);
8430             
8431             if(!f){
8432                 return;
8433             }
8434             
8435             f.show();
8436             
8437         }, this);
8438     }
8439
8440 });
8441
8442 Roo.apply(Roo.bootstrap.Form, {
8443     
8444     popover : {
8445         
8446         padding : 5,
8447         
8448         isApplied : false,
8449         
8450         isMasked : false,
8451         
8452         form : false,
8453         
8454         target : false,
8455         
8456         toolTip : false,
8457         
8458         intervalID : false,
8459         
8460         maskEl : false,
8461         
8462         apply : function()
8463         {
8464             if(this.isApplied){
8465                 return;
8466             }
8467             
8468             this.maskEl = {
8469                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8470                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8471                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8472                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8473             };
8474             
8475             this.maskEl.top.enableDisplayMode("block");
8476             this.maskEl.left.enableDisplayMode("block");
8477             this.maskEl.bottom.enableDisplayMode("block");
8478             this.maskEl.right.enableDisplayMode("block");
8479             
8480             this.toolTip = new Roo.bootstrap.Tooltip({
8481                 cls : 'roo-form-error-popover',
8482                 alignment : {
8483                     'left' : ['r-l', [-2,0], 'right'],
8484                     'right' : ['l-r', [2,0], 'left'],
8485                     'bottom' : ['tl-bl', [0,2], 'top'],
8486                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8487                 }
8488             });
8489             
8490             this.toolTip.render(Roo.get(document.body));
8491
8492             this.toolTip.el.enableDisplayMode("block");
8493             
8494             Roo.get(document.body).on('click', function(){
8495                 this.unmask();
8496             }, this);
8497             
8498             Roo.get(document.body).on('touchstart', function(){
8499                 this.unmask();
8500             }, this);
8501             
8502             this.isApplied = true
8503         },
8504         
8505         mask : function(form, target)
8506         {
8507             this.form = form;
8508             
8509             this.target = target;
8510             
8511             if(!this.form.errorMask || !target.el){
8512                 return;
8513             }
8514             
8515             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8516             
8517             Roo.log(scrollable);
8518             
8519             var ot = this.target.el.calcOffsetsTo(scrollable);
8520             
8521             var scrollTo = ot[1] - this.form.maskOffset;
8522             
8523             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8524             
8525             scrollable.scrollTo('top', scrollTo);
8526             
8527             var box = this.target.el.getBox();
8528             Roo.log(box);
8529             var zIndex = Roo.bootstrap.Modal.zIndex++;
8530
8531             
8532             this.maskEl.top.setStyle('position', 'absolute');
8533             this.maskEl.top.setStyle('z-index', zIndex);
8534             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8535             this.maskEl.top.setLeft(0);
8536             this.maskEl.top.setTop(0);
8537             this.maskEl.top.show();
8538             
8539             this.maskEl.left.setStyle('position', 'absolute');
8540             this.maskEl.left.setStyle('z-index', zIndex);
8541             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8542             this.maskEl.left.setLeft(0);
8543             this.maskEl.left.setTop(box.y - this.padding);
8544             this.maskEl.left.show();
8545
8546             this.maskEl.bottom.setStyle('position', 'absolute');
8547             this.maskEl.bottom.setStyle('z-index', zIndex);
8548             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8549             this.maskEl.bottom.setLeft(0);
8550             this.maskEl.bottom.setTop(box.bottom + this.padding);
8551             this.maskEl.bottom.show();
8552
8553             this.maskEl.right.setStyle('position', 'absolute');
8554             this.maskEl.right.setStyle('z-index', zIndex);
8555             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8556             this.maskEl.right.setLeft(box.right + this.padding);
8557             this.maskEl.right.setTop(box.y - this.padding);
8558             this.maskEl.right.show();
8559
8560             this.toolTip.bindEl = this.target.el;
8561
8562             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8563
8564             var tip = this.target.blankText;
8565
8566             if(this.target.getValue() !== '' ) {
8567                 
8568                 if (this.target.invalidText.length) {
8569                     tip = this.target.invalidText;
8570                 } else if (this.target.regexText.length){
8571                     tip = this.target.regexText;
8572                 }
8573             }
8574
8575             this.toolTip.show(tip);
8576
8577             this.intervalID = window.setInterval(function() {
8578                 Roo.bootstrap.Form.popover.unmask();
8579             }, 10000);
8580
8581             window.onwheel = function(){ return false;};
8582             
8583             (function(){ this.isMasked = true; }).defer(500, this);
8584             
8585         },
8586         
8587         unmask : function()
8588         {
8589             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8590                 return;
8591             }
8592             
8593             this.maskEl.top.setStyle('position', 'absolute');
8594             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8595             this.maskEl.top.hide();
8596
8597             this.maskEl.left.setStyle('position', 'absolute');
8598             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8599             this.maskEl.left.hide();
8600
8601             this.maskEl.bottom.setStyle('position', 'absolute');
8602             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8603             this.maskEl.bottom.hide();
8604
8605             this.maskEl.right.setStyle('position', 'absolute');
8606             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8607             this.maskEl.right.hide();
8608             
8609             this.toolTip.hide();
8610             
8611             this.toolTip.el.hide();
8612             
8613             window.onwheel = function(){ return true;};
8614             
8615             if(this.intervalID){
8616                 window.clearInterval(this.intervalID);
8617                 this.intervalID = false;
8618             }
8619             
8620             this.isMasked = false;
8621             
8622         }
8623         
8624     }
8625     
8626 });
8627
8628 /*
8629  * Based on:
8630  * Ext JS Library 1.1.1
8631  * Copyright(c) 2006-2007, Ext JS, LLC.
8632  *
8633  * Originally Released Under LGPL - original licence link has changed is not relivant.
8634  *
8635  * Fork - LGPL
8636  * <script type="text/javascript">
8637  */
8638 /**
8639  * @class Roo.form.VTypes
8640  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8641  * @singleton
8642  */
8643 Roo.form.VTypes = function(){
8644     // closure these in so they are only created once.
8645     var alpha = /^[a-zA-Z_]+$/;
8646     var alphanum = /^[a-zA-Z0-9_]+$/;
8647     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8648     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8649
8650     // All these messages and functions are configurable
8651     return {
8652         /**
8653          * The function used to validate email addresses
8654          * @param {String} value The email address
8655          */
8656         'email' : function(v){
8657             return email.test(v);
8658         },
8659         /**
8660          * The error text to display when the email validation function returns false
8661          * @type String
8662          */
8663         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8664         /**
8665          * The keystroke filter mask to be applied on email input
8666          * @type RegExp
8667          */
8668         'emailMask' : /[a-z0-9_\.\-@]/i,
8669
8670         /**
8671          * The function used to validate URLs
8672          * @param {String} value The URL
8673          */
8674         'url' : function(v){
8675             return url.test(v);
8676         },
8677         /**
8678          * The error text to display when the url validation function returns false
8679          * @type String
8680          */
8681         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8682         
8683         /**
8684          * The function used to validate alpha values
8685          * @param {String} value The value
8686          */
8687         'alpha' : function(v){
8688             return alpha.test(v);
8689         },
8690         /**
8691          * The error text to display when the alpha validation function returns false
8692          * @type String
8693          */
8694         'alphaText' : 'This field should only contain letters and _',
8695         /**
8696          * The keystroke filter mask to be applied on alpha input
8697          * @type RegExp
8698          */
8699         'alphaMask' : /[a-z_]/i,
8700
8701         /**
8702          * The function used to validate alphanumeric values
8703          * @param {String} value The value
8704          */
8705         'alphanum' : function(v){
8706             return alphanum.test(v);
8707         },
8708         /**
8709          * The error text to display when the alphanumeric validation function returns false
8710          * @type String
8711          */
8712         'alphanumText' : 'This field should only contain letters, numbers and _',
8713         /**
8714          * The keystroke filter mask to be applied on alphanumeric input
8715          * @type RegExp
8716          */
8717         'alphanumMask' : /[a-z0-9_]/i
8718     };
8719 }();/*
8720  * - LGPL
8721  *
8722  * Input
8723  * 
8724  */
8725
8726 /**
8727  * @class Roo.bootstrap.Input
8728  * @extends Roo.bootstrap.Component
8729  * Bootstrap Input class
8730  * @cfg {Boolean} disabled is it disabled
8731  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8732  * @cfg {String} name name of the input
8733  * @cfg {string} fieldLabel - the label associated
8734  * @cfg {string} placeholder - placeholder to put in text.
8735  * @cfg {string}  before - input group add on before
8736  * @cfg {string} after - input group add on after
8737  * @cfg {string} size - (lg|sm) or leave empty..
8738  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8739  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8740  * @cfg {Number} md colspan out of 12 for computer-sized screens
8741  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8742  * @cfg {string} value default value of the input
8743  * @cfg {Number} labelWidth set the width of label 
8744  * @cfg {Number} labellg set the width of label (1-12)
8745  * @cfg {Number} labelmd set the width of label (1-12)
8746  * @cfg {Number} labelsm set the width of label (1-12)
8747  * @cfg {Number} labelxs set the width of label (1-12)
8748  * @cfg {String} labelAlign (top|left)
8749  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8750  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8751  * @cfg {String} indicatorpos (left|right) default left
8752  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8753  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8754
8755  * @cfg {String} align (left|center|right) Default left
8756  * @cfg {Boolean} forceFeedback (true|false) Default false
8757  * 
8758  * @constructor
8759  * Create a new Input
8760  * @param {Object} config The config object
8761  */
8762
8763 Roo.bootstrap.Input = function(config){
8764     
8765     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8766     
8767     this.addEvents({
8768         /**
8769          * @event focus
8770          * Fires when this field receives input focus.
8771          * @param {Roo.form.Field} this
8772          */
8773         focus : true,
8774         /**
8775          * @event blur
8776          * Fires when this field loses input focus.
8777          * @param {Roo.form.Field} this
8778          */
8779         blur : true,
8780         /**
8781          * @event specialkey
8782          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8783          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8784          * @param {Roo.form.Field} this
8785          * @param {Roo.EventObject} e The event object
8786          */
8787         specialkey : true,
8788         /**
8789          * @event change
8790          * Fires just before the field blurs if the field value has changed.
8791          * @param {Roo.form.Field} this
8792          * @param {Mixed} newValue The new value
8793          * @param {Mixed} oldValue The original value
8794          */
8795         change : true,
8796         /**
8797          * @event invalid
8798          * Fires after the field has been marked as invalid.
8799          * @param {Roo.form.Field} this
8800          * @param {String} msg The validation message
8801          */
8802         invalid : true,
8803         /**
8804          * @event valid
8805          * Fires after the field has been validated with no errors.
8806          * @param {Roo.form.Field} this
8807          */
8808         valid : true,
8809          /**
8810          * @event keyup
8811          * Fires after the key up
8812          * @param {Roo.form.Field} this
8813          * @param {Roo.EventObject}  e The event Object
8814          */
8815         keyup : true
8816     });
8817 };
8818
8819 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8820      /**
8821      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8822       automatic validation (defaults to "keyup").
8823      */
8824     validationEvent : "keyup",
8825      /**
8826      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8827      */
8828     validateOnBlur : true,
8829     /**
8830      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8831      */
8832     validationDelay : 250,
8833      /**
8834      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8835      */
8836     focusClass : "x-form-focus",  // not needed???
8837     
8838        
8839     /**
8840      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8841      */
8842     invalidClass : "has-warning",
8843     
8844     /**
8845      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8846      */
8847     validClass : "has-success",
8848     
8849     /**
8850      * @cfg {Boolean} hasFeedback (true|false) default true
8851      */
8852     hasFeedback : true,
8853     
8854     /**
8855      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8856      */
8857     invalidFeedbackClass : "glyphicon-warning-sign",
8858     
8859     /**
8860      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8861      */
8862     validFeedbackClass : "glyphicon-ok",
8863     
8864     /**
8865      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8866      */
8867     selectOnFocus : false,
8868     
8869      /**
8870      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8871      */
8872     maskRe : null,
8873        /**
8874      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8875      */
8876     vtype : null,
8877     
8878       /**
8879      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8880      */
8881     disableKeyFilter : false,
8882     
8883        /**
8884      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8885      */
8886     disabled : false,
8887      /**
8888      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8889      */
8890     allowBlank : true,
8891     /**
8892      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8893      */
8894     blankText : "Please complete this mandatory field",
8895     
8896      /**
8897      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8898      */
8899     minLength : 0,
8900     /**
8901      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8902      */
8903     maxLength : Number.MAX_VALUE,
8904     /**
8905      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8906      */
8907     minLengthText : "The minimum length for this field is {0}",
8908     /**
8909      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8910      */
8911     maxLengthText : "The maximum length for this field is {0}",
8912   
8913     
8914     /**
8915      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8916      * If available, this function will be called only after the basic validators all return true, and will be passed the
8917      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8918      */
8919     validator : null,
8920     /**
8921      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8922      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8923      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8924      */
8925     regex : null,
8926     /**
8927      * @cfg {String} regexText -- Depricated - use Invalid Text
8928      */
8929     regexText : "",
8930     
8931     /**
8932      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8933      */
8934     invalidText : "",
8935     
8936     
8937     
8938     autocomplete: false,
8939     
8940     
8941     fieldLabel : '',
8942     inputType : 'text',
8943     
8944     name : false,
8945     placeholder: false,
8946     before : false,
8947     after : false,
8948     size : false,
8949     hasFocus : false,
8950     preventMark: false,
8951     isFormField : true,
8952     value : '',
8953     labelWidth : 2,
8954     labelAlign : false,
8955     readOnly : false,
8956     align : false,
8957     formatedValue : false,
8958     forceFeedback : false,
8959     
8960     indicatorpos : 'left',
8961     
8962     labellg : 0,
8963     labelmd : 0,
8964     labelsm : 0,
8965     labelxs : 0,
8966     
8967     capture : '',
8968     accept : '',
8969     
8970     parentLabelAlign : function()
8971     {
8972         var parent = this;
8973         while (parent.parent()) {
8974             parent = parent.parent();
8975             if (typeof(parent.labelAlign) !='undefined') {
8976                 return parent.labelAlign;
8977             }
8978         }
8979         return 'left';
8980         
8981     },
8982     
8983     getAutoCreate : function()
8984     {
8985         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8986         
8987         var id = Roo.id();
8988         
8989         var cfg = {};
8990         
8991         if(this.inputType != 'hidden'){
8992             cfg.cls = 'form-group' //input-group
8993         }
8994         
8995         var input =  {
8996             tag: 'input',
8997             id : id,
8998             type : this.inputType,
8999             value : this.value,
9000             cls : 'form-control',
9001             placeholder : this.placeholder || '',
9002             autocomplete : this.autocomplete || 'new-password'
9003         };
9004         
9005         if(this.capture.length){
9006             input.capture = this.capture;
9007         }
9008         
9009         if(this.accept.length){
9010             input.accept = this.accept + "/*";
9011         }
9012         
9013         if(this.align){
9014             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9015         }
9016         
9017         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9018             input.maxLength = this.maxLength;
9019         }
9020         
9021         if (this.disabled) {
9022             input.disabled=true;
9023         }
9024         
9025         if (this.readOnly) {
9026             input.readonly=true;
9027         }
9028         
9029         if (this.name) {
9030             input.name = this.name;
9031         }
9032         
9033         if (this.size) {
9034             input.cls += ' input-' + this.size;
9035         }
9036         
9037         var settings=this;
9038         ['xs','sm','md','lg'].map(function(size){
9039             if (settings[size]) {
9040                 cfg.cls += ' col-' + size + '-' + settings[size];
9041             }
9042         });
9043         
9044         var inputblock = input;
9045         
9046         var feedback = {
9047             tag: 'span',
9048             cls: 'glyphicon form-control-feedback'
9049         };
9050             
9051         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9052             
9053             inputblock = {
9054                 cls : 'has-feedback',
9055                 cn :  [
9056                     input,
9057                     feedback
9058                 ] 
9059             };  
9060         }
9061         
9062         if (this.before || this.after) {
9063             
9064             inputblock = {
9065                 cls : 'input-group',
9066                 cn :  [] 
9067             };
9068             
9069             if (this.before && typeof(this.before) == 'string') {
9070                 
9071                 inputblock.cn.push({
9072                     tag :'span',
9073                     cls : 'roo-input-before input-group-addon',
9074                     html : this.before
9075                 });
9076             }
9077             if (this.before && typeof(this.before) == 'object') {
9078                 this.before = Roo.factory(this.before);
9079                 
9080                 inputblock.cn.push({
9081                     tag :'span',
9082                     cls : 'roo-input-before input-group-' +
9083                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9084                 });
9085             }
9086             
9087             inputblock.cn.push(input);
9088             
9089             if (this.after && typeof(this.after) == 'string') {
9090                 inputblock.cn.push({
9091                     tag :'span',
9092                     cls : 'roo-input-after input-group-addon',
9093                     html : this.after
9094                 });
9095             }
9096             if (this.after && typeof(this.after) == 'object') {
9097                 this.after = Roo.factory(this.after);
9098                 
9099                 inputblock.cn.push({
9100                     tag :'span',
9101                     cls : 'roo-input-after input-group-' +
9102                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9103                 });
9104             }
9105             
9106             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9107                 inputblock.cls += ' has-feedback';
9108                 inputblock.cn.push(feedback);
9109             }
9110         };
9111         
9112         if (align ==='left' && this.fieldLabel.length) {
9113             
9114             cfg.cls += ' roo-form-group-label-left';
9115             
9116             cfg.cn = [
9117                 {
9118                     tag : 'i',
9119                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9120                     tooltip : 'This field is required'
9121                 },
9122                 {
9123                     tag: 'label',
9124                     'for' :  id,
9125                     cls : 'control-label',
9126                     html : this.fieldLabel
9127
9128                 },
9129                 {
9130                     cls : "", 
9131                     cn: [
9132                         inputblock
9133                     ]
9134                 }
9135             ];
9136             
9137             var labelCfg = cfg.cn[1];
9138             var contentCfg = cfg.cn[2];
9139             
9140             if(this.indicatorpos == 'right'){
9141                 cfg.cn = [
9142                     {
9143                         tag: 'label',
9144                         'for' :  id,
9145                         cls : 'control-label',
9146                         cn : [
9147                             {
9148                                 tag : 'span',
9149                                 html : this.fieldLabel
9150                             },
9151                             {
9152                                 tag : 'i',
9153                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9154                                 tooltip : 'This field is required'
9155                             }
9156                         ]
9157                     },
9158                     {
9159                         cls : "",
9160                         cn: [
9161                             inputblock
9162                         ]
9163                     }
9164
9165                 ];
9166                 
9167                 labelCfg = cfg.cn[0];
9168                 contentCfg = cfg.cn[1];
9169             
9170             }
9171             
9172             if(this.labelWidth > 12){
9173                 labelCfg.style = "width: " + this.labelWidth + 'px';
9174             }
9175             
9176             if(this.labelWidth < 13 && this.labelmd == 0){
9177                 this.labelmd = this.labelWidth;
9178             }
9179             
9180             if(this.labellg > 0){
9181                 labelCfg.cls += ' col-lg-' + this.labellg;
9182                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9183             }
9184             
9185             if(this.labelmd > 0){
9186                 labelCfg.cls += ' col-md-' + this.labelmd;
9187                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9188             }
9189             
9190             if(this.labelsm > 0){
9191                 labelCfg.cls += ' col-sm-' + this.labelsm;
9192                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9193             }
9194             
9195             if(this.labelxs > 0){
9196                 labelCfg.cls += ' col-xs-' + this.labelxs;
9197                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9198             }
9199             
9200             
9201         } else if ( this.fieldLabel.length) {
9202                 
9203             cfg.cn = [
9204                 {
9205                     tag : 'i',
9206                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9207                     tooltip : 'This field is required'
9208                 },
9209                 {
9210                     tag: 'label',
9211                    //cls : 'input-group-addon',
9212                     html : this.fieldLabel
9213
9214                 },
9215
9216                inputblock
9217
9218            ];
9219            
9220            if(this.indicatorpos == 'right'){
9221                 
9222                 cfg.cn = [
9223                     {
9224                         tag: 'label',
9225                        //cls : 'input-group-addon',
9226                         html : this.fieldLabel
9227
9228                     },
9229                     {
9230                         tag : 'i',
9231                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9232                         tooltip : 'This field is required'
9233                     },
9234
9235                    inputblock
9236
9237                ];
9238
9239             }
9240
9241         } else {
9242             
9243             cfg.cn = [
9244
9245                     inputblock
9246
9247             ];
9248                 
9249                 
9250         };
9251         
9252         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9253            cfg.cls += ' navbar-form';
9254         }
9255         
9256         if (this.parentType === 'NavGroup') {
9257            cfg.cls += ' navbar-form';
9258            cfg.tag = 'li';
9259         }
9260         
9261         return cfg;
9262         
9263     },
9264     /**
9265      * return the real input element.
9266      */
9267     inputEl: function ()
9268     {
9269         return this.el.select('input.form-control',true).first();
9270     },
9271     
9272     tooltipEl : function()
9273     {
9274         return this.inputEl();
9275     },
9276     
9277     indicatorEl : function()
9278     {
9279         var indicator = this.el.select('i.roo-required-indicator',true).first();
9280         
9281         if(!indicator){
9282             return false;
9283         }
9284         
9285         return indicator;
9286         
9287     },
9288     
9289     setDisabled : function(v)
9290     {
9291         var i  = this.inputEl().dom;
9292         if (!v) {
9293             i.removeAttribute('disabled');
9294             return;
9295             
9296         }
9297         i.setAttribute('disabled','true');
9298     },
9299     initEvents : function()
9300     {
9301           
9302         this.inputEl().on("keydown" , this.fireKey,  this);
9303         this.inputEl().on("focus", this.onFocus,  this);
9304         this.inputEl().on("blur", this.onBlur,  this);
9305         
9306         this.inputEl().relayEvent('keyup', this);
9307         
9308         this.indicator = this.indicatorEl();
9309         
9310         if(this.indicator){
9311             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9312         }
9313  
9314         // reference to original value for reset
9315         this.originalValue = this.getValue();
9316         //Roo.form.TextField.superclass.initEvents.call(this);
9317         if(this.validationEvent == 'keyup'){
9318             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9319             this.inputEl().on('keyup', this.filterValidation, this);
9320         }
9321         else if(this.validationEvent !== false){
9322             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9323         }
9324         
9325         if(this.selectOnFocus){
9326             this.on("focus", this.preFocus, this);
9327             
9328         }
9329         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9330             this.inputEl().on("keypress", this.filterKeys, this);
9331         } else {
9332             this.inputEl().relayEvent('keypress', this);
9333         }
9334        /* if(this.grow){
9335             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9336             this.el.on("click", this.autoSize,  this);
9337         }
9338         */
9339         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9340             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9341         }
9342         
9343         if (typeof(this.before) == 'object') {
9344             this.before.render(this.el.select('.roo-input-before',true).first());
9345         }
9346         if (typeof(this.after) == 'object') {
9347             this.after.render(this.el.select('.roo-input-after',true).first());
9348         }
9349         
9350         this.inputEl().on('change', this.onChange, this);
9351         
9352     },
9353     filterValidation : function(e){
9354         if(!e.isNavKeyPress()){
9355             this.validationTask.delay(this.validationDelay);
9356         }
9357     },
9358      /**
9359      * Validates the field value
9360      * @return {Boolean} True if the value is valid, else false
9361      */
9362     validate : function(){
9363         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9364         if(this.disabled || this.validateValue(this.getRawValue())){
9365             this.markValid();
9366             return true;
9367         }
9368         
9369         this.markInvalid();
9370         return false;
9371     },
9372     
9373     
9374     /**
9375      * Validates a value according to the field's validation rules and marks the field as invalid
9376      * if the validation fails
9377      * @param {Mixed} value The value to validate
9378      * @return {Boolean} True if the value is valid, else false
9379      */
9380     validateValue : function(value)
9381     {
9382         if(this.getVisibilityEl().hasClass('hidden')){
9383             return true;
9384         }
9385         
9386         if(value.length < 1)  { // if it's blank
9387             if(this.allowBlank){
9388                 return true;
9389             }
9390             return false;
9391         }
9392         
9393         if(value.length < this.minLength){
9394             return false;
9395         }
9396         if(value.length > this.maxLength){
9397             return false;
9398         }
9399         if(this.vtype){
9400             var vt = Roo.form.VTypes;
9401             if(!vt[this.vtype](value, this)){
9402                 return false;
9403             }
9404         }
9405         if(typeof this.validator == "function"){
9406             var msg = this.validator(value);
9407             if(msg !== true){
9408                 return false;
9409             }
9410             if (typeof(msg) == 'string') {
9411                 this.invalidText = msg;
9412             }
9413         }
9414         
9415         if(this.regex && !this.regex.test(value)){
9416             return false;
9417         }
9418         
9419         return true;
9420     },
9421     
9422      // private
9423     fireKey : function(e){
9424         //Roo.log('field ' + e.getKey());
9425         if(e.isNavKeyPress()){
9426             this.fireEvent("specialkey", this, e);
9427         }
9428     },
9429     focus : function (selectText){
9430         if(this.rendered){
9431             this.inputEl().focus();
9432             if(selectText === true){
9433                 this.inputEl().dom.select();
9434             }
9435         }
9436         return this;
9437     } ,
9438     
9439     onFocus : function(){
9440         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9441            // this.el.addClass(this.focusClass);
9442         }
9443         if(!this.hasFocus){
9444             this.hasFocus = true;
9445             this.startValue = this.getValue();
9446             this.fireEvent("focus", this);
9447         }
9448     },
9449     
9450     beforeBlur : Roo.emptyFn,
9451
9452     
9453     // private
9454     onBlur : function(){
9455         this.beforeBlur();
9456         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9457             //this.el.removeClass(this.focusClass);
9458         }
9459         this.hasFocus = false;
9460         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9461             this.validate();
9462         }
9463         var v = this.getValue();
9464         if(String(v) !== String(this.startValue)){
9465             this.fireEvent('change', this, v, this.startValue);
9466         }
9467         this.fireEvent("blur", this);
9468     },
9469     
9470     onChange : function(e)
9471     {
9472         var v = this.getValue();
9473         if(String(v) !== String(this.startValue)){
9474             this.fireEvent('change', this, v, this.startValue);
9475         }
9476         
9477     },
9478     
9479     /**
9480      * Resets the current field value to the originally loaded value and clears any validation messages
9481      */
9482     reset : function(){
9483         this.setValue(this.originalValue);
9484         this.validate();
9485     },
9486      /**
9487      * Returns the name of the field
9488      * @return {Mixed} name The name field
9489      */
9490     getName: function(){
9491         return this.name;
9492     },
9493      /**
9494      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9495      * @return {Mixed} value The field value
9496      */
9497     getValue : function(){
9498         
9499         var v = this.inputEl().getValue();
9500         
9501         return v;
9502     },
9503     /**
9504      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9505      * @return {Mixed} value The field value
9506      */
9507     getRawValue : function(){
9508         var v = this.inputEl().getValue();
9509         
9510         return v;
9511     },
9512     
9513     /**
9514      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9515      * @param {Mixed} value The value to set
9516      */
9517     setRawValue : function(v){
9518         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9519     },
9520     
9521     selectText : function(start, end){
9522         var v = this.getRawValue();
9523         if(v.length > 0){
9524             start = start === undefined ? 0 : start;
9525             end = end === undefined ? v.length : end;
9526             var d = this.inputEl().dom;
9527             if(d.setSelectionRange){
9528                 d.setSelectionRange(start, end);
9529             }else if(d.createTextRange){
9530                 var range = d.createTextRange();
9531                 range.moveStart("character", start);
9532                 range.moveEnd("character", v.length-end);
9533                 range.select();
9534             }
9535         }
9536     },
9537     
9538     /**
9539      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9540      * @param {Mixed} value The value to set
9541      */
9542     setValue : function(v){
9543         this.value = v;
9544         if(this.rendered){
9545             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9546             this.validate();
9547         }
9548     },
9549     
9550     /*
9551     processValue : function(value){
9552         if(this.stripCharsRe){
9553             var newValue = value.replace(this.stripCharsRe, '');
9554             if(newValue !== value){
9555                 this.setRawValue(newValue);
9556                 return newValue;
9557             }
9558         }
9559         return value;
9560     },
9561   */
9562     preFocus : function(){
9563         
9564         if(this.selectOnFocus){
9565             this.inputEl().dom.select();
9566         }
9567     },
9568     filterKeys : function(e){
9569         var k = e.getKey();
9570         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9571             return;
9572         }
9573         var c = e.getCharCode(), cc = String.fromCharCode(c);
9574         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9575             return;
9576         }
9577         if(!this.maskRe.test(cc)){
9578             e.stopEvent();
9579         }
9580     },
9581      /**
9582      * Clear any invalid styles/messages for this field
9583      */
9584     clearInvalid : function(){
9585         
9586         if(!this.el || this.preventMark){ // not rendered
9587             return;
9588         }
9589         
9590      
9591         this.el.removeClass(this.invalidClass);
9592         
9593         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9594             
9595             var feedback = this.el.select('.form-control-feedback', true).first();
9596             
9597             if(feedback){
9598                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9599             }
9600             
9601         }
9602         
9603         if(this.indicator){
9604             this.indicator.removeClass('visible');
9605             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9606         }
9607         
9608         this.fireEvent('valid', this);
9609     },
9610     
9611      /**
9612      * Mark this field as valid
9613      */
9614     markValid : function()
9615     {
9616         if(!this.el  || this.preventMark){ // not rendered...
9617             return;
9618         }
9619         
9620         this.el.removeClass([this.invalidClass, this.validClass]);
9621         
9622         var feedback = this.el.select('.form-control-feedback', true).first();
9623             
9624         if(feedback){
9625             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9626         }
9627         
9628         if(this.indicator){
9629             this.indicator.removeClass('visible');
9630             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9631         }
9632         
9633         if(this.disabled){
9634             return;
9635         }
9636         
9637         if(this.allowBlank && !this.getRawValue().length){
9638             return;
9639         }
9640         
9641         this.el.addClass(this.validClass);
9642         
9643         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9644             
9645             var feedback = this.el.select('.form-control-feedback', true).first();
9646             
9647             if(feedback){
9648                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9649                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9650             }
9651             
9652         }
9653         
9654         this.fireEvent('valid', this);
9655     },
9656     
9657      /**
9658      * Mark this field as invalid
9659      * @param {String} msg The validation message
9660      */
9661     markInvalid : function(msg)
9662     {
9663         if(!this.el  || this.preventMark){ // not rendered
9664             return;
9665         }
9666         
9667         this.el.removeClass([this.invalidClass, this.validClass]);
9668         
9669         var feedback = this.el.select('.form-control-feedback', true).first();
9670             
9671         if(feedback){
9672             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9673         }
9674
9675         if(this.disabled){
9676             return;
9677         }
9678         
9679         if(this.allowBlank && !this.getRawValue().length){
9680             return;
9681         }
9682         
9683         if(this.indicator){
9684             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9685             this.indicator.addClass('visible');
9686         }
9687         
9688         this.el.addClass(this.invalidClass);
9689         
9690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9691             
9692             var feedback = this.el.select('.form-control-feedback', true).first();
9693             
9694             if(feedback){
9695                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9696                 
9697                 if(this.getValue().length || this.forceFeedback){
9698                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9699                 }
9700                 
9701             }
9702             
9703         }
9704         
9705         this.fireEvent('invalid', this, msg);
9706     },
9707     // private
9708     SafariOnKeyDown : function(event)
9709     {
9710         // this is a workaround for a password hang bug on chrome/ webkit.
9711         if (this.inputEl().dom.type != 'password') {
9712             return;
9713         }
9714         
9715         var isSelectAll = false;
9716         
9717         if(this.inputEl().dom.selectionEnd > 0){
9718             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9719         }
9720         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9721             event.preventDefault();
9722             this.setValue('');
9723             return;
9724         }
9725         
9726         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9727             
9728             event.preventDefault();
9729             // this is very hacky as keydown always get's upper case.
9730             //
9731             var cc = String.fromCharCode(event.getCharCode());
9732             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9733             
9734         }
9735     },
9736     adjustWidth : function(tag, w){
9737         tag = tag.toLowerCase();
9738         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9739             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9740                 if(tag == 'input'){
9741                     return w + 2;
9742                 }
9743                 if(tag == 'textarea'){
9744                     return w-2;
9745                 }
9746             }else if(Roo.isOpera){
9747                 if(tag == 'input'){
9748                     return w + 2;
9749                 }
9750                 if(tag == 'textarea'){
9751                     return w-2;
9752                 }
9753             }
9754         }
9755         return w;
9756     },
9757     
9758     setFieldLabel : function(v)
9759     {
9760         if(!this.rendered){
9761             return;
9762         }
9763         
9764         if(this.indicator){
9765             var ar = this.el.select('label > span',true);
9766             
9767             if (ar.elements.length) {
9768                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9769                 this.fieldLabel = v;
9770                 return;
9771             }
9772             
9773             var br = this.el.select('label',true);
9774             
9775             if(br.elements.length) {
9776                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9777                 this.fieldLabel = v;
9778                 return;
9779             }
9780             
9781             Roo.log('Cannot Found any of label > span || label in input');
9782             return;
9783         }
9784         
9785         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9786         this.fieldLabel = v;
9787         
9788         
9789     }
9790 });
9791
9792  
9793 /*
9794  * - LGPL
9795  *
9796  * Input
9797  * 
9798  */
9799
9800 /**
9801  * @class Roo.bootstrap.TextArea
9802  * @extends Roo.bootstrap.Input
9803  * Bootstrap TextArea class
9804  * @cfg {Number} cols Specifies the visible width of a text area
9805  * @cfg {Number} rows Specifies the visible number of lines in a text area
9806  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9807  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9808  * @cfg {string} html text
9809  * 
9810  * @constructor
9811  * Create a new TextArea
9812  * @param {Object} config The config object
9813  */
9814
9815 Roo.bootstrap.TextArea = function(config){
9816     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9817    
9818 };
9819
9820 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9821      
9822     cols : false,
9823     rows : 5,
9824     readOnly : false,
9825     warp : 'soft',
9826     resize : false,
9827     value: false,
9828     html: false,
9829     
9830     getAutoCreate : function(){
9831         
9832         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9833         
9834         var id = Roo.id();
9835         
9836         var cfg = {};
9837         
9838         if(this.inputType != 'hidden'){
9839             cfg.cls = 'form-group' //input-group
9840         }
9841         
9842         var input =  {
9843             tag: 'textarea',
9844             id : id,
9845             warp : this.warp,
9846             rows : this.rows,
9847             value : this.value || '',
9848             html: this.html || '',
9849             cls : 'form-control',
9850             placeholder : this.placeholder || '' 
9851             
9852         };
9853         
9854         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9855             input.maxLength = this.maxLength;
9856         }
9857         
9858         if(this.resize){
9859             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9860         }
9861         
9862         if(this.cols){
9863             input.cols = this.cols;
9864         }
9865         
9866         if (this.readOnly) {
9867             input.readonly = true;
9868         }
9869         
9870         if (this.name) {
9871             input.name = this.name;
9872         }
9873         
9874         if (this.size) {
9875             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9876         }
9877         
9878         var settings=this;
9879         ['xs','sm','md','lg'].map(function(size){
9880             if (settings[size]) {
9881                 cfg.cls += ' col-' + size + '-' + settings[size];
9882             }
9883         });
9884         
9885         var inputblock = input;
9886         
9887         if(this.hasFeedback && !this.allowBlank){
9888             
9889             var feedback = {
9890                 tag: 'span',
9891                 cls: 'glyphicon form-control-feedback'
9892             };
9893
9894             inputblock = {
9895                 cls : 'has-feedback',
9896                 cn :  [
9897                     input,
9898                     feedback
9899                 ] 
9900             };  
9901         }
9902         
9903         
9904         if (this.before || this.after) {
9905             
9906             inputblock = {
9907                 cls : 'input-group',
9908                 cn :  [] 
9909             };
9910             if (this.before) {
9911                 inputblock.cn.push({
9912                     tag :'span',
9913                     cls : 'input-group-addon',
9914                     html : this.before
9915                 });
9916             }
9917             
9918             inputblock.cn.push(input);
9919             
9920             if(this.hasFeedback && !this.allowBlank){
9921                 inputblock.cls += ' has-feedback';
9922                 inputblock.cn.push(feedback);
9923             }
9924             
9925             if (this.after) {
9926                 inputblock.cn.push({
9927                     tag :'span',
9928                     cls : 'input-group-addon',
9929                     html : this.after
9930                 });
9931             }
9932             
9933         }
9934         
9935         if (align ==='left' && this.fieldLabel.length) {
9936             cfg.cn = [
9937                 {
9938                     tag: 'label',
9939                     'for' :  id,
9940                     cls : 'control-label',
9941                     html : this.fieldLabel
9942                 },
9943                 {
9944                     cls : "",
9945                     cn: [
9946                         inputblock
9947                     ]
9948                 }
9949
9950             ];
9951             
9952             if(this.labelWidth > 12){
9953                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9954             }
9955
9956             if(this.labelWidth < 13 && this.labelmd == 0){
9957                 this.labelmd = this.labelWidth;
9958             }
9959
9960             if(this.labellg > 0){
9961                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9962                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9963             }
9964
9965             if(this.labelmd > 0){
9966                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9967                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9968             }
9969
9970             if(this.labelsm > 0){
9971                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9972                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9973             }
9974
9975             if(this.labelxs > 0){
9976                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9977                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9978             }
9979             
9980         } else if ( this.fieldLabel.length) {
9981             cfg.cn = [
9982
9983                {
9984                    tag: 'label',
9985                    //cls : 'input-group-addon',
9986                    html : this.fieldLabel
9987
9988                },
9989
9990                inputblock
9991
9992            ];
9993
9994         } else {
9995
9996             cfg.cn = [
9997
9998                 inputblock
9999
10000             ];
10001                 
10002         }
10003         
10004         if (this.disabled) {
10005             input.disabled=true;
10006         }
10007         
10008         return cfg;
10009         
10010     },
10011     /**
10012      * return the real textarea element.
10013      */
10014     inputEl: function ()
10015     {
10016         return this.el.select('textarea.form-control',true).first();
10017     },
10018     
10019     /**
10020      * Clear any invalid styles/messages for this field
10021      */
10022     clearInvalid : function()
10023     {
10024         
10025         if(!this.el || this.preventMark){ // not rendered
10026             return;
10027         }
10028         
10029         var label = this.el.select('label', true).first();
10030         var icon = this.el.select('i.fa-star', true).first();
10031         
10032         if(label && icon){
10033             icon.remove();
10034         }
10035         
10036         this.el.removeClass(this.invalidClass);
10037         
10038         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10039             
10040             var feedback = this.el.select('.form-control-feedback', true).first();
10041             
10042             if(feedback){
10043                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10044             }
10045             
10046         }
10047         
10048         this.fireEvent('valid', this);
10049     },
10050     
10051      /**
10052      * Mark this field as valid
10053      */
10054     markValid : function()
10055     {
10056         if(!this.el  || this.preventMark){ // not rendered
10057             return;
10058         }
10059         
10060         this.el.removeClass([this.invalidClass, this.validClass]);
10061         
10062         var feedback = this.el.select('.form-control-feedback', true).first();
10063             
10064         if(feedback){
10065             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10066         }
10067
10068         if(this.disabled || this.allowBlank){
10069             return;
10070         }
10071         
10072         var label = this.el.select('label', true).first();
10073         var icon = this.el.select('i.fa-star', true).first();
10074         
10075         if(label && icon){
10076             icon.remove();
10077         }
10078         
10079         this.el.addClass(this.validClass);
10080         
10081         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10082             
10083             var feedback = this.el.select('.form-control-feedback', true).first();
10084             
10085             if(feedback){
10086                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10087                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10088             }
10089             
10090         }
10091         
10092         this.fireEvent('valid', this);
10093     },
10094     
10095      /**
10096      * Mark this field as invalid
10097      * @param {String} msg The validation message
10098      */
10099     markInvalid : function(msg)
10100     {
10101         if(!this.el  || this.preventMark){ // not rendered
10102             return;
10103         }
10104         
10105         this.el.removeClass([this.invalidClass, this.validClass]);
10106         
10107         var feedback = this.el.select('.form-control-feedback', true).first();
10108             
10109         if(feedback){
10110             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10111         }
10112
10113         if(this.disabled || this.allowBlank){
10114             return;
10115         }
10116         
10117         var label = this.el.select('label', true).first();
10118         var icon = this.el.select('i.fa-star', true).first();
10119         
10120         if(!this.getValue().length && label && !icon){
10121             this.el.createChild({
10122                 tag : 'i',
10123                 cls : 'text-danger fa fa-lg fa-star',
10124                 tooltip : 'This field is required',
10125                 style : 'margin-right:5px;'
10126             }, label, true);
10127         }
10128
10129         this.el.addClass(this.invalidClass);
10130         
10131         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10132             
10133             var feedback = this.el.select('.form-control-feedback', true).first();
10134             
10135             if(feedback){
10136                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10137                 
10138                 if(this.getValue().length || this.forceFeedback){
10139                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10140                 }
10141                 
10142             }
10143             
10144         }
10145         
10146         this.fireEvent('invalid', this, msg);
10147     }
10148 });
10149
10150  
10151 /*
10152  * - LGPL
10153  *
10154  * trigger field - base class for combo..
10155  * 
10156  */
10157  
10158 /**
10159  * @class Roo.bootstrap.TriggerField
10160  * @extends Roo.bootstrap.Input
10161  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10162  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10163  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10164  * for which you can provide a custom implementation.  For example:
10165  * <pre><code>
10166 var trigger = new Roo.bootstrap.TriggerField();
10167 trigger.onTriggerClick = myTriggerFn;
10168 trigger.applyTo('my-field');
10169 </code></pre>
10170  *
10171  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10172  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10173  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10174  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10175  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10176
10177  * @constructor
10178  * Create a new TriggerField.
10179  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10180  * to the base TextField)
10181  */
10182 Roo.bootstrap.TriggerField = function(config){
10183     this.mimicing = false;
10184     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10185 };
10186
10187 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10188     /**
10189      * @cfg {String} triggerClass A CSS class to apply to the trigger
10190      */
10191      /**
10192      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10193      */
10194     hideTrigger:false,
10195
10196     /**
10197      * @cfg {Boolean} removable (true|false) special filter default false
10198      */
10199     removable : false,
10200     
10201     /** @cfg {Boolean} grow @hide */
10202     /** @cfg {Number} growMin @hide */
10203     /** @cfg {Number} growMax @hide */
10204
10205     /**
10206      * @hide 
10207      * @method
10208      */
10209     autoSize: Roo.emptyFn,
10210     // private
10211     monitorTab : true,
10212     // private
10213     deferHeight : true,
10214
10215     
10216     actionMode : 'wrap',
10217     
10218     caret : false,
10219     
10220     
10221     getAutoCreate : function(){
10222        
10223         var align = this.labelAlign || this.parentLabelAlign();
10224         
10225         var id = Roo.id();
10226         
10227         var cfg = {
10228             cls: 'form-group' //input-group
10229         };
10230         
10231         
10232         var input =  {
10233             tag: 'input',
10234             id : id,
10235             type : this.inputType,
10236             cls : 'form-control',
10237             autocomplete: 'new-password',
10238             placeholder : this.placeholder || '' 
10239             
10240         };
10241         if (this.name) {
10242             input.name = this.name;
10243         }
10244         if (this.size) {
10245             input.cls += ' input-' + this.size;
10246         }
10247         
10248         if (this.disabled) {
10249             input.disabled=true;
10250         }
10251         
10252         var inputblock = input;
10253         
10254         if(this.hasFeedback && !this.allowBlank){
10255             
10256             var feedback = {
10257                 tag: 'span',
10258                 cls: 'glyphicon form-control-feedback'
10259             };
10260             
10261             if(this.removable && !this.editable && !this.tickable){
10262                 inputblock = {
10263                     cls : 'has-feedback',
10264                     cn :  [
10265                         inputblock,
10266                         {
10267                             tag: 'button',
10268                             html : 'x',
10269                             cls : 'roo-combo-removable-btn close'
10270                         },
10271                         feedback
10272                     ] 
10273                 };
10274             } else {
10275                 inputblock = {
10276                     cls : 'has-feedback',
10277                     cn :  [
10278                         inputblock,
10279                         feedback
10280                     ] 
10281                 };
10282             }
10283
10284         } else {
10285             if(this.removable && !this.editable && !this.tickable){
10286                 inputblock = {
10287                     cls : 'roo-removable',
10288                     cn :  [
10289                         inputblock,
10290                         {
10291                             tag: 'button',
10292                             html : 'x',
10293                             cls : 'roo-combo-removable-btn close'
10294                         }
10295                     ] 
10296                 };
10297             }
10298         }
10299         
10300         if (this.before || this.after) {
10301             
10302             inputblock = {
10303                 cls : 'input-group',
10304                 cn :  [] 
10305             };
10306             if (this.before) {
10307                 inputblock.cn.push({
10308                     tag :'span',
10309                     cls : 'input-group-addon',
10310                     html : this.before
10311                 });
10312             }
10313             
10314             inputblock.cn.push(input);
10315             
10316             if(this.hasFeedback && !this.allowBlank){
10317                 inputblock.cls += ' has-feedback';
10318                 inputblock.cn.push(feedback);
10319             }
10320             
10321             if (this.after) {
10322                 inputblock.cn.push({
10323                     tag :'span',
10324                     cls : 'input-group-addon',
10325                     html : this.after
10326                 });
10327             }
10328             
10329         };
10330         
10331         var box = {
10332             tag: 'div',
10333             cn: [
10334                 {
10335                     tag: 'input',
10336                     type : 'hidden',
10337                     cls: 'form-hidden-field'
10338                 },
10339                 inputblock
10340             ]
10341             
10342         };
10343         
10344         if(this.multiple){
10345             box = {
10346                 tag: 'div',
10347                 cn: [
10348                     {
10349                         tag: 'input',
10350                         type : 'hidden',
10351                         cls: 'form-hidden-field'
10352                     },
10353                     {
10354                         tag: 'ul',
10355                         cls: 'roo-select2-choices',
10356                         cn:[
10357                             {
10358                                 tag: 'li',
10359                                 cls: 'roo-select2-search-field',
10360                                 cn: [
10361
10362                                     inputblock
10363                                 ]
10364                             }
10365                         ]
10366                     }
10367                 ]
10368             }
10369         };
10370         
10371         var combobox = {
10372             cls: 'roo-select2-container input-group',
10373             cn: [
10374                 box
10375 //                {
10376 //                    tag: 'ul',
10377 //                    cls: 'typeahead typeahead-long dropdown-menu',
10378 //                    style: 'display:none'
10379 //                }
10380             ]
10381         };
10382         
10383         if(!this.multiple && this.showToggleBtn){
10384             
10385             var caret = {
10386                         tag: 'span',
10387                         cls: 'caret'
10388              };
10389             if (this.caret != false) {
10390                 caret = {
10391                      tag: 'i',
10392                      cls: 'fa fa-' + this.caret
10393                 };
10394                 
10395             }
10396             
10397             combobox.cn.push({
10398                 tag :'span',
10399                 cls : 'input-group-addon btn dropdown-toggle',
10400                 cn : [
10401                     caret,
10402                     {
10403                         tag: 'span',
10404                         cls: 'combobox-clear',
10405                         cn  : [
10406                             {
10407                                 tag : 'i',
10408                                 cls: 'icon-remove'
10409                             }
10410                         ]
10411                     }
10412                 ]
10413
10414             })
10415         }
10416         
10417         if(this.multiple){
10418             combobox.cls += ' roo-select2-container-multi';
10419         }
10420         
10421         if (align ==='left' && this.fieldLabel.length) {
10422             
10423             cfg.cls += ' roo-form-group-label-left';
10424
10425             cfg.cn = [
10426                 {
10427                     tag : 'i',
10428                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10429                     tooltip : 'This field is required'
10430                 },
10431                 {
10432                     tag: 'label',
10433                     'for' :  id,
10434                     cls : 'control-label',
10435                     html : this.fieldLabel
10436
10437                 },
10438                 {
10439                     cls : "", 
10440                     cn: [
10441                         combobox
10442                     ]
10443                 }
10444
10445             ];
10446             
10447             var labelCfg = cfg.cn[1];
10448             var contentCfg = cfg.cn[2];
10449             
10450             if(this.indicatorpos == 'right'){
10451                 cfg.cn = [
10452                     {
10453                         tag: 'label',
10454                         'for' :  id,
10455                         cls : 'control-label',
10456                         cn : [
10457                             {
10458                                 tag : 'span',
10459                                 html : this.fieldLabel
10460                             },
10461                             {
10462                                 tag : 'i',
10463                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10464                                 tooltip : 'This field is required'
10465                             }
10466                         ]
10467                     },
10468                     {
10469                         cls : "", 
10470                         cn: [
10471                             combobox
10472                         ]
10473                     }
10474
10475                 ];
10476                 
10477                 labelCfg = cfg.cn[0];
10478                 contentCfg = cfg.cn[1];
10479             }
10480             
10481             if(this.labelWidth > 12){
10482                 labelCfg.style = "width: " + this.labelWidth + 'px';
10483             }
10484             
10485             if(this.labelWidth < 13 && this.labelmd == 0){
10486                 this.labelmd = this.labelWidth;
10487             }
10488             
10489             if(this.labellg > 0){
10490                 labelCfg.cls += ' col-lg-' + this.labellg;
10491                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10492             }
10493             
10494             if(this.labelmd > 0){
10495                 labelCfg.cls += ' col-md-' + this.labelmd;
10496                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10497             }
10498             
10499             if(this.labelsm > 0){
10500                 labelCfg.cls += ' col-sm-' + this.labelsm;
10501                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10502             }
10503             
10504             if(this.labelxs > 0){
10505                 labelCfg.cls += ' col-xs-' + this.labelxs;
10506                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10507             }
10508             
10509         } else if ( this.fieldLabel.length) {
10510 //                Roo.log(" label");
10511             cfg.cn = [
10512                 {
10513                    tag : 'i',
10514                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10515                    tooltip : 'This field is required'
10516                },
10517                {
10518                    tag: 'label',
10519                    //cls : 'input-group-addon',
10520                    html : this.fieldLabel
10521
10522                },
10523
10524                combobox
10525
10526             ];
10527             
10528             if(this.indicatorpos == 'right'){
10529                 
10530                 cfg.cn = [
10531                     {
10532                        tag: 'label',
10533                        cn : [
10534                            {
10535                                tag : 'span',
10536                                html : this.fieldLabel
10537                            },
10538                            {
10539                               tag : 'i',
10540                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10541                               tooltip : 'This field is required'
10542                            }
10543                        ]
10544
10545                     },
10546                     combobox
10547
10548                 ];
10549
10550             }
10551
10552         } else {
10553             
10554 //                Roo.log(" no label && no align");
10555                 cfg = combobox
10556                      
10557                 
10558         }
10559         
10560         var settings=this;
10561         ['xs','sm','md','lg'].map(function(size){
10562             if (settings[size]) {
10563                 cfg.cls += ' col-' + size + '-' + settings[size];
10564             }
10565         });
10566         
10567         return cfg;
10568         
10569     },
10570     
10571     
10572     
10573     // private
10574     onResize : function(w, h){
10575 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10576 //        if(typeof w == 'number'){
10577 //            var x = w - this.trigger.getWidth();
10578 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10579 //            this.trigger.setStyle('left', x+'px');
10580 //        }
10581     },
10582
10583     // private
10584     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10585
10586     // private
10587     getResizeEl : function(){
10588         return this.inputEl();
10589     },
10590
10591     // private
10592     getPositionEl : function(){
10593         return this.inputEl();
10594     },
10595
10596     // private
10597     alignErrorIcon : function(){
10598         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10599     },
10600
10601     // private
10602     initEvents : function(){
10603         
10604         this.createList();
10605         
10606         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10607         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10608         if(!this.multiple && this.showToggleBtn){
10609             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10610             if(this.hideTrigger){
10611                 this.trigger.setDisplayed(false);
10612             }
10613             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10614         }
10615         
10616         if(this.multiple){
10617             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10618         }
10619         
10620         if(this.removable && !this.editable && !this.tickable){
10621             var close = this.closeTriggerEl();
10622             
10623             if(close){
10624                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10625                 close.on('click', this.removeBtnClick, this, close);
10626             }
10627         }
10628         
10629         //this.trigger.addClassOnOver('x-form-trigger-over');
10630         //this.trigger.addClassOnClick('x-form-trigger-click');
10631         
10632         //if(!this.width){
10633         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10634         //}
10635     },
10636     
10637     closeTriggerEl : function()
10638     {
10639         var close = this.el.select('.roo-combo-removable-btn', true).first();
10640         return close ? close : false;
10641     },
10642     
10643     removeBtnClick : function(e, h, el)
10644     {
10645         e.preventDefault();
10646         
10647         if(this.fireEvent("remove", this) !== false){
10648             this.reset();
10649             this.fireEvent("afterremove", this)
10650         }
10651     },
10652     
10653     createList : function()
10654     {
10655         this.list = Roo.get(document.body).createChild({
10656             tag: 'ul',
10657             cls: 'typeahead typeahead-long dropdown-menu',
10658             style: 'display:none'
10659         });
10660         
10661         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10662         
10663     },
10664
10665     // private
10666     initTrigger : function(){
10667        
10668     },
10669
10670     // private
10671     onDestroy : function(){
10672         if(this.trigger){
10673             this.trigger.removeAllListeners();
10674           //  this.trigger.remove();
10675         }
10676         //if(this.wrap){
10677         //    this.wrap.remove();
10678         //}
10679         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10680     },
10681
10682     // private
10683     onFocus : function(){
10684         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10685         /*
10686         if(!this.mimicing){
10687             this.wrap.addClass('x-trigger-wrap-focus');
10688             this.mimicing = true;
10689             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10690             if(this.monitorTab){
10691                 this.el.on("keydown", this.checkTab, this);
10692             }
10693         }
10694         */
10695     },
10696
10697     // private
10698     checkTab : function(e){
10699         if(e.getKey() == e.TAB){
10700             this.triggerBlur();
10701         }
10702     },
10703
10704     // private
10705     onBlur : function(){
10706         // do nothing
10707     },
10708
10709     // private
10710     mimicBlur : function(e, t){
10711         /*
10712         if(!this.wrap.contains(t) && this.validateBlur()){
10713             this.triggerBlur();
10714         }
10715         */
10716     },
10717
10718     // private
10719     triggerBlur : function(){
10720         this.mimicing = false;
10721         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10722         if(this.monitorTab){
10723             this.el.un("keydown", this.checkTab, this);
10724         }
10725         //this.wrap.removeClass('x-trigger-wrap-focus');
10726         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10727     },
10728
10729     // private
10730     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10731     validateBlur : function(e, t){
10732         return true;
10733     },
10734
10735     // private
10736     onDisable : function(){
10737         this.inputEl().dom.disabled = true;
10738         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10739         //if(this.wrap){
10740         //    this.wrap.addClass('x-item-disabled');
10741         //}
10742     },
10743
10744     // private
10745     onEnable : function(){
10746         this.inputEl().dom.disabled = false;
10747         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10748         //if(this.wrap){
10749         //    this.el.removeClass('x-item-disabled');
10750         //}
10751     },
10752
10753     // private
10754     onShow : function(){
10755         var ae = this.getActionEl();
10756         
10757         if(ae){
10758             ae.dom.style.display = '';
10759             ae.dom.style.visibility = 'visible';
10760         }
10761     },
10762
10763     // private
10764     
10765     onHide : function(){
10766         var ae = this.getActionEl();
10767         ae.dom.style.display = 'none';
10768     },
10769
10770     /**
10771      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10772      * by an implementing function.
10773      * @method
10774      * @param {EventObject} e
10775      */
10776     onTriggerClick : Roo.emptyFn
10777 });
10778  /*
10779  * Based on:
10780  * Ext JS Library 1.1.1
10781  * Copyright(c) 2006-2007, Ext JS, LLC.
10782  *
10783  * Originally Released Under LGPL - original licence link has changed is not relivant.
10784  *
10785  * Fork - LGPL
10786  * <script type="text/javascript">
10787  */
10788
10789
10790 /**
10791  * @class Roo.data.SortTypes
10792  * @singleton
10793  * Defines the default sorting (casting?) comparison functions used when sorting data.
10794  */
10795 Roo.data.SortTypes = {
10796     /**
10797      * Default sort that does nothing
10798      * @param {Mixed} s The value being converted
10799      * @return {Mixed} The comparison value
10800      */
10801     none : function(s){
10802         return s;
10803     },
10804     
10805     /**
10806      * The regular expression used to strip tags
10807      * @type {RegExp}
10808      * @property
10809      */
10810     stripTagsRE : /<\/?[^>]+>/gi,
10811     
10812     /**
10813      * Strips all HTML tags to sort on text only
10814      * @param {Mixed} s The value being converted
10815      * @return {String} The comparison value
10816      */
10817     asText : function(s){
10818         return String(s).replace(this.stripTagsRE, "");
10819     },
10820     
10821     /**
10822      * Strips all HTML tags to sort on text only - Case insensitive
10823      * @param {Mixed} s The value being converted
10824      * @return {String} The comparison value
10825      */
10826     asUCText : function(s){
10827         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10828     },
10829     
10830     /**
10831      * Case insensitive string
10832      * @param {Mixed} s The value being converted
10833      * @return {String} The comparison value
10834      */
10835     asUCString : function(s) {
10836         return String(s).toUpperCase();
10837     },
10838     
10839     /**
10840      * Date sorting
10841      * @param {Mixed} s The value being converted
10842      * @return {Number} The comparison value
10843      */
10844     asDate : function(s) {
10845         if(!s){
10846             return 0;
10847         }
10848         if(s instanceof Date){
10849             return s.getTime();
10850         }
10851         return Date.parse(String(s));
10852     },
10853     
10854     /**
10855      * Float sorting
10856      * @param {Mixed} s The value being converted
10857      * @return {Float} The comparison value
10858      */
10859     asFloat : function(s) {
10860         var val = parseFloat(String(s).replace(/,/g, ""));
10861         if(isNaN(val)) {
10862             val = 0;
10863         }
10864         return val;
10865     },
10866     
10867     /**
10868      * Integer sorting
10869      * @param {Mixed} s The value being converted
10870      * @return {Number} The comparison value
10871      */
10872     asInt : function(s) {
10873         var val = parseInt(String(s).replace(/,/g, ""));
10874         if(isNaN(val)) {
10875             val = 0;
10876         }
10877         return val;
10878     }
10879 };/*
10880  * Based on:
10881  * Ext JS Library 1.1.1
10882  * Copyright(c) 2006-2007, Ext JS, LLC.
10883  *
10884  * Originally Released Under LGPL - original licence link has changed is not relivant.
10885  *
10886  * Fork - LGPL
10887  * <script type="text/javascript">
10888  */
10889
10890 /**
10891 * @class Roo.data.Record
10892  * Instances of this class encapsulate both record <em>definition</em> information, and record
10893  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10894  * to access Records cached in an {@link Roo.data.Store} object.<br>
10895  * <p>
10896  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10897  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10898  * objects.<br>
10899  * <p>
10900  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10901  * @constructor
10902  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10903  * {@link #create}. The parameters are the same.
10904  * @param {Array} data An associative Array of data values keyed by the field name.
10905  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10906  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10907  * not specified an integer id is generated.
10908  */
10909 Roo.data.Record = function(data, id){
10910     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10911     this.data = data;
10912 };
10913
10914 /**
10915  * Generate a constructor for a specific record layout.
10916  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10917  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10918  * Each field definition object may contain the following properties: <ul>
10919  * <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,
10920  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10921  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10922  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10923  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10924  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10925  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10926  * this may be omitted.</p></li>
10927  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10928  * <ul><li>auto (Default, implies no conversion)</li>
10929  * <li>string</li>
10930  * <li>int</li>
10931  * <li>float</li>
10932  * <li>boolean</li>
10933  * <li>date</li></ul></p></li>
10934  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10935  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10936  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10937  * by the Reader into an object that will be stored in the Record. It is passed the
10938  * following parameters:<ul>
10939  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10940  * </ul></p></li>
10941  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10942  * </ul>
10943  * <br>usage:<br><pre><code>
10944 var TopicRecord = Roo.data.Record.create(
10945     {name: 'title', mapping: 'topic_title'},
10946     {name: 'author', mapping: 'username'},
10947     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10948     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10949     {name: 'lastPoster', mapping: 'user2'},
10950     {name: 'excerpt', mapping: 'post_text'}
10951 );
10952
10953 var myNewRecord = new TopicRecord({
10954     title: 'Do my job please',
10955     author: 'noobie',
10956     totalPosts: 1,
10957     lastPost: new Date(),
10958     lastPoster: 'Animal',
10959     excerpt: 'No way dude!'
10960 });
10961 myStore.add(myNewRecord);
10962 </code></pre>
10963  * @method create
10964  * @static
10965  */
10966 Roo.data.Record.create = function(o){
10967     var f = function(){
10968         f.superclass.constructor.apply(this, arguments);
10969     };
10970     Roo.extend(f, Roo.data.Record);
10971     var p = f.prototype;
10972     p.fields = new Roo.util.MixedCollection(false, function(field){
10973         return field.name;
10974     });
10975     for(var i = 0, len = o.length; i < len; i++){
10976         p.fields.add(new Roo.data.Field(o[i]));
10977     }
10978     f.getField = function(name){
10979         return p.fields.get(name);  
10980     };
10981     return f;
10982 };
10983
10984 Roo.data.Record.AUTO_ID = 1000;
10985 Roo.data.Record.EDIT = 'edit';
10986 Roo.data.Record.REJECT = 'reject';
10987 Roo.data.Record.COMMIT = 'commit';
10988
10989 Roo.data.Record.prototype = {
10990     /**
10991      * Readonly flag - true if this record has been modified.
10992      * @type Boolean
10993      */
10994     dirty : false,
10995     editing : false,
10996     error: null,
10997     modified: null,
10998
10999     // private
11000     join : function(store){
11001         this.store = store;
11002     },
11003
11004     /**
11005      * Set the named field to the specified value.
11006      * @param {String} name The name of the field to set.
11007      * @param {Object} value The value to set the field to.
11008      */
11009     set : function(name, value){
11010         if(this.data[name] == value){
11011             return;
11012         }
11013         this.dirty = true;
11014         if(!this.modified){
11015             this.modified = {};
11016         }
11017         if(typeof this.modified[name] == 'undefined'){
11018             this.modified[name] = this.data[name];
11019         }
11020         this.data[name] = value;
11021         if(!this.editing && this.store){
11022             this.store.afterEdit(this);
11023         }       
11024     },
11025
11026     /**
11027      * Get the value of the named field.
11028      * @param {String} name The name of the field to get the value of.
11029      * @return {Object} The value of the field.
11030      */
11031     get : function(name){
11032         return this.data[name]; 
11033     },
11034
11035     // private
11036     beginEdit : function(){
11037         this.editing = true;
11038         this.modified = {}; 
11039     },
11040
11041     // private
11042     cancelEdit : function(){
11043         this.editing = false;
11044         delete this.modified;
11045     },
11046
11047     // private
11048     endEdit : function(){
11049         this.editing = false;
11050         if(this.dirty && this.store){
11051             this.store.afterEdit(this);
11052         }
11053     },
11054
11055     /**
11056      * Usually called by the {@link Roo.data.Store} which owns the Record.
11057      * Rejects all changes made to the Record since either creation, or the last commit operation.
11058      * Modified fields are reverted to their original values.
11059      * <p>
11060      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11061      * of reject operations.
11062      */
11063     reject : function(){
11064         var m = this.modified;
11065         for(var n in m){
11066             if(typeof m[n] != "function"){
11067                 this.data[n] = m[n];
11068             }
11069         }
11070         this.dirty = false;
11071         delete this.modified;
11072         this.editing = false;
11073         if(this.store){
11074             this.store.afterReject(this);
11075         }
11076     },
11077
11078     /**
11079      * Usually called by the {@link Roo.data.Store} which owns the Record.
11080      * Commits all changes made to the Record since either creation, or the last commit operation.
11081      * <p>
11082      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11083      * of commit operations.
11084      */
11085     commit : function(){
11086         this.dirty = false;
11087         delete this.modified;
11088         this.editing = false;
11089         if(this.store){
11090             this.store.afterCommit(this);
11091         }
11092     },
11093
11094     // private
11095     hasError : function(){
11096         return this.error != null;
11097     },
11098
11099     // private
11100     clearError : function(){
11101         this.error = null;
11102     },
11103
11104     /**
11105      * Creates a copy of this record.
11106      * @param {String} id (optional) A new record id if you don't want to use this record's id
11107      * @return {Record}
11108      */
11109     copy : function(newId) {
11110         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11111     }
11112 };/*
11113  * Based on:
11114  * Ext JS Library 1.1.1
11115  * Copyright(c) 2006-2007, Ext JS, LLC.
11116  *
11117  * Originally Released Under LGPL - original licence link has changed is not relivant.
11118  *
11119  * Fork - LGPL
11120  * <script type="text/javascript">
11121  */
11122
11123
11124
11125 /**
11126  * @class Roo.data.Store
11127  * @extends Roo.util.Observable
11128  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11129  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11130  * <p>
11131  * 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
11132  * has no knowledge of the format of the data returned by the Proxy.<br>
11133  * <p>
11134  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11135  * instances from the data object. These records are cached and made available through accessor functions.
11136  * @constructor
11137  * Creates a new Store.
11138  * @param {Object} config A config object containing the objects needed for the Store to access data,
11139  * and read the data into Records.
11140  */
11141 Roo.data.Store = function(config){
11142     this.data = new Roo.util.MixedCollection(false);
11143     this.data.getKey = function(o){
11144         return o.id;
11145     };
11146     this.baseParams = {};
11147     // private
11148     this.paramNames = {
11149         "start" : "start",
11150         "limit" : "limit",
11151         "sort" : "sort",
11152         "dir" : "dir",
11153         "multisort" : "_multisort"
11154     };
11155
11156     if(config && config.data){
11157         this.inlineData = config.data;
11158         delete config.data;
11159     }
11160
11161     Roo.apply(this, config);
11162     
11163     if(this.reader){ // reader passed
11164         this.reader = Roo.factory(this.reader, Roo.data);
11165         this.reader.xmodule = this.xmodule || false;
11166         if(!this.recordType){
11167             this.recordType = this.reader.recordType;
11168         }
11169         if(this.reader.onMetaChange){
11170             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11171         }
11172     }
11173
11174     if(this.recordType){
11175         this.fields = this.recordType.prototype.fields;
11176     }
11177     this.modified = [];
11178
11179     this.addEvents({
11180         /**
11181          * @event datachanged
11182          * Fires when the data cache has changed, and a widget which is using this Store
11183          * as a Record cache should refresh its view.
11184          * @param {Store} this
11185          */
11186         datachanged : true,
11187         /**
11188          * @event metachange
11189          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11190          * @param {Store} this
11191          * @param {Object} meta The JSON metadata
11192          */
11193         metachange : true,
11194         /**
11195          * @event add
11196          * Fires when Records have been added to the Store
11197          * @param {Store} this
11198          * @param {Roo.data.Record[]} records The array of Records added
11199          * @param {Number} index The index at which the record(s) were added
11200          */
11201         add : true,
11202         /**
11203          * @event remove
11204          * Fires when a Record has been removed from the Store
11205          * @param {Store} this
11206          * @param {Roo.data.Record} record The Record that was removed
11207          * @param {Number} index The index at which the record was removed
11208          */
11209         remove : true,
11210         /**
11211          * @event update
11212          * Fires when a Record has been updated
11213          * @param {Store} this
11214          * @param {Roo.data.Record} record The Record that was updated
11215          * @param {String} operation The update operation being performed.  Value may be one of:
11216          * <pre><code>
11217  Roo.data.Record.EDIT
11218  Roo.data.Record.REJECT
11219  Roo.data.Record.COMMIT
11220          * </code></pre>
11221          */
11222         update : true,
11223         /**
11224          * @event clear
11225          * Fires when the data cache has been cleared.
11226          * @param {Store} this
11227          */
11228         clear : true,
11229         /**
11230          * @event beforeload
11231          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11232          * the load action will be canceled.
11233          * @param {Store} this
11234          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11235          */
11236         beforeload : true,
11237         /**
11238          * @event beforeloadadd
11239          * Fires after a new set of Records has been loaded.
11240          * @param {Store} this
11241          * @param {Roo.data.Record[]} records The Records that were loaded
11242          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11243          */
11244         beforeloadadd : true,
11245         /**
11246          * @event load
11247          * Fires after a new set of Records has been loaded, before they are added to the store.
11248          * @param {Store} this
11249          * @param {Roo.data.Record[]} records The Records that were loaded
11250          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11251          * @params {Object} return from reader
11252          */
11253         load : true,
11254         /**
11255          * @event loadexception
11256          * Fires if an exception occurs in the Proxy during loading.
11257          * Called with the signature of the Proxy's "loadexception" event.
11258          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11259          * 
11260          * @param {Proxy} 
11261          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11262          * @param {Object} load options 
11263          * @param {Object} jsonData from your request (normally this contains the Exception)
11264          */
11265         loadexception : true
11266     });
11267     
11268     if(this.proxy){
11269         this.proxy = Roo.factory(this.proxy, Roo.data);
11270         this.proxy.xmodule = this.xmodule || false;
11271         this.relayEvents(this.proxy,  ["loadexception"]);
11272     }
11273     this.sortToggle = {};
11274     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11275
11276     Roo.data.Store.superclass.constructor.call(this);
11277
11278     if(this.inlineData){
11279         this.loadData(this.inlineData);
11280         delete this.inlineData;
11281     }
11282 };
11283
11284 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11285      /**
11286     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11287     * without a remote query - used by combo/forms at present.
11288     */
11289     
11290     /**
11291     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11292     */
11293     /**
11294     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11295     */
11296     /**
11297     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11298     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11299     */
11300     /**
11301     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11302     * on any HTTP request
11303     */
11304     /**
11305     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11306     */
11307     /**
11308     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11309     */
11310     multiSort: false,
11311     /**
11312     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11313     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11314     */
11315     remoteSort : false,
11316
11317     /**
11318     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11319      * loaded or when a record is removed. (defaults to false).
11320     */
11321     pruneModifiedRecords : false,
11322
11323     // private
11324     lastOptions : null,
11325
11326     /**
11327      * Add Records to the Store and fires the add event.
11328      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11329      */
11330     add : function(records){
11331         records = [].concat(records);
11332         for(var i = 0, len = records.length; i < len; i++){
11333             records[i].join(this);
11334         }
11335         var index = this.data.length;
11336         this.data.addAll(records);
11337         this.fireEvent("add", this, records, index);
11338     },
11339
11340     /**
11341      * Remove a Record from the Store and fires the remove event.
11342      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11343      */
11344     remove : function(record){
11345         var index = this.data.indexOf(record);
11346         this.data.removeAt(index);
11347  
11348         if(this.pruneModifiedRecords){
11349             this.modified.remove(record);
11350         }
11351         this.fireEvent("remove", this, record, index);
11352     },
11353
11354     /**
11355      * Remove all Records from the Store and fires the clear event.
11356      */
11357     removeAll : function(){
11358         this.data.clear();
11359         if(this.pruneModifiedRecords){
11360             this.modified = [];
11361         }
11362         this.fireEvent("clear", this);
11363     },
11364
11365     /**
11366      * Inserts Records to the Store at the given index and fires the add event.
11367      * @param {Number} index The start index at which to insert the passed Records.
11368      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11369      */
11370     insert : function(index, records){
11371         records = [].concat(records);
11372         for(var i = 0, len = records.length; i < len; i++){
11373             this.data.insert(index, records[i]);
11374             records[i].join(this);
11375         }
11376         this.fireEvent("add", this, records, index);
11377     },
11378
11379     /**
11380      * Get the index within the cache of the passed Record.
11381      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11382      * @return {Number} The index of the passed Record. Returns -1 if not found.
11383      */
11384     indexOf : function(record){
11385         return this.data.indexOf(record);
11386     },
11387
11388     /**
11389      * Get the index within the cache of the Record with the passed id.
11390      * @param {String} id The id of the Record to find.
11391      * @return {Number} The index of the Record. Returns -1 if not found.
11392      */
11393     indexOfId : function(id){
11394         return this.data.indexOfKey(id);
11395     },
11396
11397     /**
11398      * Get the Record with the specified id.
11399      * @param {String} id The id of the Record to find.
11400      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11401      */
11402     getById : function(id){
11403         return this.data.key(id);
11404     },
11405
11406     /**
11407      * Get the Record at the specified index.
11408      * @param {Number} index The index of the Record to find.
11409      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11410      */
11411     getAt : function(index){
11412         return this.data.itemAt(index);
11413     },
11414
11415     /**
11416      * Returns a range of Records between specified indices.
11417      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11418      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11419      * @return {Roo.data.Record[]} An array of Records
11420      */
11421     getRange : function(start, end){
11422         return this.data.getRange(start, end);
11423     },
11424
11425     // private
11426     storeOptions : function(o){
11427         o = Roo.apply({}, o);
11428         delete o.callback;
11429         delete o.scope;
11430         this.lastOptions = o;
11431     },
11432
11433     /**
11434      * Loads the Record cache from the configured Proxy using the configured Reader.
11435      * <p>
11436      * If using remote paging, then the first load call must specify the <em>start</em>
11437      * and <em>limit</em> properties in the options.params property to establish the initial
11438      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11439      * <p>
11440      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11441      * and this call will return before the new data has been loaded. Perform any post-processing
11442      * in a callback function, or in a "load" event handler.</strong>
11443      * <p>
11444      * @param {Object} options An object containing properties which control loading options:<ul>
11445      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11446      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11447      * passed the following arguments:<ul>
11448      * <li>r : Roo.data.Record[]</li>
11449      * <li>options: Options object from the load call</li>
11450      * <li>success: Boolean success indicator</li></ul></li>
11451      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11452      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11453      * </ul>
11454      */
11455     load : function(options){
11456         options = options || {};
11457         if(this.fireEvent("beforeload", this, options) !== false){
11458             this.storeOptions(options);
11459             var p = Roo.apply(options.params || {}, this.baseParams);
11460             // if meta was not loaded from remote source.. try requesting it.
11461             if (!this.reader.metaFromRemote) {
11462                 p._requestMeta = 1;
11463             }
11464             if(this.sortInfo && this.remoteSort){
11465                 var pn = this.paramNames;
11466                 p[pn["sort"]] = this.sortInfo.field;
11467                 p[pn["dir"]] = this.sortInfo.direction;
11468             }
11469             if (this.multiSort) {
11470                 var pn = this.paramNames;
11471                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11472             }
11473             
11474             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11475         }
11476     },
11477
11478     /**
11479      * Reloads the Record cache from the configured Proxy using the configured Reader and
11480      * the options from the last load operation performed.
11481      * @param {Object} options (optional) An object containing properties which may override the options
11482      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11483      * the most recently used options are reused).
11484      */
11485     reload : function(options){
11486         this.load(Roo.applyIf(options||{}, this.lastOptions));
11487     },
11488
11489     // private
11490     // Called as a callback by the Reader during a load operation.
11491     loadRecords : function(o, options, success){
11492         if(!o || success === false){
11493             if(success !== false){
11494                 this.fireEvent("load", this, [], options, o);
11495             }
11496             if(options.callback){
11497                 options.callback.call(options.scope || this, [], options, false);
11498             }
11499             return;
11500         }
11501         // if data returned failure - throw an exception.
11502         if (o.success === false) {
11503             // show a message if no listener is registered.
11504             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11505                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11506             }
11507             // loadmask wil be hooked into this..
11508             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11509             return;
11510         }
11511         var r = o.records, t = o.totalRecords || r.length;
11512         
11513         this.fireEvent("beforeloadadd", this, r, options, o);
11514         
11515         if(!options || options.add !== true){
11516             if(this.pruneModifiedRecords){
11517                 this.modified = [];
11518             }
11519             for(var i = 0, len = r.length; i < len; i++){
11520                 r[i].join(this);
11521             }
11522             if(this.snapshot){
11523                 this.data = this.snapshot;
11524                 delete this.snapshot;
11525             }
11526             this.data.clear();
11527             this.data.addAll(r);
11528             this.totalLength = t;
11529             this.applySort();
11530             this.fireEvent("datachanged", this);
11531         }else{
11532             this.totalLength = Math.max(t, this.data.length+r.length);
11533             this.add(r);
11534         }
11535         
11536         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11537                 
11538             var e = new Roo.data.Record({});
11539
11540             e.set(this.parent.displayField, this.parent.emptyTitle);
11541             e.set(this.parent.valueField, '');
11542
11543             this.insert(0, e);
11544         }
11545             
11546         this.fireEvent("load", this, r, options, o);
11547         if(options.callback){
11548             options.callback.call(options.scope || this, r, options, true);
11549         }
11550     },
11551
11552
11553     /**
11554      * Loads data from a passed data block. A Reader which understands the format of the data
11555      * must have been configured in the constructor.
11556      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11557      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11558      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11559      */
11560     loadData : function(o, append){
11561         var r = this.reader.readRecords(o);
11562         this.loadRecords(r, {add: append}, true);
11563     },
11564
11565     /**
11566      * Gets the number of cached records.
11567      * <p>
11568      * <em>If using paging, this may not be the total size of the dataset. If the data object
11569      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11570      * the data set size</em>
11571      */
11572     getCount : function(){
11573         return this.data.length || 0;
11574     },
11575
11576     /**
11577      * Gets the total number of records in the dataset as returned by the server.
11578      * <p>
11579      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11580      * the dataset size</em>
11581      */
11582     getTotalCount : function(){
11583         return this.totalLength || 0;
11584     },
11585
11586     /**
11587      * Returns the sort state of the Store as an object with two properties:
11588      * <pre><code>
11589  field {String} The name of the field by which the Records are sorted
11590  direction {String} The sort order, "ASC" or "DESC"
11591      * </code></pre>
11592      */
11593     getSortState : function(){
11594         return this.sortInfo;
11595     },
11596
11597     // private
11598     applySort : function(){
11599         if(this.sortInfo && !this.remoteSort){
11600             var s = this.sortInfo, f = s.field;
11601             var st = this.fields.get(f).sortType;
11602             var fn = function(r1, r2){
11603                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11604                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11605             };
11606             this.data.sort(s.direction, fn);
11607             if(this.snapshot && this.snapshot != this.data){
11608                 this.snapshot.sort(s.direction, fn);
11609             }
11610         }
11611     },
11612
11613     /**
11614      * Sets the default sort column and order to be used by the next load operation.
11615      * @param {String} fieldName The name of the field to sort by.
11616      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11617      */
11618     setDefaultSort : function(field, dir){
11619         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11620     },
11621
11622     /**
11623      * Sort the Records.
11624      * If remote sorting is used, the sort is performed on the server, and the cache is
11625      * reloaded. If local sorting is used, the cache is sorted internally.
11626      * @param {String} fieldName The name of the field to sort by.
11627      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11628      */
11629     sort : function(fieldName, dir){
11630         var f = this.fields.get(fieldName);
11631         if(!dir){
11632             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11633             
11634             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11635                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11636             }else{
11637                 dir = f.sortDir;
11638             }
11639         }
11640         this.sortToggle[f.name] = dir;
11641         this.sortInfo = {field: f.name, direction: dir};
11642         if(!this.remoteSort){
11643             this.applySort();
11644             this.fireEvent("datachanged", this);
11645         }else{
11646             this.load(this.lastOptions);
11647         }
11648     },
11649
11650     /**
11651      * Calls the specified function for each of the Records in the cache.
11652      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11653      * Returning <em>false</em> aborts and exits the iteration.
11654      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11655      */
11656     each : function(fn, scope){
11657         this.data.each(fn, scope);
11658     },
11659
11660     /**
11661      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11662      * (e.g., during paging).
11663      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11664      */
11665     getModifiedRecords : function(){
11666         return this.modified;
11667     },
11668
11669     // private
11670     createFilterFn : function(property, value, anyMatch){
11671         if(!value.exec){ // not a regex
11672             value = String(value);
11673             if(value.length == 0){
11674                 return false;
11675             }
11676             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11677         }
11678         return function(r){
11679             return value.test(r.data[property]);
11680         };
11681     },
11682
11683     /**
11684      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11685      * @param {String} property A field on your records
11686      * @param {Number} start The record index to start at (defaults to 0)
11687      * @param {Number} end The last record index to include (defaults to length - 1)
11688      * @return {Number} The sum
11689      */
11690     sum : function(property, start, end){
11691         var rs = this.data.items, v = 0;
11692         start = start || 0;
11693         end = (end || end === 0) ? end : rs.length-1;
11694
11695         for(var i = start; i <= end; i++){
11696             v += (rs[i].data[property] || 0);
11697         }
11698         return v;
11699     },
11700
11701     /**
11702      * Filter the records by a specified property.
11703      * @param {String} field A field on your records
11704      * @param {String/RegExp} value Either a string that the field
11705      * should start with or a RegExp to test against the field
11706      * @param {Boolean} anyMatch True to match any part not just the beginning
11707      */
11708     filter : function(property, value, anyMatch){
11709         var fn = this.createFilterFn(property, value, anyMatch);
11710         return fn ? this.filterBy(fn) : this.clearFilter();
11711     },
11712
11713     /**
11714      * Filter by a function. The specified function will be called with each
11715      * record in this data source. If the function returns true the record is included,
11716      * otherwise it is filtered.
11717      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11718      * @param {Object} scope (optional) The scope of the function (defaults to this)
11719      */
11720     filterBy : function(fn, scope){
11721         this.snapshot = this.snapshot || this.data;
11722         this.data = this.queryBy(fn, scope||this);
11723         this.fireEvent("datachanged", this);
11724     },
11725
11726     /**
11727      * Query the records by a specified property.
11728      * @param {String} field A field on your records
11729      * @param {String/RegExp} value Either a string that the field
11730      * should start with or a RegExp to test against the field
11731      * @param {Boolean} anyMatch True to match any part not just the beginning
11732      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11733      */
11734     query : function(property, value, anyMatch){
11735         var fn = this.createFilterFn(property, value, anyMatch);
11736         return fn ? this.queryBy(fn) : this.data.clone();
11737     },
11738
11739     /**
11740      * Query by a function. The specified function will be called with each
11741      * record in this data source. If the function returns true the record is included
11742      * in the results.
11743      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11744      * @param {Object} scope (optional) The scope of the function (defaults to this)
11745       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11746      **/
11747     queryBy : function(fn, scope){
11748         var data = this.snapshot || this.data;
11749         return data.filterBy(fn, scope||this);
11750     },
11751
11752     /**
11753      * Collects unique values for a particular dataIndex from this store.
11754      * @param {String} dataIndex The property to collect
11755      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11756      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11757      * @return {Array} An array of the unique values
11758      **/
11759     collect : function(dataIndex, allowNull, bypassFilter){
11760         var d = (bypassFilter === true && this.snapshot) ?
11761                 this.snapshot.items : this.data.items;
11762         var v, sv, r = [], l = {};
11763         for(var i = 0, len = d.length; i < len; i++){
11764             v = d[i].data[dataIndex];
11765             sv = String(v);
11766             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11767                 l[sv] = true;
11768                 r[r.length] = v;
11769             }
11770         }
11771         return r;
11772     },
11773
11774     /**
11775      * Revert to a view of the Record cache with no filtering applied.
11776      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11777      */
11778     clearFilter : function(suppressEvent){
11779         if(this.snapshot && this.snapshot != this.data){
11780             this.data = this.snapshot;
11781             delete this.snapshot;
11782             if(suppressEvent !== true){
11783                 this.fireEvent("datachanged", this);
11784             }
11785         }
11786     },
11787
11788     // private
11789     afterEdit : function(record){
11790         if(this.modified.indexOf(record) == -1){
11791             this.modified.push(record);
11792         }
11793         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11794     },
11795     
11796     // private
11797     afterReject : function(record){
11798         this.modified.remove(record);
11799         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11800     },
11801
11802     // private
11803     afterCommit : function(record){
11804         this.modified.remove(record);
11805         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11806     },
11807
11808     /**
11809      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11810      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11811      */
11812     commitChanges : function(){
11813         var m = this.modified.slice(0);
11814         this.modified = [];
11815         for(var i = 0, len = m.length; i < len; i++){
11816             m[i].commit();
11817         }
11818     },
11819
11820     /**
11821      * Cancel outstanding changes on all changed records.
11822      */
11823     rejectChanges : function(){
11824         var m = this.modified.slice(0);
11825         this.modified = [];
11826         for(var i = 0, len = m.length; i < len; i++){
11827             m[i].reject();
11828         }
11829     },
11830
11831     onMetaChange : function(meta, rtype, o){
11832         this.recordType = rtype;
11833         this.fields = rtype.prototype.fields;
11834         delete this.snapshot;
11835         this.sortInfo = meta.sortInfo || this.sortInfo;
11836         this.modified = [];
11837         this.fireEvent('metachange', this, this.reader.meta);
11838     },
11839     
11840     moveIndex : function(data, type)
11841     {
11842         var index = this.indexOf(data);
11843         
11844         var newIndex = index + type;
11845         
11846         this.remove(data);
11847         
11848         this.insert(newIndex, data);
11849         
11850     }
11851 });/*
11852  * Based on:
11853  * Ext JS Library 1.1.1
11854  * Copyright(c) 2006-2007, Ext JS, LLC.
11855  *
11856  * Originally Released Under LGPL - original licence link has changed is not relivant.
11857  *
11858  * Fork - LGPL
11859  * <script type="text/javascript">
11860  */
11861
11862 /**
11863  * @class Roo.data.SimpleStore
11864  * @extends Roo.data.Store
11865  * Small helper class to make creating Stores from Array data easier.
11866  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11867  * @cfg {Array} fields An array of field definition objects, or field name strings.
11868  * @cfg {Array} data The multi-dimensional array of data
11869  * @constructor
11870  * @param {Object} config
11871  */
11872 Roo.data.SimpleStore = function(config){
11873     Roo.data.SimpleStore.superclass.constructor.call(this, {
11874         isLocal : true,
11875         reader: new Roo.data.ArrayReader({
11876                 id: config.id
11877             },
11878             Roo.data.Record.create(config.fields)
11879         ),
11880         proxy : new Roo.data.MemoryProxy(config.data)
11881     });
11882     this.load();
11883 };
11884 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11885  * Based on:
11886  * Ext JS Library 1.1.1
11887  * Copyright(c) 2006-2007, Ext JS, LLC.
11888  *
11889  * Originally Released Under LGPL - original licence link has changed is not relivant.
11890  *
11891  * Fork - LGPL
11892  * <script type="text/javascript">
11893  */
11894
11895 /**
11896 /**
11897  * @extends Roo.data.Store
11898  * @class Roo.data.JsonStore
11899  * Small helper class to make creating Stores for JSON data easier. <br/>
11900 <pre><code>
11901 var store = new Roo.data.JsonStore({
11902     url: 'get-images.php',
11903     root: 'images',
11904     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11905 });
11906 </code></pre>
11907  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11908  * JsonReader and HttpProxy (unless inline data is provided).</b>
11909  * @cfg {Array} fields An array of field definition objects, or field name strings.
11910  * @constructor
11911  * @param {Object} config
11912  */
11913 Roo.data.JsonStore = function(c){
11914     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11915         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11916         reader: new Roo.data.JsonReader(c, c.fields)
11917     }));
11918 };
11919 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11920  * Based on:
11921  * Ext JS Library 1.1.1
11922  * Copyright(c) 2006-2007, Ext JS, LLC.
11923  *
11924  * Originally Released Under LGPL - original licence link has changed is not relivant.
11925  *
11926  * Fork - LGPL
11927  * <script type="text/javascript">
11928  */
11929
11930  
11931 Roo.data.Field = function(config){
11932     if(typeof config == "string"){
11933         config = {name: config};
11934     }
11935     Roo.apply(this, config);
11936     
11937     if(!this.type){
11938         this.type = "auto";
11939     }
11940     
11941     var st = Roo.data.SortTypes;
11942     // named sortTypes are supported, here we look them up
11943     if(typeof this.sortType == "string"){
11944         this.sortType = st[this.sortType];
11945     }
11946     
11947     // set default sortType for strings and dates
11948     if(!this.sortType){
11949         switch(this.type){
11950             case "string":
11951                 this.sortType = st.asUCString;
11952                 break;
11953             case "date":
11954                 this.sortType = st.asDate;
11955                 break;
11956             default:
11957                 this.sortType = st.none;
11958         }
11959     }
11960
11961     // define once
11962     var stripRe = /[\$,%]/g;
11963
11964     // prebuilt conversion function for this field, instead of
11965     // switching every time we're reading a value
11966     if(!this.convert){
11967         var cv, dateFormat = this.dateFormat;
11968         switch(this.type){
11969             case "":
11970             case "auto":
11971             case undefined:
11972                 cv = function(v){ return v; };
11973                 break;
11974             case "string":
11975                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11976                 break;
11977             case "int":
11978                 cv = function(v){
11979                     return v !== undefined && v !== null && v !== '' ?
11980                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11981                     };
11982                 break;
11983             case "float":
11984                 cv = function(v){
11985                     return v !== undefined && v !== null && v !== '' ?
11986                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11987                     };
11988                 break;
11989             case "bool":
11990             case "boolean":
11991                 cv = function(v){ return v === true || v === "true" || v == 1; };
11992                 break;
11993             case "date":
11994                 cv = function(v){
11995                     if(!v){
11996                         return '';
11997                     }
11998                     if(v instanceof Date){
11999                         return v;
12000                     }
12001                     if(dateFormat){
12002                         if(dateFormat == "timestamp"){
12003                             return new Date(v*1000);
12004                         }
12005                         return Date.parseDate(v, dateFormat);
12006                     }
12007                     var parsed = Date.parse(v);
12008                     return parsed ? new Date(parsed) : null;
12009                 };
12010              break;
12011             
12012         }
12013         this.convert = cv;
12014     }
12015 };
12016
12017 Roo.data.Field.prototype = {
12018     dateFormat: null,
12019     defaultValue: "",
12020     mapping: null,
12021     sortType : null,
12022     sortDir : "ASC"
12023 };/*
12024  * Based on:
12025  * Ext JS Library 1.1.1
12026  * Copyright(c) 2006-2007, Ext JS, LLC.
12027  *
12028  * Originally Released Under LGPL - original licence link has changed is not relivant.
12029  *
12030  * Fork - LGPL
12031  * <script type="text/javascript">
12032  */
12033  
12034 // Base class for reading structured data from a data source.  This class is intended to be
12035 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12036
12037 /**
12038  * @class Roo.data.DataReader
12039  * Base class for reading structured data from a data source.  This class is intended to be
12040  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12041  */
12042
12043 Roo.data.DataReader = function(meta, recordType){
12044     
12045     this.meta = meta;
12046     
12047     this.recordType = recordType instanceof Array ? 
12048         Roo.data.Record.create(recordType) : recordType;
12049 };
12050
12051 Roo.data.DataReader.prototype = {
12052      /**
12053      * Create an empty record
12054      * @param {Object} data (optional) - overlay some values
12055      * @return {Roo.data.Record} record created.
12056      */
12057     newRow :  function(d) {
12058         var da =  {};
12059         this.recordType.prototype.fields.each(function(c) {
12060             switch( c.type) {
12061                 case 'int' : da[c.name] = 0; break;
12062                 case 'date' : da[c.name] = new Date(); break;
12063                 case 'float' : da[c.name] = 0.0; break;
12064                 case 'boolean' : da[c.name] = false; break;
12065                 default : da[c.name] = ""; break;
12066             }
12067             
12068         });
12069         return new this.recordType(Roo.apply(da, d));
12070     }
12071     
12072 };/*
12073  * Based on:
12074  * Ext JS Library 1.1.1
12075  * Copyright(c) 2006-2007, Ext JS, LLC.
12076  *
12077  * Originally Released Under LGPL - original licence link has changed is not relivant.
12078  *
12079  * Fork - LGPL
12080  * <script type="text/javascript">
12081  */
12082
12083 /**
12084  * @class Roo.data.DataProxy
12085  * @extends Roo.data.Observable
12086  * This class is an abstract base class for implementations which provide retrieval of
12087  * unformatted data objects.<br>
12088  * <p>
12089  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12090  * (of the appropriate type which knows how to parse the data object) to provide a block of
12091  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12092  * <p>
12093  * Custom implementations must implement the load method as described in
12094  * {@link Roo.data.HttpProxy#load}.
12095  */
12096 Roo.data.DataProxy = function(){
12097     this.addEvents({
12098         /**
12099          * @event beforeload
12100          * Fires before a network request is made to retrieve a data object.
12101          * @param {Object} This DataProxy object.
12102          * @param {Object} params The params parameter to the load function.
12103          */
12104         beforeload : true,
12105         /**
12106          * @event load
12107          * Fires before the load method's callback is called.
12108          * @param {Object} This DataProxy object.
12109          * @param {Object} o The data object.
12110          * @param {Object} arg The callback argument object passed to the load function.
12111          */
12112         load : true,
12113         /**
12114          * @event loadexception
12115          * Fires if an Exception occurs during data retrieval.
12116          * @param {Object} This DataProxy object.
12117          * @param {Object} o The data object.
12118          * @param {Object} arg The callback argument object passed to the load function.
12119          * @param {Object} e The Exception.
12120          */
12121         loadexception : true
12122     });
12123     Roo.data.DataProxy.superclass.constructor.call(this);
12124 };
12125
12126 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12127
12128     /**
12129      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12130      */
12131 /*
12132  * Based on:
12133  * Ext JS Library 1.1.1
12134  * Copyright(c) 2006-2007, Ext JS, LLC.
12135  *
12136  * Originally Released Under LGPL - original licence link has changed is not relivant.
12137  *
12138  * Fork - LGPL
12139  * <script type="text/javascript">
12140  */
12141 /**
12142  * @class Roo.data.MemoryProxy
12143  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12144  * to the Reader when its load method is called.
12145  * @constructor
12146  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12147  */
12148 Roo.data.MemoryProxy = function(data){
12149     if (data.data) {
12150         data = data.data;
12151     }
12152     Roo.data.MemoryProxy.superclass.constructor.call(this);
12153     this.data = data;
12154 };
12155
12156 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12157     
12158     /**
12159      * Load data from the requested source (in this case an in-memory
12160      * data object passed to the constructor), read the data object into
12161      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12162      * process that block using the passed callback.
12163      * @param {Object} params This parameter is not used by the MemoryProxy class.
12164      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12165      * object into a block of Roo.data.Records.
12166      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12167      * The function must be passed <ul>
12168      * <li>The Record block object</li>
12169      * <li>The "arg" argument from the load function</li>
12170      * <li>A boolean success indicator</li>
12171      * </ul>
12172      * @param {Object} scope The scope in which to call the callback
12173      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12174      */
12175     load : function(params, reader, callback, scope, arg){
12176         params = params || {};
12177         var result;
12178         try {
12179             result = reader.readRecords(this.data);
12180         }catch(e){
12181             this.fireEvent("loadexception", this, arg, null, e);
12182             callback.call(scope, null, arg, false);
12183             return;
12184         }
12185         callback.call(scope, result, arg, true);
12186     },
12187     
12188     // private
12189     update : function(params, records){
12190         
12191     }
12192 });/*
12193  * Based on:
12194  * Ext JS Library 1.1.1
12195  * Copyright(c) 2006-2007, Ext JS, LLC.
12196  *
12197  * Originally Released Under LGPL - original licence link has changed is not relivant.
12198  *
12199  * Fork - LGPL
12200  * <script type="text/javascript">
12201  */
12202 /**
12203  * @class Roo.data.HttpProxy
12204  * @extends Roo.data.DataProxy
12205  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12206  * configured to reference a certain URL.<br><br>
12207  * <p>
12208  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12209  * from which the running page was served.<br><br>
12210  * <p>
12211  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12212  * <p>
12213  * Be aware that to enable the browser to parse an XML document, the server must set
12214  * the Content-Type header in the HTTP response to "text/xml".
12215  * @constructor
12216  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12217  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12218  * will be used to make the request.
12219  */
12220 Roo.data.HttpProxy = function(conn){
12221     Roo.data.HttpProxy.superclass.constructor.call(this);
12222     // is conn a conn config or a real conn?
12223     this.conn = conn;
12224     this.useAjax = !conn || !conn.events;
12225   
12226 };
12227
12228 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12229     // thse are take from connection...
12230     
12231     /**
12232      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12233      */
12234     /**
12235      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12236      * extra parameters to each request made by this object. (defaults to undefined)
12237      */
12238     /**
12239      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12240      *  to each request made by this object. (defaults to undefined)
12241      */
12242     /**
12243      * @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)
12244      */
12245     /**
12246      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12247      */
12248      /**
12249      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12250      * @type Boolean
12251      */
12252   
12253
12254     /**
12255      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12256      * @type Boolean
12257      */
12258     /**
12259      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12260      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12261      * a finer-grained basis than the DataProxy events.
12262      */
12263     getConnection : function(){
12264         return this.useAjax ? Roo.Ajax : this.conn;
12265     },
12266
12267     /**
12268      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12269      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12270      * process that block using the passed callback.
12271      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12272      * for the request to the remote server.
12273      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12274      * object into a block of Roo.data.Records.
12275      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12276      * The function must be passed <ul>
12277      * <li>The Record block object</li>
12278      * <li>The "arg" argument from the load function</li>
12279      * <li>A boolean success indicator</li>
12280      * </ul>
12281      * @param {Object} scope The scope in which to call the callback
12282      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12283      */
12284     load : function(params, reader, callback, scope, arg){
12285         if(this.fireEvent("beforeload", this, params) !== false){
12286             var  o = {
12287                 params : params || {},
12288                 request: {
12289                     callback : callback,
12290                     scope : scope,
12291                     arg : arg
12292                 },
12293                 reader: reader,
12294                 callback : this.loadResponse,
12295                 scope: this
12296             };
12297             if(this.useAjax){
12298                 Roo.applyIf(o, this.conn);
12299                 if(this.activeRequest){
12300                     Roo.Ajax.abort(this.activeRequest);
12301                 }
12302                 this.activeRequest = Roo.Ajax.request(o);
12303             }else{
12304                 this.conn.request(o);
12305             }
12306         }else{
12307             callback.call(scope||this, null, arg, false);
12308         }
12309     },
12310
12311     // private
12312     loadResponse : function(o, success, response){
12313         delete this.activeRequest;
12314         if(!success){
12315             this.fireEvent("loadexception", this, o, response);
12316             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12317             return;
12318         }
12319         var result;
12320         try {
12321             result = o.reader.read(response);
12322         }catch(e){
12323             this.fireEvent("loadexception", this, o, response, e);
12324             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12325             return;
12326         }
12327         
12328         this.fireEvent("load", this, o, o.request.arg);
12329         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12330     },
12331
12332     // private
12333     update : function(dataSet){
12334
12335     },
12336
12337     // private
12338     updateResponse : function(dataSet){
12339
12340     }
12341 });/*
12342  * Based on:
12343  * Ext JS Library 1.1.1
12344  * Copyright(c) 2006-2007, Ext JS, LLC.
12345  *
12346  * Originally Released Under LGPL - original licence link has changed is not relivant.
12347  *
12348  * Fork - LGPL
12349  * <script type="text/javascript">
12350  */
12351
12352 /**
12353  * @class Roo.data.ScriptTagProxy
12354  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12355  * other than the originating domain of the running page.<br><br>
12356  * <p>
12357  * <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
12358  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12359  * <p>
12360  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12361  * source code that is used as the source inside a &lt;script> tag.<br><br>
12362  * <p>
12363  * In order for the browser to process the returned data, the server must wrap the data object
12364  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12365  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12366  * depending on whether the callback name was passed:
12367  * <p>
12368  * <pre><code>
12369 boolean scriptTag = false;
12370 String cb = request.getParameter("callback");
12371 if (cb != null) {
12372     scriptTag = true;
12373     response.setContentType("text/javascript");
12374 } else {
12375     response.setContentType("application/x-json");
12376 }
12377 Writer out = response.getWriter();
12378 if (scriptTag) {
12379     out.write(cb + "(");
12380 }
12381 out.print(dataBlock.toJsonString());
12382 if (scriptTag) {
12383     out.write(");");
12384 }
12385 </pre></code>
12386  *
12387  * @constructor
12388  * @param {Object} config A configuration object.
12389  */
12390 Roo.data.ScriptTagProxy = function(config){
12391     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12392     Roo.apply(this, config);
12393     this.head = document.getElementsByTagName("head")[0];
12394 };
12395
12396 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12397
12398 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12399     /**
12400      * @cfg {String} url The URL from which to request the data object.
12401      */
12402     /**
12403      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12404      */
12405     timeout : 30000,
12406     /**
12407      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12408      * the server the name of the callback function set up by the load call to process the returned data object.
12409      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12410      * javascript output which calls this named function passing the data object as its only parameter.
12411      */
12412     callbackParam : "callback",
12413     /**
12414      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12415      * name to the request.
12416      */
12417     nocache : true,
12418
12419     /**
12420      * Load data from the configured URL, read the data object into
12421      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12422      * process that block using the passed callback.
12423      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12424      * for the request to the remote server.
12425      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12426      * object into a block of Roo.data.Records.
12427      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12428      * The function must be passed <ul>
12429      * <li>The Record block object</li>
12430      * <li>The "arg" argument from the load function</li>
12431      * <li>A boolean success indicator</li>
12432      * </ul>
12433      * @param {Object} scope The scope in which to call the callback
12434      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12435      */
12436     load : function(params, reader, callback, scope, arg){
12437         if(this.fireEvent("beforeload", this, params) !== false){
12438
12439             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12440
12441             var url = this.url;
12442             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12443             if(this.nocache){
12444                 url += "&_dc=" + (new Date().getTime());
12445             }
12446             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12447             var trans = {
12448                 id : transId,
12449                 cb : "stcCallback"+transId,
12450                 scriptId : "stcScript"+transId,
12451                 params : params,
12452                 arg : arg,
12453                 url : url,
12454                 callback : callback,
12455                 scope : scope,
12456                 reader : reader
12457             };
12458             var conn = this;
12459
12460             window[trans.cb] = function(o){
12461                 conn.handleResponse(o, trans);
12462             };
12463
12464             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12465
12466             if(this.autoAbort !== false){
12467                 this.abort();
12468             }
12469
12470             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12471
12472             var script = document.createElement("script");
12473             script.setAttribute("src", url);
12474             script.setAttribute("type", "text/javascript");
12475             script.setAttribute("id", trans.scriptId);
12476             this.head.appendChild(script);
12477
12478             this.trans = trans;
12479         }else{
12480             callback.call(scope||this, null, arg, false);
12481         }
12482     },
12483
12484     // private
12485     isLoading : function(){
12486         return this.trans ? true : false;
12487     },
12488
12489     /**
12490      * Abort the current server request.
12491      */
12492     abort : function(){
12493         if(this.isLoading()){
12494             this.destroyTrans(this.trans);
12495         }
12496     },
12497
12498     // private
12499     destroyTrans : function(trans, isLoaded){
12500         this.head.removeChild(document.getElementById(trans.scriptId));
12501         clearTimeout(trans.timeoutId);
12502         if(isLoaded){
12503             window[trans.cb] = undefined;
12504             try{
12505                 delete window[trans.cb];
12506             }catch(e){}
12507         }else{
12508             // if hasn't been loaded, wait for load to remove it to prevent script error
12509             window[trans.cb] = function(){
12510                 window[trans.cb] = undefined;
12511                 try{
12512                     delete window[trans.cb];
12513                 }catch(e){}
12514             };
12515         }
12516     },
12517
12518     // private
12519     handleResponse : function(o, trans){
12520         this.trans = false;
12521         this.destroyTrans(trans, true);
12522         var result;
12523         try {
12524             result = trans.reader.readRecords(o);
12525         }catch(e){
12526             this.fireEvent("loadexception", this, o, trans.arg, e);
12527             trans.callback.call(trans.scope||window, null, trans.arg, false);
12528             return;
12529         }
12530         this.fireEvent("load", this, o, trans.arg);
12531         trans.callback.call(trans.scope||window, result, trans.arg, true);
12532     },
12533
12534     // private
12535     handleFailure : function(trans){
12536         this.trans = false;
12537         this.destroyTrans(trans, false);
12538         this.fireEvent("loadexception", this, null, trans.arg);
12539         trans.callback.call(trans.scope||window, null, trans.arg, false);
12540     }
12541 });/*
12542  * Based on:
12543  * Ext JS Library 1.1.1
12544  * Copyright(c) 2006-2007, Ext JS, LLC.
12545  *
12546  * Originally Released Under LGPL - original licence link has changed is not relivant.
12547  *
12548  * Fork - LGPL
12549  * <script type="text/javascript">
12550  */
12551
12552 /**
12553  * @class Roo.data.JsonReader
12554  * @extends Roo.data.DataReader
12555  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12556  * based on mappings in a provided Roo.data.Record constructor.
12557  * 
12558  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12559  * in the reply previously. 
12560  * 
12561  * <p>
12562  * Example code:
12563  * <pre><code>
12564 var RecordDef = Roo.data.Record.create([
12565     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12566     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12567 ]);
12568 var myReader = new Roo.data.JsonReader({
12569     totalProperty: "results",    // The property which contains the total dataset size (optional)
12570     root: "rows",                // The property which contains an Array of row objects
12571     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12572 }, RecordDef);
12573 </code></pre>
12574  * <p>
12575  * This would consume a JSON file like this:
12576  * <pre><code>
12577 { 'results': 2, 'rows': [
12578     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12579     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12580 }
12581 </code></pre>
12582  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12583  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12584  * paged from the remote server.
12585  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12586  * @cfg {String} root name of the property which contains the Array of row objects.
12587  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12588  * @cfg {Array} fields Array of field definition objects
12589  * @constructor
12590  * Create a new JsonReader
12591  * @param {Object} meta Metadata configuration options
12592  * @param {Object} recordType Either an Array of field definition objects,
12593  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12594  */
12595 Roo.data.JsonReader = function(meta, recordType){
12596     
12597     meta = meta || {};
12598     // set some defaults:
12599     Roo.applyIf(meta, {
12600         totalProperty: 'total',
12601         successProperty : 'success',
12602         root : 'data',
12603         id : 'id'
12604     });
12605     
12606     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12607 };
12608 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12609     
12610     /**
12611      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12612      * Used by Store query builder to append _requestMeta to params.
12613      * 
12614      */
12615     metaFromRemote : false,
12616     /**
12617      * This method is only used by a DataProxy which has retrieved data from a remote server.
12618      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12619      * @return {Object} data A data block which is used by an Roo.data.Store object as
12620      * a cache of Roo.data.Records.
12621      */
12622     read : function(response){
12623         var json = response.responseText;
12624        
12625         var o = /* eval:var:o */ eval("("+json+")");
12626         if(!o) {
12627             throw {message: "JsonReader.read: Json object not found"};
12628         }
12629         
12630         if(o.metaData){
12631             
12632             delete this.ef;
12633             this.metaFromRemote = true;
12634             this.meta = o.metaData;
12635             this.recordType = Roo.data.Record.create(o.metaData.fields);
12636             this.onMetaChange(this.meta, this.recordType, o);
12637         }
12638         return this.readRecords(o);
12639     },
12640
12641     // private function a store will implement
12642     onMetaChange : function(meta, recordType, o){
12643
12644     },
12645
12646     /**
12647          * @ignore
12648          */
12649     simpleAccess: function(obj, subsc) {
12650         return obj[subsc];
12651     },
12652
12653         /**
12654          * @ignore
12655          */
12656     getJsonAccessor: function(){
12657         var re = /[\[\.]/;
12658         return function(expr) {
12659             try {
12660                 return(re.test(expr))
12661                     ? new Function("obj", "return obj." + expr)
12662                     : function(obj){
12663                         return obj[expr];
12664                     };
12665             } catch(e){}
12666             return Roo.emptyFn;
12667         };
12668     }(),
12669
12670     /**
12671      * Create a data block containing Roo.data.Records from an XML document.
12672      * @param {Object} o An object which contains an Array of row objects in the property specified
12673      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12674      * which contains the total size of the dataset.
12675      * @return {Object} data A data block which is used by an Roo.data.Store object as
12676      * a cache of Roo.data.Records.
12677      */
12678     readRecords : function(o){
12679         /**
12680          * After any data loads, the raw JSON data is available for further custom processing.
12681          * @type Object
12682          */
12683         this.o = o;
12684         var s = this.meta, Record = this.recordType,
12685             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12686
12687 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12688         if (!this.ef) {
12689             if(s.totalProperty) {
12690                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12691                 }
12692                 if(s.successProperty) {
12693                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12694                 }
12695                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12696                 if (s.id) {
12697                         var g = this.getJsonAccessor(s.id);
12698                         this.getId = function(rec) {
12699                                 var r = g(rec);  
12700                                 return (r === undefined || r === "") ? null : r;
12701                         };
12702                 } else {
12703                         this.getId = function(){return null;};
12704                 }
12705             this.ef = [];
12706             for(var jj = 0; jj < fl; jj++){
12707                 f = fi[jj];
12708                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12709                 this.ef[jj] = this.getJsonAccessor(map);
12710             }
12711         }
12712
12713         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12714         if(s.totalProperty){
12715             var vt = parseInt(this.getTotal(o), 10);
12716             if(!isNaN(vt)){
12717                 totalRecords = vt;
12718             }
12719         }
12720         if(s.successProperty){
12721             var vs = this.getSuccess(o);
12722             if(vs === false || vs === 'false'){
12723                 success = false;
12724             }
12725         }
12726         var records = [];
12727         for(var i = 0; i < c; i++){
12728                 var n = root[i];
12729             var values = {};
12730             var id = this.getId(n);
12731             for(var j = 0; j < fl; j++){
12732                 f = fi[j];
12733             var v = this.ef[j](n);
12734             if (!f.convert) {
12735                 Roo.log('missing convert for ' + f.name);
12736                 Roo.log(f);
12737                 continue;
12738             }
12739             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12740             }
12741             var record = new Record(values, id);
12742             record.json = n;
12743             records[i] = record;
12744         }
12745         return {
12746             raw : o,
12747             success : success,
12748             records : records,
12749             totalRecords : totalRecords
12750         };
12751     }
12752 });/*
12753  * Based on:
12754  * Ext JS Library 1.1.1
12755  * Copyright(c) 2006-2007, Ext JS, LLC.
12756  *
12757  * Originally Released Under LGPL - original licence link has changed is not relivant.
12758  *
12759  * Fork - LGPL
12760  * <script type="text/javascript">
12761  */
12762
12763 /**
12764  * @class Roo.data.ArrayReader
12765  * @extends Roo.data.DataReader
12766  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12767  * Each element of that Array represents a row of data fields. The
12768  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12769  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12770  * <p>
12771  * Example code:.
12772  * <pre><code>
12773 var RecordDef = Roo.data.Record.create([
12774     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12775     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12776 ]);
12777 var myReader = new Roo.data.ArrayReader({
12778     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12779 }, RecordDef);
12780 </code></pre>
12781  * <p>
12782  * This would consume an Array like this:
12783  * <pre><code>
12784 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12785   </code></pre>
12786  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12787  * @constructor
12788  * Create a new JsonReader
12789  * @param {Object} meta Metadata configuration options.
12790  * @param {Object} recordType Either an Array of field definition objects
12791  * as specified to {@link Roo.data.Record#create},
12792  * or an {@link Roo.data.Record} object
12793  * created using {@link Roo.data.Record#create}.
12794  */
12795 Roo.data.ArrayReader = function(meta, recordType){
12796     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12797 };
12798
12799 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12800     /**
12801      * Create a data block containing Roo.data.Records from an XML document.
12802      * @param {Object} o An Array of row objects which represents the dataset.
12803      * @return {Object} data A data block which is used by an Roo.data.Store object as
12804      * a cache of Roo.data.Records.
12805      */
12806     readRecords : function(o){
12807         var sid = this.meta ? this.meta.id : null;
12808         var recordType = this.recordType, fields = recordType.prototype.fields;
12809         var records = [];
12810         var root = o;
12811             for(var i = 0; i < root.length; i++){
12812                     var n = root[i];
12813                 var values = {};
12814                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12815                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12816                 var f = fields.items[j];
12817                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12818                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12819                 v = f.convert(v);
12820                 values[f.name] = v;
12821             }
12822                 var record = new recordType(values, id);
12823                 record.json = n;
12824                 records[records.length] = record;
12825             }
12826             return {
12827                 records : records,
12828                 totalRecords : records.length
12829             };
12830     }
12831 });/*
12832  * - LGPL
12833  * * 
12834  */
12835
12836 /**
12837  * @class Roo.bootstrap.ComboBox
12838  * @extends Roo.bootstrap.TriggerField
12839  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12840  * @cfg {Boolean} append (true|false) default false
12841  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12842  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12843  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12844  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12845  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12846  * @cfg {Boolean} animate default true
12847  * @cfg {Boolean} emptyResultText only for touch device
12848  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12849  * @cfg {String} emptyTitle default ''
12850  * @constructor
12851  * Create a new ComboBox.
12852  * @param {Object} config Configuration options
12853  */
12854 Roo.bootstrap.ComboBox = function(config){
12855     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12856     this.addEvents({
12857         /**
12858          * @event expand
12859          * Fires when the dropdown list is expanded
12860         * @param {Roo.bootstrap.ComboBox} combo This combo box
12861         */
12862         'expand' : true,
12863         /**
12864          * @event collapse
12865          * Fires when the dropdown list is collapsed
12866         * @param {Roo.bootstrap.ComboBox} combo This combo box
12867         */
12868         'collapse' : true,
12869         /**
12870          * @event beforeselect
12871          * Fires before a list item is selected. Return false to cancel the selection.
12872         * @param {Roo.bootstrap.ComboBox} combo This combo box
12873         * @param {Roo.data.Record} record The data record returned from the underlying store
12874         * @param {Number} index The index of the selected item in the dropdown list
12875         */
12876         'beforeselect' : true,
12877         /**
12878          * @event select
12879          * Fires when a list item is selected
12880         * @param {Roo.bootstrap.ComboBox} combo This combo box
12881         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12882         * @param {Number} index The index of the selected item in the dropdown list
12883         */
12884         'select' : true,
12885         /**
12886          * @event beforequery
12887          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12888          * The event object passed has these properties:
12889         * @param {Roo.bootstrap.ComboBox} combo This combo box
12890         * @param {String} query The query
12891         * @param {Boolean} forceAll true to force "all" query
12892         * @param {Boolean} cancel true to cancel the query
12893         * @param {Object} e The query event object
12894         */
12895         'beforequery': true,
12896          /**
12897          * @event add
12898          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12899         * @param {Roo.bootstrap.ComboBox} combo This combo box
12900         */
12901         'add' : true,
12902         /**
12903          * @event edit
12904          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12905         * @param {Roo.bootstrap.ComboBox} combo This combo box
12906         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12907         */
12908         'edit' : true,
12909         /**
12910          * @event remove
12911          * Fires when the remove value from the combobox array
12912         * @param {Roo.bootstrap.ComboBox} combo This combo box
12913         */
12914         'remove' : true,
12915         /**
12916          * @event afterremove
12917          * Fires when the remove value from the combobox array
12918         * @param {Roo.bootstrap.ComboBox} combo This combo box
12919         */
12920         'afterremove' : true,
12921         /**
12922          * @event specialfilter
12923          * Fires when specialfilter
12924             * @param {Roo.bootstrap.ComboBox} combo This combo box
12925             */
12926         'specialfilter' : true,
12927         /**
12928          * @event tick
12929          * Fires when tick the element
12930             * @param {Roo.bootstrap.ComboBox} combo This combo box
12931             */
12932         'tick' : true,
12933         /**
12934          * @event touchviewdisplay
12935          * Fires when touch view require special display (default is using displayField)
12936             * @param {Roo.bootstrap.ComboBox} combo This combo box
12937             * @param {Object} cfg set html .
12938             */
12939         'touchviewdisplay' : true
12940         
12941     });
12942     
12943     this.item = [];
12944     this.tickItems = [];
12945     
12946     this.selectedIndex = -1;
12947     if(this.mode == 'local'){
12948         if(config.queryDelay === undefined){
12949             this.queryDelay = 10;
12950         }
12951         if(config.minChars === undefined){
12952             this.minChars = 0;
12953         }
12954     }
12955 };
12956
12957 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12958      
12959     /**
12960      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12961      * rendering into an Roo.Editor, defaults to false)
12962      */
12963     /**
12964      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12965      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12966      */
12967     /**
12968      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12969      */
12970     /**
12971      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12972      * the dropdown list (defaults to undefined, with no header element)
12973      */
12974
12975      /**
12976      * @cfg {String/Roo.Template} tpl The template to use to render the output
12977      */
12978      
12979      /**
12980      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12981      */
12982     listWidth: undefined,
12983     /**
12984      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12985      * mode = 'remote' or 'text' if mode = 'local')
12986      */
12987     displayField: undefined,
12988     
12989     /**
12990      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12991      * mode = 'remote' or 'value' if mode = 'local'). 
12992      * Note: use of a valueField requires the user make a selection
12993      * in order for a value to be mapped.
12994      */
12995     valueField: undefined,
12996     /**
12997      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12998      */
12999     modalTitle : '',
13000     
13001     /**
13002      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13003      * field's data value (defaults to the underlying DOM element's name)
13004      */
13005     hiddenName: undefined,
13006     /**
13007      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13008      */
13009     listClass: '',
13010     /**
13011      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13012      */
13013     selectedClass: 'active',
13014     
13015     /**
13016      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13017      */
13018     shadow:'sides',
13019     /**
13020      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13021      * anchor positions (defaults to 'tl-bl')
13022      */
13023     listAlign: 'tl-bl?',
13024     /**
13025      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13026      */
13027     maxHeight: 300,
13028     /**
13029      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13030      * query specified by the allQuery config option (defaults to 'query')
13031      */
13032     triggerAction: 'query',
13033     /**
13034      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13035      * (defaults to 4, does not apply if editable = false)
13036      */
13037     minChars : 4,
13038     /**
13039      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13040      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13041      */
13042     typeAhead: false,
13043     /**
13044      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13045      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13046      */
13047     queryDelay: 500,
13048     /**
13049      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13050      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13051      */
13052     pageSize: 0,
13053     /**
13054      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13055      * when editable = true (defaults to false)
13056      */
13057     selectOnFocus:false,
13058     /**
13059      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13060      */
13061     queryParam: 'query',
13062     /**
13063      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13064      * when mode = 'remote' (defaults to 'Loading...')
13065      */
13066     loadingText: 'Loading...',
13067     /**
13068      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13069      */
13070     resizable: false,
13071     /**
13072      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13073      */
13074     handleHeight : 8,
13075     /**
13076      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13077      * traditional select (defaults to true)
13078      */
13079     editable: true,
13080     /**
13081      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13082      */
13083     allQuery: '',
13084     /**
13085      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13086      */
13087     mode: 'remote',
13088     /**
13089      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13090      * listWidth has a higher value)
13091      */
13092     minListWidth : 70,
13093     /**
13094      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13095      * allow the user to set arbitrary text into the field (defaults to false)
13096      */
13097     forceSelection:false,
13098     /**
13099      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13100      * if typeAhead = true (defaults to 250)
13101      */
13102     typeAheadDelay : 250,
13103     /**
13104      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13105      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13106      */
13107     valueNotFoundText : undefined,
13108     /**
13109      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13110      */
13111     blockFocus : false,
13112     
13113     /**
13114      * @cfg {Boolean} disableClear Disable showing of clear button.
13115      */
13116     disableClear : false,
13117     /**
13118      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13119      */
13120     alwaysQuery : false,
13121     
13122     /**
13123      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13124      */
13125     multiple : false,
13126     
13127     /**
13128      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13129      */
13130     invalidClass : "has-warning",
13131     
13132     /**
13133      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13134      */
13135     validClass : "has-success",
13136     
13137     /**
13138      * @cfg {Boolean} specialFilter (true|false) special filter default false
13139      */
13140     specialFilter : false,
13141     
13142     /**
13143      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13144      */
13145     mobileTouchView : true,
13146     
13147     /**
13148      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13149      */
13150     useNativeIOS : false,
13151     
13152     /**
13153      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13154      */
13155     mobile_restrict_height : false,
13156     
13157     ios_options : false,
13158     
13159     //private
13160     addicon : false,
13161     editicon: false,
13162     
13163     page: 0,
13164     hasQuery: false,
13165     append: false,
13166     loadNext: false,
13167     autoFocus : true,
13168     tickable : false,
13169     btnPosition : 'right',
13170     triggerList : true,
13171     showToggleBtn : true,
13172     animate : true,
13173     emptyResultText: 'Empty',
13174     triggerText : 'Select',
13175     emptyTitle : '',
13176     
13177     // element that contains real text value.. (when hidden is used..)
13178     
13179     getAutoCreate : function()
13180     {   
13181         var cfg = false;
13182         //render
13183         /*
13184          * Render classic select for iso
13185          */
13186         
13187         if(Roo.isIOS && this.useNativeIOS){
13188             cfg = this.getAutoCreateNativeIOS();
13189             return cfg;
13190         }
13191         
13192         /*
13193          * Touch Devices
13194          */
13195         
13196         if(Roo.isTouch && this.mobileTouchView){
13197             cfg = this.getAutoCreateTouchView();
13198             return cfg;;
13199         }
13200         
13201         /*
13202          *  Normal ComboBox
13203          */
13204         if(!this.tickable){
13205             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13206             return cfg;
13207         }
13208         
13209         /*
13210          *  ComboBox with tickable selections
13211          */
13212              
13213         var align = this.labelAlign || this.parentLabelAlign();
13214         
13215         cfg = {
13216             cls : 'form-group roo-combobox-tickable' //input-group
13217         };
13218         
13219         var btn_text_select = '';
13220         var btn_text_done = '';
13221         var btn_text_cancel = '';
13222         
13223         if (this.btn_text_show) {
13224             btn_text_select = 'Select';
13225             btn_text_done = 'Done';
13226             btn_text_cancel = 'Cancel'; 
13227         }
13228         
13229         var buttons = {
13230             tag : 'div',
13231             cls : 'tickable-buttons',
13232             cn : [
13233                 {
13234                     tag : 'button',
13235                     type : 'button',
13236                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13237                     //html : this.triggerText
13238                     html: btn_text_select
13239                 },
13240                 {
13241                     tag : 'button',
13242                     type : 'button',
13243                     name : 'ok',
13244                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13245                     //html : 'Done'
13246                     html: btn_text_done
13247                 },
13248                 {
13249                     tag : 'button',
13250                     type : 'button',
13251                     name : 'cancel',
13252                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13253                     //html : 'Cancel'
13254                     html: btn_text_cancel
13255                 }
13256             ]
13257         };
13258         
13259         if(this.editable){
13260             buttons.cn.unshift({
13261                 tag: 'input',
13262                 cls: 'roo-select2-search-field-input'
13263             });
13264         }
13265         
13266         var _this = this;
13267         
13268         Roo.each(buttons.cn, function(c){
13269             if (_this.size) {
13270                 c.cls += ' btn-' + _this.size;
13271             }
13272
13273             if (_this.disabled) {
13274                 c.disabled = true;
13275             }
13276         });
13277         
13278         var box = {
13279             tag: 'div',
13280             cn: [
13281                 {
13282                     tag: 'input',
13283                     type : 'hidden',
13284                     cls: 'form-hidden-field'
13285                 },
13286                 {
13287                     tag: 'ul',
13288                     cls: 'roo-select2-choices',
13289                     cn:[
13290                         {
13291                             tag: 'li',
13292                             cls: 'roo-select2-search-field',
13293                             cn: [
13294                                 buttons
13295                             ]
13296                         }
13297                     ]
13298                 }
13299             ]
13300         };
13301         
13302         var combobox = {
13303             cls: 'roo-select2-container input-group roo-select2-container-multi',
13304             cn: [
13305                 box
13306 //                {
13307 //                    tag: 'ul',
13308 //                    cls: 'typeahead typeahead-long dropdown-menu',
13309 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13310 //                }
13311             ]
13312         };
13313         
13314         if(this.hasFeedback && !this.allowBlank){
13315             
13316             var feedback = {
13317                 tag: 'span',
13318                 cls: 'glyphicon form-control-feedback'
13319             };
13320
13321             combobox.cn.push(feedback);
13322         }
13323         
13324         
13325         if (align ==='left' && this.fieldLabel.length) {
13326             
13327             cfg.cls += ' roo-form-group-label-left';
13328             
13329             cfg.cn = [
13330                 {
13331                     tag : 'i',
13332                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13333                     tooltip : 'This field is required'
13334                 },
13335                 {
13336                     tag: 'label',
13337                     'for' :  id,
13338                     cls : 'control-label',
13339                     html : this.fieldLabel
13340
13341                 },
13342                 {
13343                     cls : "", 
13344                     cn: [
13345                         combobox
13346                     ]
13347                 }
13348
13349             ];
13350             
13351             var labelCfg = cfg.cn[1];
13352             var contentCfg = cfg.cn[2];
13353             
13354
13355             if(this.indicatorpos == 'right'){
13356                 
13357                 cfg.cn = [
13358                     {
13359                         tag: 'label',
13360                         'for' :  id,
13361                         cls : 'control-label',
13362                         cn : [
13363                             {
13364                                 tag : 'span',
13365                                 html : this.fieldLabel
13366                             },
13367                             {
13368                                 tag : 'i',
13369                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13370                                 tooltip : 'This field is required'
13371                             }
13372                         ]
13373                     },
13374                     {
13375                         cls : "",
13376                         cn: [
13377                             combobox
13378                         ]
13379                     }
13380
13381                 ];
13382                 
13383                 
13384                 
13385                 labelCfg = cfg.cn[0];
13386                 contentCfg = cfg.cn[1];
13387             
13388             }
13389             
13390             if(this.labelWidth > 12){
13391                 labelCfg.style = "width: " + this.labelWidth + 'px';
13392             }
13393             
13394             if(this.labelWidth < 13 && this.labelmd == 0){
13395                 this.labelmd = this.labelWidth;
13396             }
13397             
13398             if(this.labellg > 0){
13399                 labelCfg.cls += ' col-lg-' + this.labellg;
13400                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13401             }
13402             
13403             if(this.labelmd > 0){
13404                 labelCfg.cls += ' col-md-' + this.labelmd;
13405                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13406             }
13407             
13408             if(this.labelsm > 0){
13409                 labelCfg.cls += ' col-sm-' + this.labelsm;
13410                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13411             }
13412             
13413             if(this.labelxs > 0){
13414                 labelCfg.cls += ' col-xs-' + this.labelxs;
13415                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13416             }
13417                 
13418                 
13419         } else if ( this.fieldLabel.length) {
13420 //                Roo.log(" label");
13421                  cfg.cn = [
13422                     {
13423                         tag : 'i',
13424                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13425                         tooltip : 'This field is required'
13426                     },
13427                     {
13428                         tag: 'label',
13429                         //cls : 'input-group-addon',
13430                         html : this.fieldLabel
13431                     },
13432                     combobox
13433                 ];
13434                 
13435                 if(this.indicatorpos == 'right'){
13436                     cfg.cn = [
13437                         {
13438                             tag: 'label',
13439                             //cls : 'input-group-addon',
13440                             html : this.fieldLabel
13441                         },
13442                         {
13443                             tag : 'i',
13444                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13445                             tooltip : 'This field is required'
13446                         },
13447                         combobox
13448                     ];
13449                     
13450                 }
13451
13452         } else {
13453             
13454 //                Roo.log(" no label && no align");
13455                 cfg = combobox
13456                      
13457                 
13458         }
13459          
13460         var settings=this;
13461         ['xs','sm','md','lg'].map(function(size){
13462             if (settings[size]) {
13463                 cfg.cls += ' col-' + size + '-' + settings[size];
13464             }
13465         });
13466         
13467         return cfg;
13468         
13469     },
13470     
13471     _initEventsCalled : false,
13472     
13473     // private
13474     initEvents: function()
13475     {   
13476         if (this._initEventsCalled) { // as we call render... prevent looping...
13477             return;
13478         }
13479         this._initEventsCalled = true;
13480         
13481         if (!this.store) {
13482             throw "can not find store for combo";
13483         }
13484         
13485         this.indicator = this.indicatorEl();
13486         
13487         this.store = Roo.factory(this.store, Roo.data);
13488         this.store.parent = this;
13489         
13490         // if we are building from html. then this element is so complex, that we can not really
13491         // use the rendered HTML.
13492         // so we have to trash and replace the previous code.
13493         if (Roo.XComponent.build_from_html) {
13494             // remove this element....
13495             var e = this.el.dom, k=0;
13496             while (e ) { e = e.previousSibling;  ++k;}
13497
13498             this.el.remove();
13499             
13500             this.el=false;
13501             this.rendered = false;
13502             
13503             this.render(this.parent().getChildContainer(true), k);
13504         }
13505         
13506         if(Roo.isIOS && this.useNativeIOS){
13507             this.initIOSView();
13508             return;
13509         }
13510         
13511         /*
13512          * Touch Devices
13513          */
13514         
13515         if(Roo.isTouch && this.mobileTouchView){
13516             this.initTouchView();
13517             return;
13518         }
13519         
13520         if(this.tickable){
13521             this.initTickableEvents();
13522             return;
13523         }
13524         
13525         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13526         
13527         if(this.hiddenName){
13528             
13529             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13530             
13531             this.hiddenField.dom.value =
13532                 this.hiddenValue !== undefined ? this.hiddenValue :
13533                 this.value !== undefined ? this.value : '';
13534
13535             // prevent input submission
13536             this.el.dom.removeAttribute('name');
13537             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13538              
13539              
13540         }
13541         //if(Roo.isGecko){
13542         //    this.el.dom.setAttribute('autocomplete', 'off');
13543         //}
13544         
13545         var cls = 'x-combo-list';
13546         
13547         //this.list = new Roo.Layer({
13548         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13549         //});
13550         
13551         var _this = this;
13552         
13553         (function(){
13554             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13555             _this.list.setWidth(lw);
13556         }).defer(100);
13557         
13558         this.list.on('mouseover', this.onViewOver, this);
13559         this.list.on('mousemove', this.onViewMove, this);
13560         this.list.on('scroll', this.onViewScroll, this);
13561         
13562         /*
13563         this.list.swallowEvent('mousewheel');
13564         this.assetHeight = 0;
13565
13566         if(this.title){
13567             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13568             this.assetHeight += this.header.getHeight();
13569         }
13570
13571         this.innerList = this.list.createChild({cls:cls+'-inner'});
13572         this.innerList.on('mouseover', this.onViewOver, this);
13573         this.innerList.on('mousemove', this.onViewMove, this);
13574         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13575         
13576         if(this.allowBlank && !this.pageSize && !this.disableClear){
13577             this.footer = this.list.createChild({cls:cls+'-ft'});
13578             this.pageTb = new Roo.Toolbar(this.footer);
13579            
13580         }
13581         if(this.pageSize){
13582             this.footer = this.list.createChild({cls:cls+'-ft'});
13583             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13584                     {pageSize: this.pageSize});
13585             
13586         }
13587         
13588         if (this.pageTb && this.allowBlank && !this.disableClear) {
13589             var _this = this;
13590             this.pageTb.add(new Roo.Toolbar.Fill(), {
13591                 cls: 'x-btn-icon x-btn-clear',
13592                 text: '&#160;',
13593                 handler: function()
13594                 {
13595                     _this.collapse();
13596                     _this.clearValue();
13597                     _this.onSelect(false, -1);
13598                 }
13599             });
13600         }
13601         if (this.footer) {
13602             this.assetHeight += this.footer.getHeight();
13603         }
13604         */
13605             
13606         if(!this.tpl){
13607             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13608         }
13609
13610         this.view = new Roo.View(this.list, this.tpl, {
13611             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13612         });
13613         //this.view.wrapEl.setDisplayed(false);
13614         this.view.on('click', this.onViewClick, this);
13615         
13616         
13617         this.store.on('beforeload', this.onBeforeLoad, this);
13618         this.store.on('load', this.onLoad, this);
13619         this.store.on('loadexception', this.onLoadException, this);
13620         /*
13621         if(this.resizable){
13622             this.resizer = new Roo.Resizable(this.list,  {
13623                pinned:true, handles:'se'
13624             });
13625             this.resizer.on('resize', function(r, w, h){
13626                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13627                 this.listWidth = w;
13628                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13629                 this.restrictHeight();
13630             }, this);
13631             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13632         }
13633         */
13634         if(!this.editable){
13635             this.editable = true;
13636             this.setEditable(false);
13637         }
13638         
13639         /*
13640         
13641         if (typeof(this.events.add.listeners) != 'undefined') {
13642             
13643             this.addicon = this.wrap.createChild(
13644                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13645        
13646             this.addicon.on('click', function(e) {
13647                 this.fireEvent('add', this);
13648             }, this);
13649         }
13650         if (typeof(this.events.edit.listeners) != 'undefined') {
13651             
13652             this.editicon = this.wrap.createChild(
13653                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13654             if (this.addicon) {
13655                 this.editicon.setStyle('margin-left', '40px');
13656             }
13657             this.editicon.on('click', function(e) {
13658                 
13659                 // we fire even  if inothing is selected..
13660                 this.fireEvent('edit', this, this.lastData );
13661                 
13662             }, this);
13663         }
13664         */
13665         
13666         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13667             "up" : function(e){
13668                 this.inKeyMode = true;
13669                 this.selectPrev();
13670             },
13671
13672             "down" : function(e){
13673                 if(!this.isExpanded()){
13674                     this.onTriggerClick();
13675                 }else{
13676                     this.inKeyMode = true;
13677                     this.selectNext();
13678                 }
13679             },
13680
13681             "enter" : function(e){
13682 //                this.onViewClick();
13683                 //return true;
13684                 this.collapse();
13685                 
13686                 if(this.fireEvent("specialkey", this, e)){
13687                     this.onViewClick(false);
13688                 }
13689                 
13690                 return true;
13691             },
13692
13693             "esc" : function(e){
13694                 this.collapse();
13695             },
13696
13697             "tab" : function(e){
13698                 this.collapse();
13699                 
13700                 if(this.fireEvent("specialkey", this, e)){
13701                     this.onViewClick(false);
13702                 }
13703                 
13704                 return true;
13705             },
13706
13707             scope : this,
13708
13709             doRelay : function(foo, bar, hname){
13710                 if(hname == 'down' || this.scope.isExpanded()){
13711                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13712                 }
13713                 return true;
13714             },
13715
13716             forceKeyDown: true
13717         });
13718         
13719         
13720         this.queryDelay = Math.max(this.queryDelay || 10,
13721                 this.mode == 'local' ? 10 : 250);
13722         
13723         
13724         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13725         
13726         if(this.typeAhead){
13727             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13728         }
13729         if(this.editable !== false){
13730             this.inputEl().on("keyup", this.onKeyUp, this);
13731         }
13732         if(this.forceSelection){
13733             this.inputEl().on('blur', this.doForce, this);
13734         }
13735         
13736         if(this.multiple){
13737             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13738             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13739         }
13740     },
13741     
13742     initTickableEvents: function()
13743     {   
13744         this.createList();
13745         
13746         if(this.hiddenName){
13747             
13748             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13749             
13750             this.hiddenField.dom.value =
13751                 this.hiddenValue !== undefined ? this.hiddenValue :
13752                 this.value !== undefined ? this.value : '';
13753
13754             // prevent input submission
13755             this.el.dom.removeAttribute('name');
13756             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13757              
13758              
13759         }
13760         
13761 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13762         
13763         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13764         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13765         if(this.triggerList){
13766             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13767         }
13768          
13769         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13770         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13771         
13772         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13773         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13774         
13775         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13776         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13777         
13778         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13779         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13780         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13781         
13782         this.okBtn.hide();
13783         this.cancelBtn.hide();
13784         
13785         var _this = this;
13786         
13787         (function(){
13788             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13789             _this.list.setWidth(lw);
13790         }).defer(100);
13791         
13792         this.list.on('mouseover', this.onViewOver, this);
13793         this.list.on('mousemove', this.onViewMove, this);
13794         
13795         this.list.on('scroll', this.onViewScroll, this);
13796         
13797         if(!this.tpl){
13798             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13799                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13800         }
13801
13802         this.view = new Roo.View(this.list, this.tpl, {
13803             singleSelect:true,
13804             tickable:true,
13805             parent:this,
13806             store: this.store,
13807             selectedClass: this.selectedClass
13808         });
13809         
13810         //this.view.wrapEl.setDisplayed(false);
13811         this.view.on('click', this.onViewClick, this);
13812         
13813         
13814         
13815         this.store.on('beforeload', this.onBeforeLoad, this);
13816         this.store.on('load', this.onLoad, this);
13817         this.store.on('loadexception', this.onLoadException, this);
13818         
13819         if(this.editable){
13820             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13821                 "up" : function(e){
13822                     this.inKeyMode = true;
13823                     this.selectPrev();
13824                 },
13825
13826                 "down" : function(e){
13827                     this.inKeyMode = true;
13828                     this.selectNext();
13829                 },
13830
13831                 "enter" : function(e){
13832                     if(this.fireEvent("specialkey", this, e)){
13833                         this.onViewClick(false);
13834                     }
13835                     
13836                     return true;
13837                 },
13838
13839                 "esc" : function(e){
13840                     this.onTickableFooterButtonClick(e, false, false);
13841                 },
13842
13843                 "tab" : function(e){
13844                     this.fireEvent("specialkey", this, e);
13845                     
13846                     this.onTickableFooterButtonClick(e, false, false);
13847                     
13848                     return true;
13849                 },
13850
13851                 scope : this,
13852
13853                 doRelay : function(e, fn, key){
13854                     if(this.scope.isExpanded()){
13855                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13856                     }
13857                     return true;
13858                 },
13859
13860                 forceKeyDown: true
13861             });
13862         }
13863         
13864         this.queryDelay = Math.max(this.queryDelay || 10,
13865                 this.mode == 'local' ? 10 : 250);
13866         
13867         
13868         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13869         
13870         if(this.typeAhead){
13871             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13872         }
13873         
13874         if(this.editable !== false){
13875             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13876         }
13877         
13878         this.indicator = this.indicatorEl();
13879         
13880         if(this.indicator){
13881             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13882             this.indicator.hide();
13883         }
13884         
13885     },
13886
13887     onDestroy : function(){
13888         if(this.view){
13889             this.view.setStore(null);
13890             this.view.el.removeAllListeners();
13891             this.view.el.remove();
13892             this.view.purgeListeners();
13893         }
13894         if(this.list){
13895             this.list.dom.innerHTML  = '';
13896         }
13897         
13898         if(this.store){
13899             this.store.un('beforeload', this.onBeforeLoad, this);
13900             this.store.un('load', this.onLoad, this);
13901             this.store.un('loadexception', this.onLoadException, this);
13902         }
13903         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13904     },
13905
13906     // private
13907     fireKey : function(e){
13908         if(e.isNavKeyPress() && !this.list.isVisible()){
13909             this.fireEvent("specialkey", this, e);
13910         }
13911     },
13912
13913     // private
13914     onResize: function(w, h){
13915 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13916 //        
13917 //        if(typeof w != 'number'){
13918 //            // we do not handle it!?!?
13919 //            return;
13920 //        }
13921 //        var tw = this.trigger.getWidth();
13922 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13923 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13924 //        var x = w - tw;
13925 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13926 //            
13927 //        //this.trigger.setStyle('left', x+'px');
13928 //        
13929 //        if(this.list && this.listWidth === undefined){
13930 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13931 //            this.list.setWidth(lw);
13932 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13933 //        }
13934         
13935     
13936         
13937     },
13938
13939     /**
13940      * Allow or prevent the user from directly editing the field text.  If false is passed,
13941      * the user will only be able to select from the items defined in the dropdown list.  This method
13942      * is the runtime equivalent of setting the 'editable' config option at config time.
13943      * @param {Boolean} value True to allow the user to directly edit the field text
13944      */
13945     setEditable : function(value){
13946         if(value == this.editable){
13947             return;
13948         }
13949         this.editable = value;
13950         if(!value){
13951             this.inputEl().dom.setAttribute('readOnly', true);
13952             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13953             this.inputEl().addClass('x-combo-noedit');
13954         }else{
13955             this.inputEl().dom.setAttribute('readOnly', false);
13956             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13957             this.inputEl().removeClass('x-combo-noedit');
13958         }
13959     },
13960
13961     // private
13962     
13963     onBeforeLoad : function(combo,opts){
13964         if(!this.hasFocus){
13965             return;
13966         }
13967          if (!opts.add) {
13968             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13969          }
13970         this.restrictHeight();
13971         this.selectedIndex = -1;
13972     },
13973
13974     // private
13975     onLoad : function(){
13976         
13977         this.hasQuery = false;
13978         
13979         if(!this.hasFocus){
13980             return;
13981         }
13982         
13983         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13984             this.loading.hide();
13985         }
13986         
13987         if(this.store.getCount() > 0){
13988             
13989             this.expand();
13990             this.restrictHeight();
13991             if(this.lastQuery == this.allQuery){
13992                 if(this.editable && !this.tickable){
13993                     this.inputEl().dom.select();
13994                 }
13995                 
13996                 if(
13997                     !this.selectByValue(this.value, true) &&
13998                     this.autoFocus && 
13999                     (
14000                         !this.store.lastOptions ||
14001                         typeof(this.store.lastOptions.add) == 'undefined' || 
14002                         this.store.lastOptions.add != true
14003                     )
14004                 ){
14005                     this.select(0, true);
14006                 }
14007             }else{
14008                 if(this.autoFocus){
14009                     this.selectNext();
14010                 }
14011                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14012                     this.taTask.delay(this.typeAheadDelay);
14013                 }
14014             }
14015         }else{
14016             this.onEmptyResults();
14017         }
14018         
14019         //this.el.focus();
14020     },
14021     // private
14022     onLoadException : function()
14023     {
14024         this.hasQuery = false;
14025         
14026         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14027             this.loading.hide();
14028         }
14029         
14030         if(this.tickable && this.editable){
14031             return;
14032         }
14033         
14034         this.collapse();
14035         // only causes errors at present
14036         //Roo.log(this.store.reader.jsonData);
14037         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14038             // fixme
14039             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14040         //}
14041         
14042         
14043     },
14044     // private
14045     onTypeAhead : function(){
14046         if(this.store.getCount() > 0){
14047             var r = this.store.getAt(0);
14048             var newValue = r.data[this.displayField];
14049             var len = newValue.length;
14050             var selStart = this.getRawValue().length;
14051             
14052             if(selStart != len){
14053                 this.setRawValue(newValue);
14054                 this.selectText(selStart, newValue.length);
14055             }
14056         }
14057     },
14058
14059     // private
14060     onSelect : function(record, index){
14061         
14062         if(this.fireEvent('beforeselect', this, record, index) !== false){
14063         
14064             this.setFromData(index > -1 ? record.data : false);
14065             
14066             this.collapse();
14067             this.fireEvent('select', this, record, index);
14068         }
14069     },
14070
14071     /**
14072      * Returns the currently selected field value or empty string if no value is set.
14073      * @return {String} value The selected value
14074      */
14075     getValue : function()
14076     {
14077         if(Roo.isIOS && this.useNativeIOS){
14078             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14079         }
14080         
14081         if(this.multiple){
14082             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14083         }
14084         
14085         if(this.valueField){
14086             return typeof this.value != 'undefined' ? this.value : '';
14087         }else{
14088             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14089         }
14090     },
14091     
14092     getRawValue : function()
14093     {
14094         if(Roo.isIOS && this.useNativeIOS){
14095             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14096         }
14097         
14098         var v = this.inputEl().getValue();
14099         
14100         return v;
14101     },
14102
14103     /**
14104      * Clears any text/value currently set in the field
14105      */
14106     clearValue : function(){
14107         
14108         if(this.hiddenField){
14109             this.hiddenField.dom.value = '';
14110         }
14111         this.value = '';
14112         this.setRawValue('');
14113         this.lastSelectionText = '';
14114         this.lastData = false;
14115         
14116         var close = this.closeTriggerEl();
14117         
14118         if(close){
14119             close.hide();
14120         }
14121         
14122         this.validate();
14123         
14124     },
14125
14126     /**
14127      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14128      * will be displayed in the field.  If the value does not match the data value of an existing item,
14129      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14130      * Otherwise the field will be blank (although the value will still be set).
14131      * @param {String} value The value to match
14132      */
14133     setValue : function(v)
14134     {
14135         if(Roo.isIOS && this.useNativeIOS){
14136             this.setIOSValue(v);
14137             return;
14138         }
14139         
14140         if(this.multiple){
14141             this.syncValue();
14142             return;
14143         }
14144         
14145         var text = v;
14146         if(this.valueField){
14147             var r = this.findRecord(this.valueField, v);
14148             if(r){
14149                 text = r.data[this.displayField];
14150             }else if(this.valueNotFoundText !== undefined){
14151                 text = this.valueNotFoundText;
14152             }
14153         }
14154         this.lastSelectionText = text;
14155         if(this.hiddenField){
14156             this.hiddenField.dom.value = v;
14157         }
14158         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14159         this.value = v;
14160         
14161         var close = this.closeTriggerEl();
14162         
14163         if(close){
14164             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14165         }
14166         
14167         this.validate();
14168     },
14169     /**
14170      * @property {Object} the last set data for the element
14171      */
14172     
14173     lastData : false,
14174     /**
14175      * Sets the value of the field based on a object which is related to the record format for the store.
14176      * @param {Object} value the value to set as. or false on reset?
14177      */
14178     setFromData : function(o){
14179         
14180         if(this.multiple){
14181             this.addItem(o);
14182             return;
14183         }
14184             
14185         var dv = ''; // display value
14186         var vv = ''; // value value..
14187         this.lastData = o;
14188         if (this.displayField) {
14189             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14190         } else {
14191             // this is an error condition!!!
14192             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14193         }
14194         
14195         if(this.valueField){
14196             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14197         }
14198         
14199         var close = this.closeTriggerEl();
14200         
14201         if(close){
14202             if(dv.length || vv * 1 > 0){
14203                 close.show() ;
14204                 this.blockFocus=true;
14205             } else {
14206                 close.hide();
14207             }             
14208         }
14209         
14210         if(this.hiddenField){
14211             this.hiddenField.dom.value = vv;
14212             
14213             this.lastSelectionText = dv;
14214             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14215             this.value = vv;
14216             return;
14217         }
14218         // no hidden field.. - we store the value in 'value', but still display
14219         // display field!!!!
14220         this.lastSelectionText = dv;
14221         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14222         this.value = vv;
14223         
14224         
14225         
14226     },
14227     // private
14228     reset : function(){
14229         // overridden so that last data is reset..
14230         
14231         if(this.multiple){
14232             this.clearItem();
14233             return;
14234         }
14235         
14236         this.setValue(this.originalValue);
14237         //this.clearInvalid();
14238         this.lastData = false;
14239         if (this.view) {
14240             this.view.clearSelections();
14241         }
14242         
14243         this.validate();
14244     },
14245     // private
14246     findRecord : function(prop, value){
14247         var record;
14248         if(this.store.getCount() > 0){
14249             this.store.each(function(r){
14250                 if(r.data[prop] == value){
14251                     record = r;
14252                     return false;
14253                 }
14254                 return true;
14255             });
14256         }
14257         return record;
14258     },
14259     
14260     getName: function()
14261     {
14262         // returns hidden if it's set..
14263         if (!this.rendered) {return ''};
14264         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14265         
14266     },
14267     // private
14268     onViewMove : function(e, t){
14269         this.inKeyMode = false;
14270     },
14271
14272     // private
14273     onViewOver : function(e, t){
14274         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14275             return;
14276         }
14277         var item = this.view.findItemFromChild(t);
14278         
14279         if(item){
14280             var index = this.view.indexOf(item);
14281             this.select(index, false);
14282         }
14283     },
14284
14285     // private
14286     onViewClick : function(view, doFocus, el, e)
14287     {
14288         var index = this.view.getSelectedIndexes()[0];
14289         
14290         var r = this.store.getAt(index);
14291         
14292         if(this.tickable){
14293             
14294             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14295                 return;
14296             }
14297             
14298             var rm = false;
14299             var _this = this;
14300             
14301             Roo.each(this.tickItems, function(v,k){
14302                 
14303                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14304                     Roo.log(v);
14305                     _this.tickItems.splice(k, 1);
14306                     
14307                     if(typeof(e) == 'undefined' && view == false){
14308                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14309                     }
14310                     
14311                     rm = true;
14312                     return;
14313                 }
14314             });
14315             
14316             if(rm){
14317                 return;
14318             }
14319             
14320             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14321                 this.tickItems.push(r.data);
14322             }
14323             
14324             if(typeof(e) == 'undefined' && view == false){
14325                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14326             }
14327                     
14328             return;
14329         }
14330         
14331         if(r){
14332             this.onSelect(r, index);
14333         }
14334         if(doFocus !== false && !this.blockFocus){
14335             this.inputEl().focus();
14336         }
14337     },
14338
14339     // private
14340     restrictHeight : function(){
14341         //this.innerList.dom.style.height = '';
14342         //var inner = this.innerList.dom;
14343         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14344         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14345         //this.list.beginUpdate();
14346         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14347         this.list.alignTo(this.inputEl(), this.listAlign);
14348         this.list.alignTo(this.inputEl(), this.listAlign);
14349         //this.list.endUpdate();
14350     },
14351
14352     // private
14353     onEmptyResults : function(){
14354         
14355         if(this.tickable && this.editable){
14356             this.hasFocus = false;
14357             this.restrictHeight();
14358             return;
14359         }
14360         
14361         this.collapse();
14362     },
14363
14364     /**
14365      * Returns true if the dropdown list is expanded, else false.
14366      */
14367     isExpanded : function(){
14368         return this.list.isVisible();
14369     },
14370
14371     /**
14372      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14373      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14374      * @param {String} value The data value of the item to select
14375      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14376      * selected item if it is not currently in view (defaults to true)
14377      * @return {Boolean} True if the value matched an item in the list, else false
14378      */
14379     selectByValue : function(v, scrollIntoView){
14380         if(v !== undefined && v !== null){
14381             var r = this.findRecord(this.valueField || this.displayField, v);
14382             if(r){
14383                 this.select(this.store.indexOf(r), scrollIntoView);
14384                 return true;
14385             }
14386         }
14387         return false;
14388     },
14389
14390     /**
14391      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14392      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14393      * @param {Number} index The zero-based index of the list item to select
14394      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14395      * selected item if it is not currently in view (defaults to true)
14396      */
14397     select : function(index, scrollIntoView){
14398         this.selectedIndex = index;
14399         this.view.select(index);
14400         if(scrollIntoView !== false){
14401             var el = this.view.getNode(index);
14402             /*
14403              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14404              */
14405             if(el){
14406                 this.list.scrollChildIntoView(el, false);
14407             }
14408         }
14409     },
14410
14411     // private
14412     selectNext : function(){
14413         var ct = this.store.getCount();
14414         if(ct > 0){
14415             if(this.selectedIndex == -1){
14416                 this.select(0);
14417             }else if(this.selectedIndex < ct-1){
14418                 this.select(this.selectedIndex+1);
14419             }
14420         }
14421     },
14422
14423     // private
14424     selectPrev : function(){
14425         var ct = this.store.getCount();
14426         if(ct > 0){
14427             if(this.selectedIndex == -1){
14428                 this.select(0);
14429             }else if(this.selectedIndex != 0){
14430                 this.select(this.selectedIndex-1);
14431             }
14432         }
14433     },
14434
14435     // private
14436     onKeyUp : function(e){
14437         if(this.editable !== false && !e.isSpecialKey()){
14438             this.lastKey = e.getKey();
14439             this.dqTask.delay(this.queryDelay);
14440         }
14441     },
14442
14443     // private
14444     validateBlur : function(){
14445         return !this.list || !this.list.isVisible();   
14446     },
14447
14448     // private
14449     initQuery : function(){
14450         
14451         var v = this.getRawValue();
14452         
14453         if(this.tickable && this.editable){
14454             v = this.tickableInputEl().getValue();
14455         }
14456         
14457         this.doQuery(v);
14458     },
14459
14460     // private
14461     doForce : function(){
14462         if(this.inputEl().dom.value.length > 0){
14463             this.inputEl().dom.value =
14464                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14465              
14466         }
14467     },
14468
14469     /**
14470      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14471      * query allowing the query action to be canceled if needed.
14472      * @param {String} query The SQL query to execute
14473      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14474      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14475      * saved in the current store (defaults to false)
14476      */
14477     doQuery : function(q, forceAll){
14478         
14479         if(q === undefined || q === null){
14480             q = '';
14481         }
14482         var qe = {
14483             query: q,
14484             forceAll: forceAll,
14485             combo: this,
14486             cancel:false
14487         };
14488         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14489             return false;
14490         }
14491         q = qe.query;
14492         
14493         forceAll = qe.forceAll;
14494         if(forceAll === true || (q.length >= this.minChars)){
14495             
14496             this.hasQuery = true;
14497             
14498             if(this.lastQuery != q || this.alwaysQuery){
14499                 this.lastQuery = q;
14500                 if(this.mode == 'local'){
14501                     this.selectedIndex = -1;
14502                     if(forceAll){
14503                         this.store.clearFilter();
14504                     }else{
14505                         
14506                         if(this.specialFilter){
14507                             this.fireEvent('specialfilter', this);
14508                             this.onLoad();
14509                             return;
14510                         }
14511                         
14512                         this.store.filter(this.displayField, q);
14513                     }
14514                     
14515                     this.store.fireEvent("datachanged", this.store);
14516                     
14517                     this.onLoad();
14518                     
14519                     
14520                 }else{
14521                     
14522                     this.store.baseParams[this.queryParam] = q;
14523                     
14524                     var options = {params : this.getParams(q)};
14525                     
14526                     if(this.loadNext){
14527                         options.add = true;
14528                         options.params.start = this.page * this.pageSize;
14529                     }
14530                     
14531                     this.store.load(options);
14532                     
14533                     /*
14534                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14535                      *  we should expand the list on onLoad
14536                      *  so command out it
14537                      */
14538 //                    this.expand();
14539                 }
14540             }else{
14541                 this.selectedIndex = -1;
14542                 this.onLoad();   
14543             }
14544         }
14545         
14546         this.loadNext = false;
14547     },
14548     
14549     // private
14550     getParams : function(q){
14551         var p = {};
14552         //p[this.queryParam] = q;
14553         
14554         if(this.pageSize){
14555             p.start = 0;
14556             p.limit = this.pageSize;
14557         }
14558         return p;
14559     },
14560
14561     /**
14562      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14563      */
14564     collapse : function(){
14565         if(!this.isExpanded()){
14566             return;
14567         }
14568         
14569         this.list.hide();
14570         
14571         this.hasFocus = false;
14572         
14573         if(this.tickable){
14574             this.okBtn.hide();
14575             this.cancelBtn.hide();
14576             this.trigger.show();
14577             
14578             if(this.editable){
14579                 this.tickableInputEl().dom.value = '';
14580                 this.tickableInputEl().blur();
14581             }
14582             
14583         }
14584         
14585         Roo.get(document).un('mousedown', this.collapseIf, this);
14586         Roo.get(document).un('mousewheel', this.collapseIf, this);
14587         if (!this.editable) {
14588             Roo.get(document).un('keydown', this.listKeyPress, this);
14589         }
14590         this.fireEvent('collapse', this);
14591         
14592         this.validate();
14593     },
14594
14595     // private
14596     collapseIf : function(e){
14597         var in_combo  = e.within(this.el);
14598         var in_list =  e.within(this.list);
14599         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14600         
14601         if (in_combo || in_list || is_list) {
14602             //e.stopPropagation();
14603             return;
14604         }
14605         
14606         if(this.tickable){
14607             this.onTickableFooterButtonClick(e, false, false);
14608         }
14609
14610         this.collapse();
14611         
14612     },
14613
14614     /**
14615      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14616      */
14617     expand : function(){
14618        
14619         if(this.isExpanded() || !this.hasFocus){
14620             return;
14621         }
14622         
14623         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14624         this.list.setWidth(lw);
14625         
14626         Roo.log('expand');
14627         
14628         this.list.show();
14629         
14630         this.restrictHeight();
14631         
14632         if(this.tickable){
14633             
14634             this.tickItems = Roo.apply([], this.item);
14635             
14636             this.okBtn.show();
14637             this.cancelBtn.show();
14638             this.trigger.hide();
14639             
14640             if(this.editable){
14641                 this.tickableInputEl().focus();
14642             }
14643             
14644         }
14645         
14646         Roo.get(document).on('mousedown', this.collapseIf, this);
14647         Roo.get(document).on('mousewheel', this.collapseIf, this);
14648         if (!this.editable) {
14649             Roo.get(document).on('keydown', this.listKeyPress, this);
14650         }
14651         
14652         this.fireEvent('expand', this);
14653     },
14654
14655     // private
14656     // Implements the default empty TriggerField.onTriggerClick function
14657     onTriggerClick : function(e)
14658     {
14659         Roo.log('trigger click');
14660         
14661         if(this.disabled || !this.triggerList){
14662             return;
14663         }
14664         
14665         this.page = 0;
14666         this.loadNext = false;
14667         
14668         if(this.isExpanded()){
14669             this.collapse();
14670             if (!this.blockFocus) {
14671                 this.inputEl().focus();
14672             }
14673             
14674         }else {
14675             this.hasFocus = true;
14676             if(this.triggerAction == 'all') {
14677                 this.doQuery(this.allQuery, true);
14678             } else {
14679                 this.doQuery(this.getRawValue());
14680             }
14681             if (!this.blockFocus) {
14682                 this.inputEl().focus();
14683             }
14684         }
14685     },
14686     
14687     onTickableTriggerClick : function(e)
14688     {
14689         if(this.disabled){
14690             return;
14691         }
14692         
14693         this.page = 0;
14694         this.loadNext = false;
14695         this.hasFocus = true;
14696         
14697         if(this.triggerAction == 'all') {
14698             this.doQuery(this.allQuery, true);
14699         } else {
14700             this.doQuery(this.getRawValue());
14701         }
14702     },
14703     
14704     onSearchFieldClick : function(e)
14705     {
14706         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14707             this.onTickableFooterButtonClick(e, false, false);
14708             return;
14709         }
14710         
14711         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14712             return;
14713         }
14714         
14715         this.page = 0;
14716         this.loadNext = false;
14717         this.hasFocus = true;
14718         
14719         if(this.triggerAction == 'all') {
14720             this.doQuery(this.allQuery, true);
14721         } else {
14722             this.doQuery(this.getRawValue());
14723         }
14724     },
14725     
14726     listKeyPress : function(e)
14727     {
14728         //Roo.log('listkeypress');
14729         // scroll to first matching element based on key pres..
14730         if (e.isSpecialKey()) {
14731             return false;
14732         }
14733         var k = String.fromCharCode(e.getKey()).toUpperCase();
14734         //Roo.log(k);
14735         var match  = false;
14736         var csel = this.view.getSelectedNodes();
14737         var cselitem = false;
14738         if (csel.length) {
14739             var ix = this.view.indexOf(csel[0]);
14740             cselitem  = this.store.getAt(ix);
14741             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14742                 cselitem = false;
14743             }
14744             
14745         }
14746         
14747         this.store.each(function(v) { 
14748             if (cselitem) {
14749                 // start at existing selection.
14750                 if (cselitem.id == v.id) {
14751                     cselitem = false;
14752                 }
14753                 return true;
14754             }
14755                 
14756             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14757                 match = this.store.indexOf(v);
14758                 return false;
14759             }
14760             return true;
14761         }, this);
14762         
14763         if (match === false) {
14764             return true; // no more action?
14765         }
14766         // scroll to?
14767         this.view.select(match);
14768         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14769         sn.scrollIntoView(sn.dom.parentNode, false);
14770     },
14771     
14772     onViewScroll : function(e, t){
14773         
14774         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){
14775             return;
14776         }
14777         
14778         this.hasQuery = true;
14779         
14780         this.loading = this.list.select('.loading', true).first();
14781         
14782         if(this.loading === null){
14783             this.list.createChild({
14784                 tag: 'div',
14785                 cls: 'loading roo-select2-more-results roo-select2-active',
14786                 html: 'Loading more results...'
14787             });
14788             
14789             this.loading = this.list.select('.loading', true).first();
14790             
14791             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14792             
14793             this.loading.hide();
14794         }
14795         
14796         this.loading.show();
14797         
14798         var _combo = this;
14799         
14800         this.page++;
14801         this.loadNext = true;
14802         
14803         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14804         
14805         return;
14806     },
14807     
14808     addItem : function(o)
14809     {   
14810         var dv = ''; // display value
14811         
14812         if (this.displayField) {
14813             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14814         } else {
14815             // this is an error condition!!!
14816             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14817         }
14818         
14819         if(!dv.length){
14820             return;
14821         }
14822         
14823         var choice = this.choices.createChild({
14824             tag: 'li',
14825             cls: 'roo-select2-search-choice',
14826             cn: [
14827                 {
14828                     tag: 'div',
14829                     html: dv
14830                 },
14831                 {
14832                     tag: 'a',
14833                     href: '#',
14834                     cls: 'roo-select2-search-choice-close fa fa-times',
14835                     tabindex: '-1'
14836                 }
14837             ]
14838             
14839         }, this.searchField);
14840         
14841         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14842         
14843         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14844         
14845         this.item.push(o);
14846         
14847         this.lastData = o;
14848         
14849         this.syncValue();
14850         
14851         this.inputEl().dom.value = '';
14852         
14853         this.validate();
14854     },
14855     
14856     onRemoveItem : function(e, _self, o)
14857     {
14858         e.preventDefault();
14859         
14860         this.lastItem = Roo.apply([], this.item);
14861         
14862         var index = this.item.indexOf(o.data) * 1;
14863         
14864         if( index < 0){
14865             Roo.log('not this item?!');
14866             return;
14867         }
14868         
14869         this.item.splice(index, 1);
14870         o.item.remove();
14871         
14872         this.syncValue();
14873         
14874         this.fireEvent('remove', this, e);
14875         
14876         this.validate();
14877         
14878     },
14879     
14880     syncValue : function()
14881     {
14882         if(!this.item.length){
14883             this.clearValue();
14884             return;
14885         }
14886             
14887         var value = [];
14888         var _this = this;
14889         Roo.each(this.item, function(i){
14890             if(_this.valueField){
14891                 value.push(i[_this.valueField]);
14892                 return;
14893             }
14894
14895             value.push(i);
14896         });
14897
14898         this.value = value.join(',');
14899
14900         if(this.hiddenField){
14901             this.hiddenField.dom.value = this.value;
14902         }
14903         
14904         this.store.fireEvent("datachanged", this.store);
14905         
14906         this.validate();
14907     },
14908     
14909     clearItem : function()
14910     {
14911         if(!this.multiple){
14912             return;
14913         }
14914         
14915         this.item = [];
14916         
14917         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14918            c.remove();
14919         });
14920         
14921         this.syncValue();
14922         
14923         this.validate();
14924         
14925         if(this.tickable && !Roo.isTouch){
14926             this.view.refresh();
14927         }
14928     },
14929     
14930     inputEl: function ()
14931     {
14932         if(Roo.isIOS && this.useNativeIOS){
14933             return this.el.select('select.roo-ios-select', true).first();
14934         }
14935         
14936         if(Roo.isTouch && this.mobileTouchView){
14937             return this.el.select('input.form-control',true).first();
14938         }
14939         
14940         if(this.tickable){
14941             return this.searchField;
14942         }
14943         
14944         return this.el.select('input.form-control',true).first();
14945     },
14946     
14947     onTickableFooterButtonClick : function(e, btn, el)
14948     {
14949         e.preventDefault();
14950         
14951         this.lastItem = Roo.apply([], this.item);
14952         
14953         if(btn && btn.name == 'cancel'){
14954             this.tickItems = Roo.apply([], this.item);
14955             this.collapse();
14956             return;
14957         }
14958         
14959         this.clearItem();
14960         
14961         var _this = this;
14962         
14963         Roo.each(this.tickItems, function(o){
14964             _this.addItem(o);
14965         });
14966         
14967         this.collapse();
14968         
14969     },
14970     
14971     validate : function()
14972     {
14973         if(this.getVisibilityEl().hasClass('hidden')){
14974             return true;
14975         }
14976         
14977         var v = this.getRawValue();
14978         
14979         if(this.multiple){
14980             v = this.getValue();
14981         }
14982         
14983         if(this.disabled || this.allowBlank || v.length){
14984             this.markValid();
14985             return true;
14986         }
14987         
14988         this.markInvalid();
14989         return false;
14990     },
14991     
14992     tickableInputEl : function()
14993     {
14994         if(!this.tickable || !this.editable){
14995             return this.inputEl();
14996         }
14997         
14998         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14999     },
15000     
15001     
15002     getAutoCreateTouchView : function()
15003     {
15004         var id = Roo.id();
15005         
15006         var cfg = {
15007             cls: 'form-group' //input-group
15008         };
15009         
15010         var input =  {
15011             tag: 'input',
15012             id : id,
15013             type : this.inputType,
15014             cls : 'form-control x-combo-noedit',
15015             autocomplete: 'new-password',
15016             placeholder : this.placeholder || '',
15017             readonly : true
15018         };
15019         
15020         if (this.name) {
15021             input.name = this.name;
15022         }
15023         
15024         if (this.size) {
15025             input.cls += ' input-' + this.size;
15026         }
15027         
15028         if (this.disabled) {
15029             input.disabled = true;
15030         }
15031         
15032         var inputblock = {
15033             cls : '',
15034             cn : [
15035                 input
15036             ]
15037         };
15038         
15039         if(this.before){
15040             inputblock.cls += ' input-group';
15041             
15042             inputblock.cn.unshift({
15043                 tag :'span',
15044                 cls : 'input-group-addon',
15045                 html : this.before
15046             });
15047         }
15048         
15049         if(this.removable && !this.multiple){
15050             inputblock.cls += ' roo-removable';
15051             
15052             inputblock.cn.push({
15053                 tag: 'button',
15054                 html : 'x',
15055                 cls : 'roo-combo-removable-btn close'
15056             });
15057         }
15058
15059         if(this.hasFeedback && !this.allowBlank){
15060             
15061             inputblock.cls += ' has-feedback';
15062             
15063             inputblock.cn.push({
15064                 tag: 'span',
15065                 cls: 'glyphicon form-control-feedback'
15066             });
15067             
15068         }
15069         
15070         if (this.after) {
15071             
15072             inputblock.cls += (this.before) ? '' : ' input-group';
15073             
15074             inputblock.cn.push({
15075                 tag :'span',
15076                 cls : 'input-group-addon',
15077                 html : this.after
15078             });
15079         }
15080
15081         var box = {
15082             tag: 'div',
15083             cn: [
15084                 {
15085                     tag: 'input',
15086                     type : 'hidden',
15087                     cls: 'form-hidden-field'
15088                 },
15089                 inputblock
15090             ]
15091             
15092         };
15093         
15094         if(this.multiple){
15095             box = {
15096                 tag: 'div',
15097                 cn: [
15098                     {
15099                         tag: 'input',
15100                         type : 'hidden',
15101                         cls: 'form-hidden-field'
15102                     },
15103                     {
15104                         tag: 'ul',
15105                         cls: 'roo-select2-choices',
15106                         cn:[
15107                             {
15108                                 tag: 'li',
15109                                 cls: 'roo-select2-search-field',
15110                                 cn: [
15111
15112                                     inputblock
15113                                 ]
15114                             }
15115                         ]
15116                     }
15117                 ]
15118             }
15119         };
15120         
15121         var combobox = {
15122             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15123             cn: [
15124                 box
15125             ]
15126         };
15127         
15128         if(!this.multiple && this.showToggleBtn){
15129             
15130             var caret = {
15131                         tag: 'span',
15132                         cls: 'caret'
15133             };
15134             
15135             if (this.caret != false) {
15136                 caret = {
15137                      tag: 'i',
15138                      cls: 'fa fa-' + this.caret
15139                 };
15140                 
15141             }
15142             
15143             combobox.cn.push({
15144                 tag :'span',
15145                 cls : 'input-group-addon btn dropdown-toggle',
15146                 cn : [
15147                     caret,
15148                     {
15149                         tag: 'span',
15150                         cls: 'combobox-clear',
15151                         cn  : [
15152                             {
15153                                 tag : 'i',
15154                                 cls: 'icon-remove'
15155                             }
15156                         ]
15157                     }
15158                 ]
15159
15160             })
15161         }
15162         
15163         if(this.multiple){
15164             combobox.cls += ' roo-select2-container-multi';
15165         }
15166         
15167         var align = this.labelAlign || this.parentLabelAlign();
15168         
15169         if (align ==='left' && this.fieldLabel.length) {
15170
15171             cfg.cn = [
15172                 {
15173                    tag : 'i',
15174                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15175                    tooltip : 'This field is required'
15176                 },
15177                 {
15178                     tag: 'label',
15179                     cls : 'control-label',
15180                     html : this.fieldLabel
15181
15182                 },
15183                 {
15184                     cls : '', 
15185                     cn: [
15186                         combobox
15187                     ]
15188                 }
15189             ];
15190             
15191             var labelCfg = cfg.cn[1];
15192             var contentCfg = cfg.cn[2];
15193             
15194
15195             if(this.indicatorpos == 'right'){
15196                 cfg.cn = [
15197                     {
15198                         tag: 'label',
15199                         'for' :  id,
15200                         cls : 'control-label',
15201                         cn : [
15202                             {
15203                                 tag : 'span',
15204                                 html : this.fieldLabel
15205                             },
15206                             {
15207                                 tag : 'i',
15208                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15209                                 tooltip : 'This field is required'
15210                             }
15211                         ]
15212                     },
15213                     {
15214                         cls : "",
15215                         cn: [
15216                             combobox
15217                         ]
15218                     }
15219
15220                 ];
15221                 
15222                 labelCfg = cfg.cn[0];
15223                 contentCfg = cfg.cn[1];
15224             }
15225             
15226            
15227             
15228             if(this.labelWidth > 12){
15229                 labelCfg.style = "width: " + this.labelWidth + 'px';
15230             }
15231             
15232             if(this.labelWidth < 13 && this.labelmd == 0){
15233                 this.labelmd = this.labelWidth;
15234             }
15235             
15236             if(this.labellg > 0){
15237                 labelCfg.cls += ' col-lg-' + this.labellg;
15238                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15239             }
15240             
15241             if(this.labelmd > 0){
15242                 labelCfg.cls += ' col-md-' + this.labelmd;
15243                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15244             }
15245             
15246             if(this.labelsm > 0){
15247                 labelCfg.cls += ' col-sm-' + this.labelsm;
15248                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15249             }
15250             
15251             if(this.labelxs > 0){
15252                 labelCfg.cls += ' col-xs-' + this.labelxs;
15253                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15254             }
15255                 
15256                 
15257         } else if ( this.fieldLabel.length) {
15258             cfg.cn = [
15259                 {
15260                    tag : 'i',
15261                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15262                    tooltip : 'This field is required'
15263                 },
15264                 {
15265                     tag: 'label',
15266                     cls : 'control-label',
15267                     html : this.fieldLabel
15268
15269                 },
15270                 {
15271                     cls : '', 
15272                     cn: [
15273                         combobox
15274                     ]
15275                 }
15276             ];
15277             
15278             if(this.indicatorpos == 'right'){
15279                 cfg.cn = [
15280                     {
15281                         tag: 'label',
15282                         cls : 'control-label',
15283                         html : this.fieldLabel,
15284                         cn : [
15285                             {
15286                                tag : 'i',
15287                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15288                                tooltip : 'This field is required'
15289                             }
15290                         ]
15291                     },
15292                     {
15293                         cls : '', 
15294                         cn: [
15295                             combobox
15296                         ]
15297                     }
15298                 ];
15299             }
15300         } else {
15301             cfg.cn = combobox;    
15302         }
15303         
15304         
15305         var settings = this;
15306         
15307         ['xs','sm','md','lg'].map(function(size){
15308             if (settings[size]) {
15309                 cfg.cls += ' col-' + size + '-' + settings[size];
15310             }
15311         });
15312         
15313         return cfg;
15314     },
15315     
15316     initTouchView : function()
15317     {
15318         this.renderTouchView();
15319         
15320         this.touchViewEl.on('scroll', function(){
15321             this.el.dom.scrollTop = 0;
15322         }, this);
15323         
15324         this.originalValue = this.getValue();
15325         
15326         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15327         
15328         this.inputEl().on("click", this.showTouchView, this);
15329         if (this.triggerEl) {
15330             this.triggerEl.on("click", this.showTouchView, this);
15331         }
15332         
15333         
15334         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15335         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15336         
15337         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15338         
15339         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15340         this.store.on('load', this.onTouchViewLoad, this);
15341         this.store.on('loadexception', this.onTouchViewLoadException, this);
15342         
15343         if(this.hiddenName){
15344             
15345             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15346             
15347             this.hiddenField.dom.value =
15348                 this.hiddenValue !== undefined ? this.hiddenValue :
15349                 this.value !== undefined ? this.value : '';
15350         
15351             this.el.dom.removeAttribute('name');
15352             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15353         }
15354         
15355         if(this.multiple){
15356             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15357             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15358         }
15359         
15360         if(this.removable && !this.multiple){
15361             var close = this.closeTriggerEl();
15362             if(close){
15363                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15364                 close.on('click', this.removeBtnClick, this, close);
15365             }
15366         }
15367         /*
15368          * fix the bug in Safari iOS8
15369          */
15370         this.inputEl().on("focus", function(e){
15371             document.activeElement.blur();
15372         }, this);
15373         
15374         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15375         
15376         return;
15377         
15378         
15379     },
15380     
15381     renderTouchView : function()
15382     {
15383         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15384         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15385         
15386         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15387         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15388         
15389         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15390         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15391         this.touchViewBodyEl.setStyle('overflow', 'auto');
15392         
15393         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15394         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15395         
15396         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15397         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15398         
15399     },
15400     
15401     showTouchView : function()
15402     {
15403         if(this.disabled){
15404             return;
15405         }
15406         
15407         this.touchViewHeaderEl.hide();
15408
15409         if(this.modalTitle.length){
15410             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15411             this.touchViewHeaderEl.show();
15412         }
15413
15414         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15415         this.touchViewEl.show();
15416
15417         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15418         
15419         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15420         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15421
15422         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15423
15424         if(this.modalTitle.length){
15425             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15426         }
15427         
15428         this.touchViewBodyEl.setHeight(bodyHeight);
15429
15430         if(this.animate){
15431             var _this = this;
15432             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15433         }else{
15434             this.touchViewEl.addClass('in');
15435         }
15436         
15437         if(this._touchViewMask){
15438             Roo.get(document.body).addClass("x-body-masked");
15439             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15440             this._touchViewMask.setStyle('z-index', 10000);
15441             this._touchViewMask.addClass('show');
15442         }
15443         
15444         this.doTouchViewQuery();
15445         
15446     },
15447     
15448     hideTouchView : function()
15449     {
15450         this.touchViewEl.removeClass('in');
15451
15452         if(this.animate){
15453             var _this = this;
15454             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15455         }else{
15456             this.touchViewEl.setStyle('display', 'none');
15457         }
15458         
15459         if(this._touchViewMask){
15460             this._touchViewMask.removeClass('show');
15461             Roo.get(document.body).removeClass("x-body-masked");
15462         }
15463     },
15464     
15465     setTouchViewValue : function()
15466     {
15467         if(this.multiple){
15468             this.clearItem();
15469         
15470             var _this = this;
15471
15472             Roo.each(this.tickItems, function(o){
15473                 this.addItem(o);
15474             }, this);
15475         }
15476         
15477         this.hideTouchView();
15478     },
15479     
15480     doTouchViewQuery : function()
15481     {
15482         var qe = {
15483             query: '',
15484             forceAll: true,
15485             combo: this,
15486             cancel:false
15487         };
15488         
15489         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15490             return false;
15491         }
15492         
15493         if(!this.alwaysQuery || this.mode == 'local'){
15494             this.onTouchViewLoad();
15495             return;
15496         }
15497         
15498         this.store.load();
15499     },
15500     
15501     onTouchViewBeforeLoad : function(combo,opts)
15502     {
15503         return;
15504     },
15505
15506     // private
15507     onTouchViewLoad : function()
15508     {
15509         if(this.store.getCount() < 1){
15510             this.onTouchViewEmptyResults();
15511             return;
15512         }
15513         
15514         this.clearTouchView();
15515         
15516         var rawValue = this.getRawValue();
15517         
15518         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15519         
15520         this.tickItems = [];
15521         
15522         this.store.data.each(function(d, rowIndex){
15523             var row = this.touchViewListGroup.createChild(template);
15524             
15525             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15526                 row.addClass(d.data.cls);
15527             }
15528             
15529             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15530                 var cfg = {
15531                     data : d.data,
15532                     html : d.data[this.displayField]
15533                 };
15534                 
15535                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15536                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15537                 }
15538             }
15539             row.removeClass('selected');
15540             if(!this.multiple && this.valueField &&
15541                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15542             {
15543                 // radio buttons..
15544                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15545                 row.addClass('selected');
15546             }
15547             
15548             if(this.multiple && this.valueField &&
15549                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15550             {
15551                 
15552                 // checkboxes...
15553                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15554                 this.tickItems.push(d.data);
15555             }
15556             
15557             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15558             
15559         }, this);
15560         
15561         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15562         
15563         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15564
15565         if(this.modalTitle.length){
15566             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15567         }
15568
15569         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15570         
15571         if(this.mobile_restrict_height && listHeight < bodyHeight){
15572             this.touchViewBodyEl.setHeight(listHeight);
15573         }
15574         
15575         var _this = this;
15576         
15577         if(firstChecked && listHeight > bodyHeight){
15578             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15579         }
15580         
15581     },
15582     
15583     onTouchViewLoadException : function()
15584     {
15585         this.hideTouchView();
15586     },
15587     
15588     onTouchViewEmptyResults : function()
15589     {
15590         this.clearTouchView();
15591         
15592         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15593         
15594         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15595         
15596     },
15597     
15598     clearTouchView : function()
15599     {
15600         this.touchViewListGroup.dom.innerHTML = '';
15601     },
15602     
15603     onTouchViewClick : function(e, el, o)
15604     {
15605         e.preventDefault();
15606         
15607         var row = o.row;
15608         var rowIndex = o.rowIndex;
15609         
15610         var r = this.store.getAt(rowIndex);
15611         
15612         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15613             
15614             if(!this.multiple){
15615                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15616                     c.dom.removeAttribute('checked');
15617                 }, this);
15618
15619                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15620
15621                 this.setFromData(r.data);
15622
15623                 var close = this.closeTriggerEl();
15624
15625                 if(close){
15626                     close.show();
15627                 }
15628
15629                 this.hideTouchView();
15630
15631                 this.fireEvent('select', this, r, rowIndex);
15632
15633                 return;
15634             }
15635
15636             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15637                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15638                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15639                 return;
15640             }
15641
15642             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15643             this.addItem(r.data);
15644             this.tickItems.push(r.data);
15645         }
15646     },
15647     
15648     getAutoCreateNativeIOS : function()
15649     {
15650         var cfg = {
15651             cls: 'form-group' //input-group,
15652         };
15653         
15654         var combobox =  {
15655             tag: 'select',
15656             cls : 'roo-ios-select'
15657         };
15658         
15659         if (this.name) {
15660             combobox.name = this.name;
15661         }
15662         
15663         if (this.disabled) {
15664             combobox.disabled = true;
15665         }
15666         
15667         var settings = this;
15668         
15669         ['xs','sm','md','lg'].map(function(size){
15670             if (settings[size]) {
15671                 cfg.cls += ' col-' + size + '-' + settings[size];
15672             }
15673         });
15674         
15675         cfg.cn = combobox;
15676         
15677         return cfg;
15678         
15679     },
15680     
15681     initIOSView : function()
15682     {
15683         this.store.on('load', this.onIOSViewLoad, this);
15684         
15685         return;
15686     },
15687     
15688     onIOSViewLoad : function()
15689     {
15690         if(this.store.getCount() < 1){
15691             return;
15692         }
15693         
15694         this.clearIOSView();
15695         
15696         if(this.allowBlank) {
15697             
15698             var default_text = '-- SELECT --';
15699             
15700             if(this.placeholder.length){
15701                 default_text = this.placeholder;
15702             }
15703             
15704             if(this.emptyTitle.length){
15705                 default_text += ' - ' + this.emptyTitle + ' -';
15706             }
15707             
15708             var opt = this.inputEl().createChild({
15709                 tag: 'option',
15710                 value : 0,
15711                 html : default_text
15712             });
15713             
15714             var o = {};
15715             o[this.valueField] = 0;
15716             o[this.displayField] = default_text;
15717             
15718             this.ios_options.push({
15719                 data : o,
15720                 el : opt
15721             });
15722             
15723         }
15724         
15725         this.store.data.each(function(d, rowIndex){
15726             
15727             var html = '';
15728             
15729             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15730                 html = d.data[this.displayField];
15731             }
15732             
15733             var value = '';
15734             
15735             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15736                 value = d.data[this.valueField];
15737             }
15738             
15739             var option = {
15740                 tag: 'option',
15741                 value : value,
15742                 html : html
15743             };
15744             
15745             if(this.value == d.data[this.valueField]){
15746                 option['selected'] = true;
15747             }
15748             
15749             var opt = this.inputEl().createChild(option);
15750             
15751             this.ios_options.push({
15752                 data : d.data,
15753                 el : opt
15754             });
15755             
15756         }, this);
15757         
15758         this.inputEl().on('change', function(){
15759            this.fireEvent('select', this);
15760         }, this);
15761         
15762     },
15763     
15764     clearIOSView: function()
15765     {
15766         this.inputEl().dom.innerHTML = '';
15767         
15768         this.ios_options = [];
15769     },
15770     
15771     setIOSValue: function(v)
15772     {
15773         this.value = v;
15774         
15775         if(!this.ios_options){
15776             return;
15777         }
15778         
15779         Roo.each(this.ios_options, function(opts){
15780            
15781            opts.el.dom.removeAttribute('selected');
15782            
15783            if(opts.data[this.valueField] != v){
15784                return;
15785            }
15786            
15787            opts.el.dom.setAttribute('selected', true);
15788            
15789         }, this);
15790     }
15791
15792     /** 
15793     * @cfg {Boolean} grow 
15794     * @hide 
15795     */
15796     /** 
15797     * @cfg {Number} growMin 
15798     * @hide 
15799     */
15800     /** 
15801     * @cfg {Number} growMax 
15802     * @hide 
15803     */
15804     /**
15805      * @hide
15806      * @method autoSize
15807      */
15808 });
15809
15810 Roo.apply(Roo.bootstrap.ComboBox,  {
15811     
15812     header : {
15813         tag: 'div',
15814         cls: 'modal-header',
15815         cn: [
15816             {
15817                 tag: 'h4',
15818                 cls: 'modal-title'
15819             }
15820         ]
15821     },
15822     
15823     body : {
15824         tag: 'div',
15825         cls: 'modal-body',
15826         cn: [
15827             {
15828                 tag: 'ul',
15829                 cls: 'list-group'
15830             }
15831         ]
15832     },
15833     
15834     listItemRadio : {
15835         tag: 'li',
15836         cls: 'list-group-item',
15837         cn: [
15838             {
15839                 tag: 'span',
15840                 cls: 'roo-combobox-list-group-item-value'
15841             },
15842             {
15843                 tag: 'div',
15844                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15845                 cn: [
15846                     {
15847                         tag: 'input',
15848                         type: 'radio'
15849                     },
15850                     {
15851                         tag: 'label'
15852                     }
15853                 ]
15854             }
15855         ]
15856     },
15857     
15858     listItemCheckbox : {
15859         tag: 'li',
15860         cls: 'list-group-item',
15861         cn: [
15862             {
15863                 tag: 'span',
15864                 cls: 'roo-combobox-list-group-item-value'
15865             },
15866             {
15867                 tag: 'div',
15868                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15869                 cn: [
15870                     {
15871                         tag: 'input',
15872                         type: 'checkbox'
15873                     },
15874                     {
15875                         tag: 'label'
15876                     }
15877                 ]
15878             }
15879         ]
15880     },
15881     
15882     emptyResult : {
15883         tag: 'div',
15884         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15885     },
15886     
15887     footer : {
15888         tag: 'div',
15889         cls: 'modal-footer',
15890         cn: [
15891             {
15892                 tag: 'div',
15893                 cls: 'row',
15894                 cn: [
15895                     {
15896                         tag: 'div',
15897                         cls: 'col-xs-6 text-left',
15898                         cn: {
15899                             tag: 'button',
15900                             cls: 'btn btn-danger roo-touch-view-cancel',
15901                             html: 'Cancel'
15902                         }
15903                     },
15904                     {
15905                         tag: 'div',
15906                         cls: 'col-xs-6 text-right',
15907                         cn: {
15908                             tag: 'button',
15909                             cls: 'btn btn-success roo-touch-view-ok',
15910                             html: 'OK'
15911                         }
15912                     }
15913                 ]
15914             }
15915         ]
15916         
15917     }
15918 });
15919
15920 Roo.apply(Roo.bootstrap.ComboBox,  {
15921     
15922     touchViewTemplate : {
15923         tag: 'div',
15924         cls: 'modal fade roo-combobox-touch-view',
15925         cn: [
15926             {
15927                 tag: 'div',
15928                 cls: 'modal-dialog',
15929                 style : 'position:fixed', // we have to fix position....
15930                 cn: [
15931                     {
15932                         tag: 'div',
15933                         cls: 'modal-content',
15934                         cn: [
15935                             Roo.bootstrap.ComboBox.header,
15936                             Roo.bootstrap.ComboBox.body,
15937                             Roo.bootstrap.ComboBox.footer
15938                         ]
15939                     }
15940                 ]
15941             }
15942         ]
15943     }
15944 });/*
15945  * Based on:
15946  * Ext JS Library 1.1.1
15947  * Copyright(c) 2006-2007, Ext JS, LLC.
15948  *
15949  * Originally Released Under LGPL - original licence link has changed is not relivant.
15950  *
15951  * Fork - LGPL
15952  * <script type="text/javascript">
15953  */
15954
15955 /**
15956  * @class Roo.View
15957  * @extends Roo.util.Observable
15958  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15959  * This class also supports single and multi selection modes. <br>
15960  * Create a data model bound view:
15961  <pre><code>
15962  var store = new Roo.data.Store(...);
15963
15964  var view = new Roo.View({
15965     el : "my-element",
15966     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15967  
15968     singleSelect: true,
15969     selectedClass: "ydataview-selected",
15970     store: store
15971  });
15972
15973  // listen for node click?
15974  view.on("click", function(vw, index, node, e){
15975  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15976  });
15977
15978  // load XML data
15979  dataModel.load("foobar.xml");
15980  </code></pre>
15981  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15982  * <br><br>
15983  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15984  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15985  * 
15986  * Note: old style constructor is still suported (container, template, config)
15987  * 
15988  * @constructor
15989  * Create a new View
15990  * @param {Object} config The config object
15991  * 
15992  */
15993 Roo.View = function(config, depreciated_tpl, depreciated_config){
15994     
15995     this.parent = false;
15996     
15997     if (typeof(depreciated_tpl) == 'undefined') {
15998         // new way.. - universal constructor.
15999         Roo.apply(this, config);
16000         this.el  = Roo.get(this.el);
16001     } else {
16002         // old format..
16003         this.el  = Roo.get(config);
16004         this.tpl = depreciated_tpl;
16005         Roo.apply(this, depreciated_config);
16006     }
16007     this.wrapEl  = this.el.wrap().wrap();
16008     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16009     
16010     
16011     if(typeof(this.tpl) == "string"){
16012         this.tpl = new Roo.Template(this.tpl);
16013     } else {
16014         // support xtype ctors..
16015         this.tpl = new Roo.factory(this.tpl, Roo);
16016     }
16017     
16018     
16019     this.tpl.compile();
16020     
16021     /** @private */
16022     this.addEvents({
16023         /**
16024          * @event beforeclick
16025          * Fires before a click is processed. Returns false to cancel the default action.
16026          * @param {Roo.View} this
16027          * @param {Number} index The index of the target node
16028          * @param {HTMLElement} node The target node
16029          * @param {Roo.EventObject} e The raw event object
16030          */
16031             "beforeclick" : true,
16032         /**
16033          * @event click
16034          * Fires when a template node is clicked.
16035          * @param {Roo.View} this
16036          * @param {Number} index The index of the target node
16037          * @param {HTMLElement} node The target node
16038          * @param {Roo.EventObject} e The raw event object
16039          */
16040             "click" : true,
16041         /**
16042          * @event dblclick
16043          * Fires when a template node is double clicked.
16044          * @param {Roo.View} this
16045          * @param {Number} index The index of the target node
16046          * @param {HTMLElement} node The target node
16047          * @param {Roo.EventObject} e The raw event object
16048          */
16049             "dblclick" : true,
16050         /**
16051          * @event contextmenu
16052          * Fires when a template node is right clicked.
16053          * @param {Roo.View} this
16054          * @param {Number} index The index of the target node
16055          * @param {HTMLElement} node The target node
16056          * @param {Roo.EventObject} e The raw event object
16057          */
16058             "contextmenu" : true,
16059         /**
16060          * @event selectionchange
16061          * Fires when the selected nodes change.
16062          * @param {Roo.View} this
16063          * @param {Array} selections Array of the selected nodes
16064          */
16065             "selectionchange" : true,
16066     
16067         /**
16068          * @event beforeselect
16069          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16070          * @param {Roo.View} this
16071          * @param {HTMLElement} node The node to be selected
16072          * @param {Array} selections Array of currently selected nodes
16073          */
16074             "beforeselect" : true,
16075         /**
16076          * @event preparedata
16077          * Fires on every row to render, to allow you to change the data.
16078          * @param {Roo.View} this
16079          * @param {Object} data to be rendered (change this)
16080          */
16081           "preparedata" : true
16082           
16083           
16084         });
16085
16086
16087
16088     this.el.on({
16089         "click": this.onClick,
16090         "dblclick": this.onDblClick,
16091         "contextmenu": this.onContextMenu,
16092         scope:this
16093     });
16094
16095     this.selections = [];
16096     this.nodes = [];
16097     this.cmp = new Roo.CompositeElementLite([]);
16098     if(this.store){
16099         this.store = Roo.factory(this.store, Roo.data);
16100         this.setStore(this.store, true);
16101     }
16102     
16103     if ( this.footer && this.footer.xtype) {
16104            
16105          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16106         
16107         this.footer.dataSource = this.store;
16108         this.footer.container = fctr;
16109         this.footer = Roo.factory(this.footer, Roo);
16110         fctr.insertFirst(this.el);
16111         
16112         // this is a bit insane - as the paging toolbar seems to detach the el..
16113 //        dom.parentNode.parentNode.parentNode
16114          // they get detached?
16115     }
16116     
16117     
16118     Roo.View.superclass.constructor.call(this);
16119     
16120     
16121 };
16122
16123 Roo.extend(Roo.View, Roo.util.Observable, {
16124     
16125      /**
16126      * @cfg {Roo.data.Store} store Data store to load data from.
16127      */
16128     store : false,
16129     
16130     /**
16131      * @cfg {String|Roo.Element} el The container element.
16132      */
16133     el : '',
16134     
16135     /**
16136      * @cfg {String|Roo.Template} tpl The template used by this View 
16137      */
16138     tpl : false,
16139     /**
16140      * @cfg {String} dataName the named area of the template to use as the data area
16141      *                          Works with domtemplates roo-name="name"
16142      */
16143     dataName: false,
16144     /**
16145      * @cfg {String} selectedClass The css class to add to selected nodes
16146      */
16147     selectedClass : "x-view-selected",
16148      /**
16149      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16150      */
16151     emptyText : "",
16152     
16153     /**
16154      * @cfg {String} text to display on mask (default Loading)
16155      */
16156     mask : false,
16157     /**
16158      * @cfg {Boolean} multiSelect Allow multiple selection
16159      */
16160     multiSelect : false,
16161     /**
16162      * @cfg {Boolean} singleSelect Allow single selection
16163      */
16164     singleSelect:  false,
16165     
16166     /**
16167      * @cfg {Boolean} toggleSelect - selecting 
16168      */
16169     toggleSelect : false,
16170     
16171     /**
16172      * @cfg {Boolean} tickable - selecting 
16173      */
16174     tickable : false,
16175     
16176     /**
16177      * Returns the element this view is bound to.
16178      * @return {Roo.Element}
16179      */
16180     getEl : function(){
16181         return this.wrapEl;
16182     },
16183     
16184     
16185
16186     /**
16187      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16188      */
16189     refresh : function(){
16190         //Roo.log('refresh');
16191         var t = this.tpl;
16192         
16193         // if we are using something like 'domtemplate', then
16194         // the what gets used is:
16195         // t.applySubtemplate(NAME, data, wrapping data..)
16196         // the outer template then get' applied with
16197         //     the store 'extra data'
16198         // and the body get's added to the
16199         //      roo-name="data" node?
16200         //      <span class='roo-tpl-{name}'></span> ?????
16201         
16202         
16203         
16204         this.clearSelections();
16205         this.el.update("");
16206         var html = [];
16207         var records = this.store.getRange();
16208         if(records.length < 1) {
16209             
16210             // is this valid??  = should it render a template??
16211             
16212             this.el.update(this.emptyText);
16213             return;
16214         }
16215         var el = this.el;
16216         if (this.dataName) {
16217             this.el.update(t.apply(this.store.meta)); //????
16218             el = this.el.child('.roo-tpl-' + this.dataName);
16219         }
16220         
16221         for(var i = 0, len = records.length; i < len; i++){
16222             var data = this.prepareData(records[i].data, i, records[i]);
16223             this.fireEvent("preparedata", this, data, i, records[i]);
16224             
16225             var d = Roo.apply({}, data);
16226             
16227             if(this.tickable){
16228                 Roo.apply(d, {'roo-id' : Roo.id()});
16229                 
16230                 var _this = this;
16231             
16232                 Roo.each(this.parent.item, function(item){
16233                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16234                         return;
16235                     }
16236                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16237                 });
16238             }
16239             
16240             html[html.length] = Roo.util.Format.trim(
16241                 this.dataName ?
16242                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16243                     t.apply(d)
16244             );
16245         }
16246         
16247         
16248         
16249         el.update(html.join(""));
16250         this.nodes = el.dom.childNodes;
16251         this.updateIndexes(0);
16252     },
16253     
16254
16255     /**
16256      * Function to override to reformat the data that is sent to
16257      * the template for each node.
16258      * DEPRICATED - use the preparedata event handler.
16259      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16260      * a JSON object for an UpdateManager bound view).
16261      */
16262     prepareData : function(data, index, record)
16263     {
16264         this.fireEvent("preparedata", this, data, index, record);
16265         return data;
16266     },
16267
16268     onUpdate : function(ds, record){
16269         // Roo.log('on update');   
16270         this.clearSelections();
16271         var index = this.store.indexOf(record);
16272         var n = this.nodes[index];
16273         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16274         n.parentNode.removeChild(n);
16275         this.updateIndexes(index, index);
16276     },
16277
16278     
16279     
16280 // --------- FIXME     
16281     onAdd : function(ds, records, index)
16282     {
16283         //Roo.log(['on Add', ds, records, index] );        
16284         this.clearSelections();
16285         if(this.nodes.length == 0){
16286             this.refresh();
16287             return;
16288         }
16289         var n = this.nodes[index];
16290         for(var i = 0, len = records.length; i < len; i++){
16291             var d = this.prepareData(records[i].data, i, records[i]);
16292             if(n){
16293                 this.tpl.insertBefore(n, d);
16294             }else{
16295                 
16296                 this.tpl.append(this.el, d);
16297             }
16298         }
16299         this.updateIndexes(index);
16300     },
16301
16302     onRemove : function(ds, record, index){
16303        // Roo.log('onRemove');
16304         this.clearSelections();
16305         var el = this.dataName  ?
16306             this.el.child('.roo-tpl-' + this.dataName) :
16307             this.el; 
16308         
16309         el.dom.removeChild(this.nodes[index]);
16310         this.updateIndexes(index);
16311     },
16312
16313     /**
16314      * Refresh an individual node.
16315      * @param {Number} index
16316      */
16317     refreshNode : function(index){
16318         this.onUpdate(this.store, this.store.getAt(index));
16319     },
16320
16321     updateIndexes : function(startIndex, endIndex){
16322         var ns = this.nodes;
16323         startIndex = startIndex || 0;
16324         endIndex = endIndex || ns.length - 1;
16325         for(var i = startIndex; i <= endIndex; i++){
16326             ns[i].nodeIndex = i;
16327         }
16328     },
16329
16330     /**
16331      * Changes the data store this view uses and refresh the view.
16332      * @param {Store} store
16333      */
16334     setStore : function(store, initial){
16335         if(!initial && this.store){
16336             this.store.un("datachanged", this.refresh);
16337             this.store.un("add", this.onAdd);
16338             this.store.un("remove", this.onRemove);
16339             this.store.un("update", this.onUpdate);
16340             this.store.un("clear", this.refresh);
16341             this.store.un("beforeload", this.onBeforeLoad);
16342             this.store.un("load", this.onLoad);
16343             this.store.un("loadexception", this.onLoad);
16344         }
16345         if(store){
16346           
16347             store.on("datachanged", this.refresh, this);
16348             store.on("add", this.onAdd, this);
16349             store.on("remove", this.onRemove, this);
16350             store.on("update", this.onUpdate, this);
16351             store.on("clear", this.refresh, this);
16352             store.on("beforeload", this.onBeforeLoad, this);
16353             store.on("load", this.onLoad, this);
16354             store.on("loadexception", this.onLoad, this);
16355         }
16356         
16357         if(store){
16358             this.refresh();
16359         }
16360     },
16361     /**
16362      * onbeforeLoad - masks the loading area.
16363      *
16364      */
16365     onBeforeLoad : function(store,opts)
16366     {
16367          //Roo.log('onBeforeLoad');   
16368         if (!opts.add) {
16369             this.el.update("");
16370         }
16371         this.el.mask(this.mask ? this.mask : "Loading" ); 
16372     },
16373     onLoad : function ()
16374     {
16375         this.el.unmask();
16376     },
16377     
16378
16379     /**
16380      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16381      * @param {HTMLElement} node
16382      * @return {HTMLElement} The template node
16383      */
16384     findItemFromChild : function(node){
16385         var el = this.dataName  ?
16386             this.el.child('.roo-tpl-' + this.dataName,true) :
16387             this.el.dom; 
16388         
16389         if(!node || node.parentNode == el){
16390                     return node;
16391             }
16392             var p = node.parentNode;
16393             while(p && p != el){
16394             if(p.parentNode == el){
16395                 return p;
16396             }
16397             p = p.parentNode;
16398         }
16399             return null;
16400     },
16401
16402     /** @ignore */
16403     onClick : function(e){
16404         var item = this.findItemFromChild(e.getTarget());
16405         if(item){
16406             var index = this.indexOf(item);
16407             if(this.onItemClick(item, index, e) !== false){
16408                 this.fireEvent("click", this, index, item, e);
16409             }
16410         }else{
16411             this.clearSelections();
16412         }
16413     },
16414
16415     /** @ignore */
16416     onContextMenu : function(e){
16417         var item = this.findItemFromChild(e.getTarget());
16418         if(item){
16419             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16420         }
16421     },
16422
16423     /** @ignore */
16424     onDblClick : function(e){
16425         var item = this.findItemFromChild(e.getTarget());
16426         if(item){
16427             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16428         }
16429     },
16430
16431     onItemClick : function(item, index, e)
16432     {
16433         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16434             return false;
16435         }
16436         if (this.toggleSelect) {
16437             var m = this.isSelected(item) ? 'unselect' : 'select';
16438             //Roo.log(m);
16439             var _t = this;
16440             _t[m](item, true, false);
16441             return true;
16442         }
16443         if(this.multiSelect || this.singleSelect){
16444             if(this.multiSelect && e.shiftKey && this.lastSelection){
16445                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16446             }else{
16447                 this.select(item, this.multiSelect && e.ctrlKey);
16448                 this.lastSelection = item;
16449             }
16450             
16451             if(!this.tickable){
16452                 e.preventDefault();
16453             }
16454             
16455         }
16456         return true;
16457     },
16458
16459     /**
16460      * Get the number of selected nodes.
16461      * @return {Number}
16462      */
16463     getSelectionCount : function(){
16464         return this.selections.length;
16465     },
16466
16467     /**
16468      * Get the currently selected nodes.
16469      * @return {Array} An array of HTMLElements
16470      */
16471     getSelectedNodes : function(){
16472         return this.selections;
16473     },
16474
16475     /**
16476      * Get the indexes of the selected nodes.
16477      * @return {Array}
16478      */
16479     getSelectedIndexes : function(){
16480         var indexes = [], s = this.selections;
16481         for(var i = 0, len = s.length; i < len; i++){
16482             indexes.push(s[i].nodeIndex);
16483         }
16484         return indexes;
16485     },
16486
16487     /**
16488      * Clear all selections
16489      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16490      */
16491     clearSelections : function(suppressEvent){
16492         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16493             this.cmp.elements = this.selections;
16494             this.cmp.removeClass(this.selectedClass);
16495             this.selections = [];
16496             if(!suppressEvent){
16497                 this.fireEvent("selectionchange", this, this.selections);
16498             }
16499         }
16500     },
16501
16502     /**
16503      * Returns true if the passed node is selected
16504      * @param {HTMLElement/Number} node The node or node index
16505      * @return {Boolean}
16506      */
16507     isSelected : function(node){
16508         var s = this.selections;
16509         if(s.length < 1){
16510             return false;
16511         }
16512         node = this.getNode(node);
16513         return s.indexOf(node) !== -1;
16514     },
16515
16516     /**
16517      * Selects nodes.
16518      * @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
16519      * @param {Boolean} keepExisting (optional) true to keep existing selections
16520      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16521      */
16522     select : function(nodeInfo, keepExisting, suppressEvent){
16523         if(nodeInfo instanceof Array){
16524             if(!keepExisting){
16525                 this.clearSelections(true);
16526             }
16527             for(var i = 0, len = nodeInfo.length; i < len; i++){
16528                 this.select(nodeInfo[i], true, true);
16529             }
16530             return;
16531         } 
16532         var node = this.getNode(nodeInfo);
16533         if(!node || this.isSelected(node)){
16534             return; // already selected.
16535         }
16536         if(!keepExisting){
16537             this.clearSelections(true);
16538         }
16539         
16540         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16541             Roo.fly(node).addClass(this.selectedClass);
16542             this.selections.push(node);
16543             if(!suppressEvent){
16544                 this.fireEvent("selectionchange", this, this.selections);
16545             }
16546         }
16547         
16548         
16549     },
16550       /**
16551      * Unselects nodes.
16552      * @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
16553      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16554      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16555      */
16556     unselect : function(nodeInfo, keepExisting, suppressEvent)
16557     {
16558         if(nodeInfo instanceof Array){
16559             Roo.each(this.selections, function(s) {
16560                 this.unselect(s, nodeInfo);
16561             }, this);
16562             return;
16563         }
16564         var node = this.getNode(nodeInfo);
16565         if(!node || !this.isSelected(node)){
16566             //Roo.log("not selected");
16567             return; // not selected.
16568         }
16569         // fireevent???
16570         var ns = [];
16571         Roo.each(this.selections, function(s) {
16572             if (s == node ) {
16573                 Roo.fly(node).removeClass(this.selectedClass);
16574
16575                 return;
16576             }
16577             ns.push(s);
16578         },this);
16579         
16580         this.selections= ns;
16581         this.fireEvent("selectionchange", this, this.selections);
16582     },
16583
16584     /**
16585      * Gets a template node.
16586      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16587      * @return {HTMLElement} The node or null if it wasn't found
16588      */
16589     getNode : function(nodeInfo){
16590         if(typeof nodeInfo == "string"){
16591             return document.getElementById(nodeInfo);
16592         }else if(typeof nodeInfo == "number"){
16593             return this.nodes[nodeInfo];
16594         }
16595         return nodeInfo;
16596     },
16597
16598     /**
16599      * Gets a range template nodes.
16600      * @param {Number} startIndex
16601      * @param {Number} endIndex
16602      * @return {Array} An array of nodes
16603      */
16604     getNodes : function(start, end){
16605         var ns = this.nodes;
16606         start = start || 0;
16607         end = typeof end == "undefined" ? ns.length - 1 : end;
16608         var nodes = [];
16609         if(start <= end){
16610             for(var i = start; i <= end; i++){
16611                 nodes.push(ns[i]);
16612             }
16613         } else{
16614             for(var i = start; i >= end; i--){
16615                 nodes.push(ns[i]);
16616             }
16617         }
16618         return nodes;
16619     },
16620
16621     /**
16622      * Finds the index of the passed node
16623      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16624      * @return {Number} The index of the node or -1
16625      */
16626     indexOf : function(node){
16627         node = this.getNode(node);
16628         if(typeof node.nodeIndex == "number"){
16629             return node.nodeIndex;
16630         }
16631         var ns = this.nodes;
16632         for(var i = 0, len = ns.length; i < len; i++){
16633             if(ns[i] == node){
16634                 return i;
16635             }
16636         }
16637         return -1;
16638     }
16639 });
16640 /*
16641  * - LGPL
16642  *
16643  * based on jquery fullcalendar
16644  * 
16645  */
16646
16647 Roo.bootstrap = Roo.bootstrap || {};
16648 /**
16649  * @class Roo.bootstrap.Calendar
16650  * @extends Roo.bootstrap.Component
16651  * Bootstrap Calendar class
16652  * @cfg {Boolean} loadMask (true|false) default false
16653  * @cfg {Object} header generate the user specific header of the calendar, default false
16654
16655  * @constructor
16656  * Create a new Container
16657  * @param {Object} config The config object
16658  */
16659
16660
16661
16662 Roo.bootstrap.Calendar = function(config){
16663     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16664      this.addEvents({
16665         /**
16666              * @event select
16667              * Fires when a date is selected
16668              * @param {DatePicker} this
16669              * @param {Date} date The selected date
16670              */
16671         'select': true,
16672         /**
16673              * @event monthchange
16674              * Fires when the displayed month changes 
16675              * @param {DatePicker} this
16676              * @param {Date} date The selected month
16677              */
16678         'monthchange': true,
16679         /**
16680              * @event evententer
16681              * Fires when mouse over an event
16682              * @param {Calendar} this
16683              * @param {event} Event
16684              */
16685         'evententer': true,
16686         /**
16687              * @event eventleave
16688              * Fires when the mouse leaves an
16689              * @param {Calendar} this
16690              * @param {event}
16691              */
16692         'eventleave': true,
16693         /**
16694              * @event eventclick
16695              * Fires when the mouse click an
16696              * @param {Calendar} this
16697              * @param {event}
16698              */
16699         'eventclick': true
16700         
16701     });
16702
16703 };
16704
16705 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16706     
16707      /**
16708      * @cfg {Number} startDay
16709      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16710      */
16711     startDay : 0,
16712     
16713     loadMask : false,
16714     
16715     header : false,
16716       
16717     getAutoCreate : function(){
16718         
16719         
16720         var fc_button = function(name, corner, style, content ) {
16721             return Roo.apply({},{
16722                 tag : 'span',
16723                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16724                          (corner.length ?
16725                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16726                             ''
16727                         ),
16728                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16729                 unselectable: 'on'
16730             });
16731         };
16732         
16733         var header = {};
16734         
16735         if(!this.header){
16736             header = {
16737                 tag : 'table',
16738                 cls : 'fc-header',
16739                 style : 'width:100%',
16740                 cn : [
16741                     {
16742                         tag: 'tr',
16743                         cn : [
16744                             {
16745                                 tag : 'td',
16746                                 cls : 'fc-header-left',
16747                                 cn : [
16748                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16749                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16750                                     { tag: 'span', cls: 'fc-header-space' },
16751                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16752
16753
16754                                 ]
16755                             },
16756
16757                             {
16758                                 tag : 'td',
16759                                 cls : 'fc-header-center',
16760                                 cn : [
16761                                     {
16762                                         tag: 'span',
16763                                         cls: 'fc-header-title',
16764                                         cn : {
16765                                             tag: 'H2',
16766                                             html : 'month / year'
16767                                         }
16768                                     }
16769
16770                                 ]
16771                             },
16772                             {
16773                                 tag : 'td',
16774                                 cls : 'fc-header-right',
16775                                 cn : [
16776                               /*      fc_button('month', 'left', '', 'month' ),
16777                                     fc_button('week', '', '', 'week' ),
16778                                     fc_button('day', 'right', '', 'day' )
16779                                 */    
16780
16781                                 ]
16782                             }
16783
16784                         ]
16785                     }
16786                 ]
16787             };
16788         }
16789         
16790         header = this.header;
16791         
16792        
16793         var cal_heads = function() {
16794             var ret = [];
16795             // fixme - handle this.
16796             
16797             for (var i =0; i < Date.dayNames.length; i++) {
16798                 var d = Date.dayNames[i];
16799                 ret.push({
16800                     tag: 'th',
16801                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16802                     html : d.substring(0,3)
16803                 });
16804                 
16805             }
16806             ret[0].cls += ' fc-first';
16807             ret[6].cls += ' fc-last';
16808             return ret;
16809         };
16810         var cal_cell = function(n) {
16811             return  {
16812                 tag: 'td',
16813                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16814                 cn : [
16815                     {
16816                         cn : [
16817                             {
16818                                 cls: 'fc-day-number',
16819                                 html: 'D'
16820                             },
16821                             {
16822                                 cls: 'fc-day-content',
16823                              
16824                                 cn : [
16825                                      {
16826                                         style: 'position: relative;' // height: 17px;
16827                                     }
16828                                 ]
16829                             }
16830                             
16831                             
16832                         ]
16833                     }
16834                 ]
16835                 
16836             }
16837         };
16838         var cal_rows = function() {
16839             
16840             var ret = [];
16841             for (var r = 0; r < 6; r++) {
16842                 var row= {
16843                     tag : 'tr',
16844                     cls : 'fc-week',
16845                     cn : []
16846                 };
16847                 
16848                 for (var i =0; i < Date.dayNames.length; i++) {
16849                     var d = Date.dayNames[i];
16850                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16851
16852                 }
16853                 row.cn[0].cls+=' fc-first';
16854                 row.cn[0].cn[0].style = 'min-height:90px';
16855                 row.cn[6].cls+=' fc-last';
16856                 ret.push(row);
16857                 
16858             }
16859             ret[0].cls += ' fc-first';
16860             ret[4].cls += ' fc-prev-last';
16861             ret[5].cls += ' fc-last';
16862             return ret;
16863             
16864         };
16865         
16866         var cal_table = {
16867             tag: 'table',
16868             cls: 'fc-border-separate',
16869             style : 'width:100%',
16870             cellspacing  : 0,
16871             cn : [
16872                 { 
16873                     tag: 'thead',
16874                     cn : [
16875                         { 
16876                             tag: 'tr',
16877                             cls : 'fc-first fc-last',
16878                             cn : cal_heads()
16879                         }
16880                     ]
16881                 },
16882                 { 
16883                     tag: 'tbody',
16884                     cn : cal_rows()
16885                 }
16886                   
16887             ]
16888         };
16889          
16890          var cfg = {
16891             cls : 'fc fc-ltr',
16892             cn : [
16893                 header,
16894                 {
16895                     cls : 'fc-content',
16896                     style : "position: relative;",
16897                     cn : [
16898                         {
16899                             cls : 'fc-view fc-view-month fc-grid',
16900                             style : 'position: relative',
16901                             unselectable : 'on',
16902                             cn : [
16903                                 {
16904                                     cls : 'fc-event-container',
16905                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16906                                 },
16907                                 cal_table
16908                             ]
16909                         }
16910                     ]
16911     
16912                 }
16913            ] 
16914             
16915         };
16916         
16917          
16918         
16919         return cfg;
16920     },
16921     
16922     
16923     initEvents : function()
16924     {
16925         if(!this.store){
16926             throw "can not find store for calendar";
16927         }
16928         
16929         var mark = {
16930             tag: "div",
16931             cls:"x-dlg-mask",
16932             style: "text-align:center",
16933             cn: [
16934                 {
16935                     tag: "div",
16936                     style: "background-color:white;width:50%;margin:250 auto",
16937                     cn: [
16938                         {
16939                             tag: "img",
16940                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16941                         },
16942                         {
16943                             tag: "span",
16944                             html: "Loading"
16945                         }
16946                         
16947                     ]
16948                 }
16949             ]
16950         };
16951         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16952         
16953         var size = this.el.select('.fc-content', true).first().getSize();
16954         this.maskEl.setSize(size.width, size.height);
16955         this.maskEl.enableDisplayMode("block");
16956         if(!this.loadMask){
16957             this.maskEl.hide();
16958         }
16959         
16960         this.store = Roo.factory(this.store, Roo.data);
16961         this.store.on('load', this.onLoad, this);
16962         this.store.on('beforeload', this.onBeforeLoad, this);
16963         
16964         this.resize();
16965         
16966         this.cells = this.el.select('.fc-day',true);
16967         //Roo.log(this.cells);
16968         this.textNodes = this.el.query('.fc-day-number');
16969         this.cells.addClassOnOver('fc-state-hover');
16970         
16971         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16972         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16973         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16974         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16975         
16976         this.on('monthchange', this.onMonthChange, this);
16977         
16978         this.update(new Date().clearTime());
16979     },
16980     
16981     resize : function() {
16982         var sz  = this.el.getSize();
16983         
16984         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16985         this.el.select('.fc-day-content div',true).setHeight(34);
16986     },
16987     
16988     
16989     // private
16990     showPrevMonth : function(e){
16991         this.update(this.activeDate.add("mo", -1));
16992     },
16993     showToday : function(e){
16994         this.update(new Date().clearTime());
16995     },
16996     // private
16997     showNextMonth : function(e){
16998         this.update(this.activeDate.add("mo", 1));
16999     },
17000
17001     // private
17002     showPrevYear : function(){
17003         this.update(this.activeDate.add("y", -1));
17004     },
17005
17006     // private
17007     showNextYear : function(){
17008         this.update(this.activeDate.add("y", 1));
17009     },
17010
17011     
17012    // private
17013     update : function(date)
17014     {
17015         var vd = this.activeDate;
17016         this.activeDate = date;
17017 //        if(vd && this.el){
17018 //            var t = date.getTime();
17019 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17020 //                Roo.log('using add remove');
17021 //                
17022 //                this.fireEvent('monthchange', this, date);
17023 //                
17024 //                this.cells.removeClass("fc-state-highlight");
17025 //                this.cells.each(function(c){
17026 //                   if(c.dateValue == t){
17027 //                       c.addClass("fc-state-highlight");
17028 //                       setTimeout(function(){
17029 //                            try{c.dom.firstChild.focus();}catch(e){}
17030 //                       }, 50);
17031 //                       return false;
17032 //                   }
17033 //                   return true;
17034 //                });
17035 //                return;
17036 //            }
17037 //        }
17038         
17039         var days = date.getDaysInMonth();
17040         
17041         var firstOfMonth = date.getFirstDateOfMonth();
17042         var startingPos = firstOfMonth.getDay()-this.startDay;
17043         
17044         if(startingPos < this.startDay){
17045             startingPos += 7;
17046         }
17047         
17048         var pm = date.add(Date.MONTH, -1);
17049         var prevStart = pm.getDaysInMonth()-startingPos;
17050 //        
17051         this.cells = this.el.select('.fc-day',true);
17052         this.textNodes = this.el.query('.fc-day-number');
17053         this.cells.addClassOnOver('fc-state-hover');
17054         
17055         var cells = this.cells.elements;
17056         var textEls = this.textNodes;
17057         
17058         Roo.each(cells, function(cell){
17059             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17060         });
17061         
17062         days += startingPos;
17063
17064         // convert everything to numbers so it's fast
17065         var day = 86400000;
17066         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17067         //Roo.log(d);
17068         //Roo.log(pm);
17069         //Roo.log(prevStart);
17070         
17071         var today = new Date().clearTime().getTime();
17072         var sel = date.clearTime().getTime();
17073         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17074         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17075         var ddMatch = this.disabledDatesRE;
17076         var ddText = this.disabledDatesText;
17077         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17078         var ddaysText = this.disabledDaysText;
17079         var format = this.format;
17080         
17081         var setCellClass = function(cal, cell){
17082             cell.row = 0;
17083             cell.events = [];
17084             cell.more = [];
17085             //Roo.log('set Cell Class');
17086             cell.title = "";
17087             var t = d.getTime();
17088             
17089             //Roo.log(d);
17090             
17091             cell.dateValue = t;
17092             if(t == today){
17093                 cell.className += " fc-today";
17094                 cell.className += " fc-state-highlight";
17095                 cell.title = cal.todayText;
17096             }
17097             if(t == sel){
17098                 // disable highlight in other month..
17099                 //cell.className += " fc-state-highlight";
17100                 
17101             }
17102             // disabling
17103             if(t < min) {
17104                 cell.className = " fc-state-disabled";
17105                 cell.title = cal.minText;
17106                 return;
17107             }
17108             if(t > max) {
17109                 cell.className = " fc-state-disabled";
17110                 cell.title = cal.maxText;
17111                 return;
17112             }
17113             if(ddays){
17114                 if(ddays.indexOf(d.getDay()) != -1){
17115                     cell.title = ddaysText;
17116                     cell.className = " fc-state-disabled";
17117                 }
17118             }
17119             if(ddMatch && format){
17120                 var fvalue = d.dateFormat(format);
17121                 if(ddMatch.test(fvalue)){
17122                     cell.title = ddText.replace("%0", fvalue);
17123                     cell.className = " fc-state-disabled";
17124                 }
17125             }
17126             
17127             if (!cell.initialClassName) {
17128                 cell.initialClassName = cell.dom.className;
17129             }
17130             
17131             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17132         };
17133
17134         var i = 0;
17135         
17136         for(; i < startingPos; i++) {
17137             textEls[i].innerHTML = (++prevStart);
17138             d.setDate(d.getDate()+1);
17139             
17140             cells[i].className = "fc-past fc-other-month";
17141             setCellClass(this, cells[i]);
17142         }
17143         
17144         var intDay = 0;
17145         
17146         for(; i < days; i++){
17147             intDay = i - startingPos + 1;
17148             textEls[i].innerHTML = (intDay);
17149             d.setDate(d.getDate()+1);
17150             
17151             cells[i].className = ''; // "x-date-active";
17152             setCellClass(this, cells[i]);
17153         }
17154         var extraDays = 0;
17155         
17156         for(; i < 42; i++) {
17157             textEls[i].innerHTML = (++extraDays);
17158             d.setDate(d.getDate()+1);
17159             
17160             cells[i].className = "fc-future fc-other-month";
17161             setCellClass(this, cells[i]);
17162         }
17163         
17164         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17165         
17166         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17167         
17168         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17169         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17170         
17171         if(totalRows != 6){
17172             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17173             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17174         }
17175         
17176         this.fireEvent('monthchange', this, date);
17177         
17178         
17179         /*
17180         if(!this.internalRender){
17181             var main = this.el.dom.firstChild;
17182             var w = main.offsetWidth;
17183             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17184             Roo.fly(main).setWidth(w);
17185             this.internalRender = true;
17186             // opera does not respect the auto grow header center column
17187             // then, after it gets a width opera refuses to recalculate
17188             // without a second pass
17189             if(Roo.isOpera && !this.secondPass){
17190                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17191                 this.secondPass = true;
17192                 this.update.defer(10, this, [date]);
17193             }
17194         }
17195         */
17196         
17197     },
17198     
17199     findCell : function(dt) {
17200         dt = dt.clearTime().getTime();
17201         var ret = false;
17202         this.cells.each(function(c){
17203             //Roo.log("check " +c.dateValue + '?=' + dt);
17204             if(c.dateValue == dt){
17205                 ret = c;
17206                 return false;
17207             }
17208             return true;
17209         });
17210         
17211         return ret;
17212     },
17213     
17214     findCells : function(ev) {
17215         var s = ev.start.clone().clearTime().getTime();
17216        // Roo.log(s);
17217         var e= ev.end.clone().clearTime().getTime();
17218        // Roo.log(e);
17219         var ret = [];
17220         this.cells.each(function(c){
17221              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17222             
17223             if(c.dateValue > e){
17224                 return ;
17225             }
17226             if(c.dateValue < s){
17227                 return ;
17228             }
17229             ret.push(c);
17230         });
17231         
17232         return ret;    
17233     },
17234     
17235 //    findBestRow: function(cells)
17236 //    {
17237 //        var ret = 0;
17238 //        
17239 //        for (var i =0 ; i < cells.length;i++) {
17240 //            ret  = Math.max(cells[i].rows || 0,ret);
17241 //        }
17242 //        return ret;
17243 //        
17244 //    },
17245     
17246     
17247     addItem : function(ev)
17248     {
17249         // look for vertical location slot in
17250         var cells = this.findCells(ev);
17251         
17252 //        ev.row = this.findBestRow(cells);
17253         
17254         // work out the location.
17255         
17256         var crow = false;
17257         var rows = [];
17258         for(var i =0; i < cells.length; i++) {
17259             
17260             cells[i].row = cells[0].row;
17261             
17262             if(i == 0){
17263                 cells[i].row = cells[i].row + 1;
17264             }
17265             
17266             if (!crow) {
17267                 crow = {
17268                     start : cells[i],
17269                     end :  cells[i]
17270                 };
17271                 continue;
17272             }
17273             if (crow.start.getY() == cells[i].getY()) {
17274                 // on same row.
17275                 crow.end = cells[i];
17276                 continue;
17277             }
17278             // different row.
17279             rows.push(crow);
17280             crow = {
17281                 start: cells[i],
17282                 end : cells[i]
17283             };
17284             
17285         }
17286         
17287         rows.push(crow);
17288         ev.els = [];
17289         ev.rows = rows;
17290         ev.cells = cells;
17291         
17292         cells[0].events.push(ev);
17293         
17294         this.calevents.push(ev);
17295     },
17296     
17297     clearEvents: function() {
17298         
17299         if(!this.calevents){
17300             return;
17301         }
17302         
17303         Roo.each(this.cells.elements, function(c){
17304             c.row = 0;
17305             c.events = [];
17306             c.more = [];
17307         });
17308         
17309         Roo.each(this.calevents, function(e) {
17310             Roo.each(e.els, function(el) {
17311                 el.un('mouseenter' ,this.onEventEnter, this);
17312                 el.un('mouseleave' ,this.onEventLeave, this);
17313                 el.remove();
17314             },this);
17315         },this);
17316         
17317         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17318             e.remove();
17319         });
17320         
17321     },
17322     
17323     renderEvents: function()
17324     {   
17325         var _this = this;
17326         
17327         this.cells.each(function(c) {
17328             
17329             if(c.row < 5){
17330                 return;
17331             }
17332             
17333             var ev = c.events;
17334             
17335             var r = 4;
17336             if(c.row != c.events.length){
17337                 r = 4 - (4 - (c.row - c.events.length));
17338             }
17339             
17340             c.events = ev.slice(0, r);
17341             c.more = ev.slice(r);
17342             
17343             if(c.more.length && c.more.length == 1){
17344                 c.events.push(c.more.pop());
17345             }
17346             
17347             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17348             
17349         });
17350             
17351         this.cells.each(function(c) {
17352             
17353             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17354             
17355             
17356             for (var e = 0; e < c.events.length; e++){
17357                 var ev = c.events[e];
17358                 var rows = ev.rows;
17359                 
17360                 for(var i = 0; i < rows.length; i++) {
17361                 
17362                     // how many rows should it span..
17363
17364                     var  cfg = {
17365                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17366                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17367
17368                         unselectable : "on",
17369                         cn : [
17370                             {
17371                                 cls: 'fc-event-inner',
17372                                 cn : [
17373     //                                {
17374     //                                  tag:'span',
17375     //                                  cls: 'fc-event-time',
17376     //                                  html : cells.length > 1 ? '' : ev.time
17377     //                                },
17378                                     {
17379                                       tag:'span',
17380                                       cls: 'fc-event-title',
17381                                       html : String.format('{0}', ev.title)
17382                                     }
17383
17384
17385                                 ]
17386                             },
17387                             {
17388                                 cls: 'ui-resizable-handle ui-resizable-e',
17389                                 html : '&nbsp;&nbsp;&nbsp'
17390                             }
17391
17392                         ]
17393                     };
17394
17395                     if (i == 0) {
17396                         cfg.cls += ' fc-event-start';
17397                     }
17398                     if ((i+1) == rows.length) {
17399                         cfg.cls += ' fc-event-end';
17400                     }
17401
17402                     var ctr = _this.el.select('.fc-event-container',true).first();
17403                     var cg = ctr.createChild(cfg);
17404
17405                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17406                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17407
17408                     var r = (c.more.length) ? 1 : 0;
17409                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17410                     cg.setWidth(ebox.right - sbox.x -2);
17411
17412                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17413                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17414                     cg.on('click', _this.onEventClick, _this, ev);
17415
17416                     ev.els.push(cg);
17417                     
17418                 }
17419                 
17420             }
17421             
17422             
17423             if(c.more.length){
17424                 var  cfg = {
17425                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17426                     style : 'position: absolute',
17427                     unselectable : "on",
17428                     cn : [
17429                         {
17430                             cls: 'fc-event-inner',
17431                             cn : [
17432                                 {
17433                                   tag:'span',
17434                                   cls: 'fc-event-title',
17435                                   html : 'More'
17436                                 }
17437
17438
17439                             ]
17440                         },
17441                         {
17442                             cls: 'ui-resizable-handle ui-resizable-e',
17443                             html : '&nbsp;&nbsp;&nbsp'
17444                         }
17445
17446                     ]
17447                 };
17448
17449                 var ctr = _this.el.select('.fc-event-container',true).first();
17450                 var cg = ctr.createChild(cfg);
17451
17452                 var sbox = c.select('.fc-day-content',true).first().getBox();
17453                 var ebox = c.select('.fc-day-content',true).first().getBox();
17454                 //Roo.log(cg);
17455                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17456                 cg.setWidth(ebox.right - sbox.x -2);
17457
17458                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17459                 
17460             }
17461             
17462         });
17463         
17464         
17465         
17466     },
17467     
17468     onEventEnter: function (e, el,event,d) {
17469         this.fireEvent('evententer', this, el, event);
17470     },
17471     
17472     onEventLeave: function (e, el,event,d) {
17473         this.fireEvent('eventleave', this, el, event);
17474     },
17475     
17476     onEventClick: function (e, el,event,d) {
17477         this.fireEvent('eventclick', this, el, event);
17478     },
17479     
17480     onMonthChange: function () {
17481         this.store.load();
17482     },
17483     
17484     onMoreEventClick: function(e, el, more)
17485     {
17486         var _this = this;
17487         
17488         this.calpopover.placement = 'right';
17489         this.calpopover.setTitle('More');
17490         
17491         this.calpopover.setContent('');
17492         
17493         var ctr = this.calpopover.el.select('.popover-content', true).first();
17494         
17495         Roo.each(more, function(m){
17496             var cfg = {
17497                 cls : 'fc-event-hori fc-event-draggable',
17498                 html : m.title
17499             };
17500             var cg = ctr.createChild(cfg);
17501             
17502             cg.on('click', _this.onEventClick, _this, m);
17503         });
17504         
17505         this.calpopover.show(el);
17506         
17507         
17508     },
17509     
17510     onLoad: function () 
17511     {   
17512         this.calevents = [];
17513         var cal = this;
17514         
17515         if(this.store.getCount() > 0){
17516             this.store.data.each(function(d){
17517                cal.addItem({
17518                     id : d.data.id,
17519                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17520                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17521                     time : d.data.start_time,
17522                     title : d.data.title,
17523                     description : d.data.description,
17524                     venue : d.data.venue
17525                 });
17526             });
17527         }
17528         
17529         this.renderEvents();
17530         
17531         if(this.calevents.length && this.loadMask){
17532             this.maskEl.hide();
17533         }
17534     },
17535     
17536     onBeforeLoad: function()
17537     {
17538         this.clearEvents();
17539         if(this.loadMask){
17540             this.maskEl.show();
17541         }
17542     }
17543 });
17544
17545  
17546  /*
17547  * - LGPL
17548  *
17549  * element
17550  * 
17551  */
17552
17553 /**
17554  * @class Roo.bootstrap.Popover
17555  * @extends Roo.bootstrap.Component
17556  * Bootstrap Popover class
17557  * @cfg {String} html contents of the popover   (or false to use children..)
17558  * @cfg {String} title of popover (or false to hide)
17559  * @cfg {String} placement how it is placed
17560  * @cfg {String} trigger click || hover (or false to trigger manually)
17561  * @cfg {String} over what (parent or false to trigger manually.)
17562  * @cfg {Number} delay - delay before showing
17563  
17564  * @constructor
17565  * Create a new Popover
17566  * @param {Object} config The config object
17567  */
17568
17569 Roo.bootstrap.Popover = function(config){
17570     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17571     
17572     this.addEvents({
17573         // raw events
17574          /**
17575          * @event show
17576          * After the popover show
17577          * 
17578          * @param {Roo.bootstrap.Popover} this
17579          */
17580         "show" : true,
17581         /**
17582          * @event hide
17583          * After the popover hide
17584          * 
17585          * @param {Roo.bootstrap.Popover} this
17586          */
17587         "hide" : true
17588     });
17589 };
17590
17591 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17592     
17593     title: 'Fill in a title',
17594     html: false,
17595     
17596     placement : 'right',
17597     trigger : 'hover', // hover
17598     
17599     delay : 0,
17600     
17601     over: 'parent',
17602     
17603     can_build_overlaid : false,
17604     
17605     getChildContainer : function()
17606     {
17607         return this.el.select('.popover-content',true).first();
17608     },
17609     
17610     getAutoCreate : function(){
17611          
17612         var cfg = {
17613            cls : 'popover roo-dynamic',
17614            style: 'display:block',
17615            cn : [
17616                 {
17617                     cls : 'arrow'
17618                 },
17619                 {
17620                     cls : 'popover-inner',
17621                     cn : [
17622                         {
17623                             tag: 'h3',
17624                             cls: 'popover-title',
17625                             html : this.title
17626                         },
17627                         {
17628                             cls : 'popover-content',
17629                             html : this.html
17630                         }
17631                     ]
17632                     
17633                 }
17634            ]
17635         };
17636         
17637         return cfg;
17638     },
17639     setTitle: function(str)
17640     {
17641         this.title = str;
17642         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17643     },
17644     setContent: function(str)
17645     {
17646         this.html = str;
17647         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17648     },
17649     // as it get's added to the bottom of the page.
17650     onRender : function(ct, position)
17651     {
17652         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17653         if(!this.el){
17654             var cfg = Roo.apply({},  this.getAutoCreate());
17655             cfg.id = Roo.id();
17656             
17657             if (this.cls) {
17658                 cfg.cls += ' ' + this.cls;
17659             }
17660             if (this.style) {
17661                 cfg.style = this.style;
17662             }
17663             //Roo.log("adding to ");
17664             this.el = Roo.get(document.body).createChild(cfg, position);
17665 //            Roo.log(this.el);
17666         }
17667         this.initEvents();
17668     },
17669     
17670     initEvents : function()
17671     {
17672         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17673         this.el.enableDisplayMode('block');
17674         this.el.hide();
17675         if (this.over === false) {
17676             return; 
17677         }
17678         if (this.triggers === false) {
17679             return;
17680         }
17681         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17682         var triggers = this.trigger ? this.trigger.split(' ') : [];
17683         Roo.each(triggers, function(trigger) {
17684         
17685             if (trigger == 'click') {
17686                 on_el.on('click', this.toggle, this);
17687             } else if (trigger != 'manual') {
17688                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17689                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17690       
17691                 on_el.on(eventIn  ,this.enter, this);
17692                 on_el.on(eventOut, this.leave, this);
17693             }
17694         }, this);
17695         
17696     },
17697     
17698     
17699     // private
17700     timeout : null,
17701     hoverState : null,
17702     
17703     toggle : function () {
17704         this.hoverState == 'in' ? this.leave() : this.enter();
17705     },
17706     
17707     enter : function () {
17708         
17709         clearTimeout(this.timeout);
17710     
17711         this.hoverState = 'in';
17712     
17713         if (!this.delay || !this.delay.show) {
17714             this.show();
17715             return;
17716         }
17717         var _t = this;
17718         this.timeout = setTimeout(function () {
17719             if (_t.hoverState == 'in') {
17720                 _t.show();
17721             }
17722         }, this.delay.show)
17723     },
17724     
17725     leave : function() {
17726         clearTimeout(this.timeout);
17727     
17728         this.hoverState = 'out';
17729     
17730         if (!this.delay || !this.delay.hide) {
17731             this.hide();
17732             return;
17733         }
17734         var _t = this;
17735         this.timeout = setTimeout(function () {
17736             if (_t.hoverState == 'out') {
17737                 _t.hide();
17738             }
17739         }, this.delay.hide)
17740     },
17741     
17742     show : function (on_el)
17743     {
17744         if (!on_el) {
17745             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17746         }
17747         
17748         // set content.
17749         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17750         if (this.html !== false) {
17751             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17752         }
17753         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17754         if (!this.title.length) {
17755             this.el.select('.popover-title',true).hide();
17756         }
17757         
17758         var placement = typeof this.placement == 'function' ?
17759             this.placement.call(this, this.el, on_el) :
17760             this.placement;
17761             
17762         var autoToken = /\s?auto?\s?/i;
17763         var autoPlace = autoToken.test(placement);
17764         if (autoPlace) {
17765             placement = placement.replace(autoToken, '') || 'top';
17766         }
17767         
17768         //this.el.detach()
17769         //this.el.setXY([0,0]);
17770         this.el.show();
17771         this.el.dom.style.display='block';
17772         this.el.addClass(placement);
17773         
17774         //this.el.appendTo(on_el);
17775         
17776         var p = this.getPosition();
17777         var box = this.el.getBox();
17778         
17779         if (autoPlace) {
17780             // fixme..
17781         }
17782         var align = Roo.bootstrap.Popover.alignment[placement];
17783         
17784 //        Roo.log(align);
17785         this.el.alignTo(on_el, align[0],align[1]);
17786         //var arrow = this.el.select('.arrow',true).first();
17787         //arrow.set(align[2], 
17788         
17789         this.el.addClass('in');
17790         
17791         
17792         if (this.el.hasClass('fade')) {
17793             // fade it?
17794         }
17795         
17796         this.hoverState = 'in';
17797         
17798         this.fireEvent('show', this);
17799         
17800     },
17801     hide : function()
17802     {
17803         this.el.setXY([0,0]);
17804         this.el.removeClass('in');
17805         this.el.hide();
17806         this.hoverState = null;
17807         
17808         this.fireEvent('hide', this);
17809     }
17810     
17811 });
17812
17813 Roo.bootstrap.Popover.alignment = {
17814     'left' : ['r-l', [-10,0], 'right'],
17815     'right' : ['l-r', [10,0], 'left'],
17816     'bottom' : ['t-b', [0,10], 'top'],
17817     'top' : [ 'b-t', [0,-10], 'bottom']
17818 };
17819
17820  /*
17821  * - LGPL
17822  *
17823  * Progress
17824  * 
17825  */
17826
17827 /**
17828  * @class Roo.bootstrap.Progress
17829  * @extends Roo.bootstrap.Component
17830  * Bootstrap Progress class
17831  * @cfg {Boolean} striped striped of the progress bar
17832  * @cfg {Boolean} active animated of the progress bar
17833  * 
17834  * 
17835  * @constructor
17836  * Create a new Progress
17837  * @param {Object} config The config object
17838  */
17839
17840 Roo.bootstrap.Progress = function(config){
17841     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17842 };
17843
17844 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17845     
17846     striped : false,
17847     active: false,
17848     
17849     getAutoCreate : function(){
17850         var cfg = {
17851             tag: 'div',
17852             cls: 'progress'
17853         };
17854         
17855         
17856         if(this.striped){
17857             cfg.cls += ' progress-striped';
17858         }
17859       
17860         if(this.active){
17861             cfg.cls += ' active';
17862         }
17863         
17864         
17865         return cfg;
17866     }
17867    
17868 });
17869
17870  
17871
17872  /*
17873  * - LGPL
17874  *
17875  * ProgressBar
17876  * 
17877  */
17878
17879 /**
17880  * @class Roo.bootstrap.ProgressBar
17881  * @extends Roo.bootstrap.Component
17882  * Bootstrap ProgressBar class
17883  * @cfg {Number} aria_valuenow aria-value now
17884  * @cfg {Number} aria_valuemin aria-value min
17885  * @cfg {Number} aria_valuemax aria-value max
17886  * @cfg {String} label label for the progress bar
17887  * @cfg {String} panel (success | info | warning | danger )
17888  * @cfg {String} role role of the progress bar
17889  * @cfg {String} sr_only text
17890  * 
17891  * 
17892  * @constructor
17893  * Create a new ProgressBar
17894  * @param {Object} config The config object
17895  */
17896
17897 Roo.bootstrap.ProgressBar = function(config){
17898     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17899 };
17900
17901 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17902     
17903     aria_valuenow : 0,
17904     aria_valuemin : 0,
17905     aria_valuemax : 100,
17906     label : false,
17907     panel : false,
17908     role : false,
17909     sr_only: false,
17910     
17911     getAutoCreate : function()
17912     {
17913         
17914         var cfg = {
17915             tag: 'div',
17916             cls: 'progress-bar',
17917             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17918         };
17919         
17920         if(this.sr_only){
17921             cfg.cn = {
17922                 tag: 'span',
17923                 cls: 'sr-only',
17924                 html: this.sr_only
17925             }
17926         }
17927         
17928         if(this.role){
17929             cfg.role = this.role;
17930         }
17931         
17932         if(this.aria_valuenow){
17933             cfg['aria-valuenow'] = this.aria_valuenow;
17934         }
17935         
17936         if(this.aria_valuemin){
17937             cfg['aria-valuemin'] = this.aria_valuemin;
17938         }
17939         
17940         if(this.aria_valuemax){
17941             cfg['aria-valuemax'] = this.aria_valuemax;
17942         }
17943         
17944         if(this.label && !this.sr_only){
17945             cfg.html = this.label;
17946         }
17947         
17948         if(this.panel){
17949             cfg.cls += ' progress-bar-' + this.panel;
17950         }
17951         
17952         return cfg;
17953     },
17954     
17955     update : function(aria_valuenow)
17956     {
17957         this.aria_valuenow = aria_valuenow;
17958         
17959         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17960     }
17961    
17962 });
17963
17964  
17965
17966  /*
17967  * - LGPL
17968  *
17969  * column
17970  * 
17971  */
17972
17973 /**
17974  * @class Roo.bootstrap.TabGroup
17975  * @extends Roo.bootstrap.Column
17976  * Bootstrap Column class
17977  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17978  * @cfg {Boolean} carousel true to make the group behave like a carousel
17979  * @cfg {Boolean} bullets show bullets for the panels
17980  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17981  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17982  * @cfg {Boolean} showarrow (true|false) show arrow default true
17983  * 
17984  * @constructor
17985  * Create a new TabGroup
17986  * @param {Object} config The config object
17987  */
17988
17989 Roo.bootstrap.TabGroup = function(config){
17990     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17991     if (!this.navId) {
17992         this.navId = Roo.id();
17993     }
17994     this.tabs = [];
17995     Roo.bootstrap.TabGroup.register(this);
17996     
17997 };
17998
17999 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18000     
18001     carousel : false,
18002     transition : false,
18003     bullets : 0,
18004     timer : 0,
18005     autoslide : false,
18006     slideFn : false,
18007     slideOnTouch : false,
18008     showarrow : true,
18009     
18010     getAutoCreate : function()
18011     {
18012         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18013         
18014         cfg.cls += ' tab-content';
18015         
18016         if (this.carousel) {
18017             cfg.cls += ' carousel slide';
18018             
18019             cfg.cn = [{
18020                cls : 'carousel-inner',
18021                cn : []
18022             }];
18023         
18024             if(this.bullets  && !Roo.isTouch){
18025                 
18026                 var bullets = {
18027                     cls : 'carousel-bullets',
18028                     cn : []
18029                 };
18030                
18031                 if(this.bullets_cls){
18032                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18033                 }
18034                 
18035                 bullets.cn.push({
18036                     cls : 'clear'
18037                 });
18038                 
18039                 cfg.cn[0].cn.push(bullets);
18040             }
18041             
18042             if(this.showarrow){
18043                 cfg.cn[0].cn.push({
18044                     tag : 'div',
18045                     class : 'carousel-arrow',
18046                     cn : [
18047                         {
18048                             tag : 'div',
18049                             class : 'carousel-prev',
18050                             cn : [
18051                                 {
18052                                     tag : 'i',
18053                                     class : 'fa fa-chevron-left'
18054                                 }
18055                             ]
18056                         },
18057                         {
18058                             tag : 'div',
18059                             class : 'carousel-next',
18060                             cn : [
18061                                 {
18062                                     tag : 'i',
18063                                     class : 'fa fa-chevron-right'
18064                                 }
18065                             ]
18066                         }
18067                     ]
18068                 });
18069             }
18070             
18071         }
18072         
18073         return cfg;
18074     },
18075     
18076     initEvents:  function()
18077     {
18078 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18079 //            this.el.on("touchstart", this.onTouchStart, this);
18080 //        }
18081         
18082         if(this.autoslide){
18083             var _this = this;
18084             
18085             this.slideFn = window.setInterval(function() {
18086                 _this.showPanelNext();
18087             }, this.timer);
18088         }
18089         
18090         if(this.showarrow){
18091             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18092             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18093         }
18094         
18095         
18096     },
18097     
18098 //    onTouchStart : function(e, el, o)
18099 //    {
18100 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18101 //            return;
18102 //        }
18103 //        
18104 //        this.showPanelNext();
18105 //    },
18106     
18107     
18108     getChildContainer : function()
18109     {
18110         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18111     },
18112     
18113     /**
18114     * register a Navigation item
18115     * @param {Roo.bootstrap.NavItem} the navitem to add
18116     */
18117     register : function(item)
18118     {
18119         this.tabs.push( item);
18120         item.navId = this.navId; // not really needed..
18121         this.addBullet();
18122     
18123     },
18124     
18125     getActivePanel : function()
18126     {
18127         var r = false;
18128         Roo.each(this.tabs, function(t) {
18129             if (t.active) {
18130                 r = t;
18131                 return false;
18132             }
18133             return null;
18134         });
18135         return r;
18136         
18137     },
18138     getPanelByName : function(n)
18139     {
18140         var r = false;
18141         Roo.each(this.tabs, function(t) {
18142             if (t.tabId == n) {
18143                 r = t;
18144                 return false;
18145             }
18146             return null;
18147         });
18148         return r;
18149     },
18150     indexOfPanel : function(p)
18151     {
18152         var r = false;
18153         Roo.each(this.tabs, function(t,i) {
18154             if (t.tabId == p.tabId) {
18155                 r = i;
18156                 return false;
18157             }
18158             return null;
18159         });
18160         return r;
18161     },
18162     /**
18163      * show a specific panel
18164      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18165      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18166      */
18167     showPanel : function (pan)
18168     {
18169         if(this.transition || typeof(pan) == 'undefined'){
18170             Roo.log("waiting for the transitionend");
18171             return;
18172         }
18173         
18174         if (typeof(pan) == 'number') {
18175             pan = this.tabs[pan];
18176         }
18177         
18178         if (typeof(pan) == 'string') {
18179             pan = this.getPanelByName(pan);
18180         }
18181         
18182         var cur = this.getActivePanel();
18183         
18184         if(!pan || !cur){
18185             Roo.log('pan or acitve pan is undefined');
18186             return false;
18187         }
18188         
18189         if (pan.tabId == this.getActivePanel().tabId) {
18190             return true;
18191         }
18192         
18193         if (false === cur.fireEvent('beforedeactivate')) {
18194             return false;
18195         }
18196         
18197         if(this.bullets > 0 && !Roo.isTouch){
18198             this.setActiveBullet(this.indexOfPanel(pan));
18199         }
18200         
18201         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18202             
18203             this.transition = true;
18204             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18205             var lr = dir == 'next' ? 'left' : 'right';
18206             pan.el.addClass(dir); // or prev
18207             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18208             cur.el.addClass(lr); // or right
18209             pan.el.addClass(lr);
18210             
18211             var _this = this;
18212             cur.el.on('transitionend', function() {
18213                 Roo.log("trans end?");
18214                 
18215                 pan.el.removeClass([lr,dir]);
18216                 pan.setActive(true);
18217                 
18218                 cur.el.removeClass([lr]);
18219                 cur.setActive(false);
18220                 
18221                 _this.transition = false;
18222                 
18223             }, this, { single:  true } );
18224             
18225             return true;
18226         }
18227         
18228         cur.setActive(false);
18229         pan.setActive(true);
18230         
18231         return true;
18232         
18233     },
18234     showPanelNext : function()
18235     {
18236         var i = this.indexOfPanel(this.getActivePanel());
18237         
18238         if (i >= this.tabs.length - 1 && !this.autoslide) {
18239             return;
18240         }
18241         
18242         if (i >= this.tabs.length - 1 && this.autoslide) {
18243             i = -1;
18244         }
18245         
18246         this.showPanel(this.tabs[i+1]);
18247     },
18248     
18249     showPanelPrev : function()
18250     {
18251         var i = this.indexOfPanel(this.getActivePanel());
18252         
18253         if (i  < 1 && !this.autoslide) {
18254             return;
18255         }
18256         
18257         if (i < 1 && this.autoslide) {
18258             i = this.tabs.length;
18259         }
18260         
18261         this.showPanel(this.tabs[i-1]);
18262     },
18263     
18264     
18265     addBullet: function()
18266     {
18267         if(!this.bullets || Roo.isTouch){
18268             return;
18269         }
18270         var ctr = this.el.select('.carousel-bullets',true).first();
18271         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18272         var bullet = ctr.createChild({
18273             cls : 'bullet bullet-' + i
18274         },ctr.dom.lastChild);
18275         
18276         
18277         var _this = this;
18278         
18279         bullet.on('click', (function(e, el, o, ii, t){
18280
18281             e.preventDefault();
18282
18283             this.showPanel(ii);
18284
18285             if(this.autoslide && this.slideFn){
18286                 clearInterval(this.slideFn);
18287                 this.slideFn = window.setInterval(function() {
18288                     _this.showPanelNext();
18289                 }, this.timer);
18290             }
18291
18292         }).createDelegate(this, [i, bullet], true));
18293                 
18294         
18295     },
18296      
18297     setActiveBullet : function(i)
18298     {
18299         if(Roo.isTouch){
18300             return;
18301         }
18302         
18303         Roo.each(this.el.select('.bullet', true).elements, function(el){
18304             el.removeClass('selected');
18305         });
18306
18307         var bullet = this.el.select('.bullet-' + i, true).first();
18308         
18309         if(!bullet){
18310             return;
18311         }
18312         
18313         bullet.addClass('selected');
18314     }
18315     
18316     
18317   
18318 });
18319
18320  
18321
18322  
18323  
18324 Roo.apply(Roo.bootstrap.TabGroup, {
18325     
18326     groups: {},
18327      /**
18328     * register a Navigation Group
18329     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18330     */
18331     register : function(navgrp)
18332     {
18333         this.groups[navgrp.navId] = navgrp;
18334         
18335     },
18336     /**
18337     * fetch a Navigation Group based on the navigation ID
18338     * if one does not exist , it will get created.
18339     * @param {string} the navgroup to add
18340     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18341     */
18342     get: function(navId) {
18343         if (typeof(this.groups[navId]) == 'undefined') {
18344             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18345         }
18346         return this.groups[navId] ;
18347     }
18348     
18349     
18350     
18351 });
18352
18353  /*
18354  * - LGPL
18355  *
18356  * TabPanel
18357  * 
18358  */
18359
18360 /**
18361  * @class Roo.bootstrap.TabPanel
18362  * @extends Roo.bootstrap.Component
18363  * Bootstrap TabPanel class
18364  * @cfg {Boolean} active panel active
18365  * @cfg {String} html panel content
18366  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18367  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18368  * @cfg {String} href click to link..
18369  * 
18370  * 
18371  * @constructor
18372  * Create a new TabPanel
18373  * @param {Object} config The config object
18374  */
18375
18376 Roo.bootstrap.TabPanel = function(config){
18377     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18378     this.addEvents({
18379         /**
18380              * @event changed
18381              * Fires when the active status changes
18382              * @param {Roo.bootstrap.TabPanel} this
18383              * @param {Boolean} state the new state
18384             
18385          */
18386         'changed': true,
18387         /**
18388              * @event beforedeactivate
18389              * Fires before a tab is de-activated - can be used to do validation on a form.
18390              * @param {Roo.bootstrap.TabPanel} this
18391              * @return {Boolean} false if there is an error
18392             
18393          */
18394         'beforedeactivate': true
18395      });
18396     
18397     this.tabId = this.tabId || Roo.id();
18398   
18399 };
18400
18401 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18402     
18403     active: false,
18404     html: false,
18405     tabId: false,
18406     navId : false,
18407     href : '',
18408     
18409     getAutoCreate : function(){
18410         var cfg = {
18411             tag: 'div',
18412             // item is needed for carousel - not sure if it has any effect otherwise
18413             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18414             html: this.html || ''
18415         };
18416         
18417         if(this.active){
18418             cfg.cls += ' active';
18419         }
18420         
18421         if(this.tabId){
18422             cfg.tabId = this.tabId;
18423         }
18424         
18425         
18426         return cfg;
18427     },
18428     
18429     initEvents:  function()
18430     {
18431         var p = this.parent();
18432         
18433         this.navId = this.navId || p.navId;
18434         
18435         if (typeof(this.navId) != 'undefined') {
18436             // not really needed.. but just in case.. parent should be a NavGroup.
18437             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18438             
18439             tg.register(this);
18440             
18441             var i = tg.tabs.length - 1;
18442             
18443             if(this.active && tg.bullets > 0 && i < tg.bullets){
18444                 tg.setActiveBullet(i);
18445             }
18446         }
18447         
18448         this.el.on('click', this.onClick, this);
18449         
18450         if(Roo.isTouch){
18451             this.el.on("touchstart", this.onTouchStart, this);
18452             this.el.on("touchmove", this.onTouchMove, this);
18453             this.el.on("touchend", this.onTouchEnd, this);
18454         }
18455         
18456     },
18457     
18458     onRender : function(ct, position)
18459     {
18460         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18461     },
18462     
18463     setActive : function(state)
18464     {
18465         Roo.log("panel - set active " + this.tabId + "=" + state);
18466         
18467         this.active = state;
18468         if (!state) {
18469             this.el.removeClass('active');
18470             
18471         } else  if (!this.el.hasClass('active')) {
18472             this.el.addClass('active');
18473         }
18474         
18475         this.fireEvent('changed', this, state);
18476     },
18477     
18478     onClick : function(e)
18479     {
18480         e.preventDefault();
18481         
18482         if(!this.href.length){
18483             return;
18484         }
18485         
18486         window.location.href = this.href;
18487     },
18488     
18489     startX : 0,
18490     startY : 0,
18491     endX : 0,
18492     endY : 0,
18493     swiping : false,
18494     
18495     onTouchStart : function(e)
18496     {
18497         this.swiping = false;
18498         
18499         this.startX = e.browserEvent.touches[0].clientX;
18500         this.startY = e.browserEvent.touches[0].clientY;
18501     },
18502     
18503     onTouchMove : function(e)
18504     {
18505         this.swiping = true;
18506         
18507         this.endX = e.browserEvent.touches[0].clientX;
18508         this.endY = e.browserEvent.touches[0].clientY;
18509     },
18510     
18511     onTouchEnd : function(e)
18512     {
18513         if(!this.swiping){
18514             this.onClick(e);
18515             return;
18516         }
18517         
18518         var tabGroup = this.parent();
18519         
18520         if(this.endX > this.startX){ // swiping right
18521             tabGroup.showPanelPrev();
18522             return;
18523         }
18524         
18525         if(this.startX > this.endX){ // swiping left
18526             tabGroup.showPanelNext();
18527             return;
18528         }
18529     }
18530     
18531     
18532 });
18533  
18534
18535  
18536
18537  /*
18538  * - LGPL
18539  *
18540  * DateField
18541  * 
18542  */
18543
18544 /**
18545  * @class Roo.bootstrap.DateField
18546  * @extends Roo.bootstrap.Input
18547  * Bootstrap DateField class
18548  * @cfg {Number} weekStart default 0
18549  * @cfg {String} viewMode default empty, (months|years)
18550  * @cfg {String} minViewMode default empty, (months|years)
18551  * @cfg {Number} startDate default -Infinity
18552  * @cfg {Number} endDate default Infinity
18553  * @cfg {Boolean} todayHighlight default false
18554  * @cfg {Boolean} todayBtn default false
18555  * @cfg {Boolean} calendarWeeks default false
18556  * @cfg {Object} daysOfWeekDisabled default empty
18557  * @cfg {Boolean} singleMode default false (true | false)
18558  * 
18559  * @cfg {Boolean} keyboardNavigation default true
18560  * @cfg {String} language default en
18561  * 
18562  * @constructor
18563  * Create a new DateField
18564  * @param {Object} config The config object
18565  */
18566
18567 Roo.bootstrap.DateField = function(config){
18568     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18569      this.addEvents({
18570             /**
18571              * @event show
18572              * Fires when this field show.
18573              * @param {Roo.bootstrap.DateField} this
18574              * @param {Mixed} date The date value
18575              */
18576             show : true,
18577             /**
18578              * @event show
18579              * Fires when this field hide.
18580              * @param {Roo.bootstrap.DateField} this
18581              * @param {Mixed} date The date value
18582              */
18583             hide : true,
18584             /**
18585              * @event select
18586              * Fires when select a date.
18587              * @param {Roo.bootstrap.DateField} this
18588              * @param {Mixed} date The date value
18589              */
18590             select : true,
18591             /**
18592              * @event beforeselect
18593              * Fires when before select a date.
18594              * @param {Roo.bootstrap.DateField} this
18595              * @param {Mixed} date The date value
18596              */
18597             beforeselect : true
18598         });
18599 };
18600
18601 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18602     
18603     /**
18604      * @cfg {String} format
18605      * The default date format string which can be overriden for localization support.  The format must be
18606      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18607      */
18608     format : "m/d/y",
18609     /**
18610      * @cfg {String} altFormats
18611      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18612      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18613      */
18614     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18615     
18616     weekStart : 0,
18617     
18618     viewMode : '',
18619     
18620     minViewMode : '',
18621     
18622     todayHighlight : false,
18623     
18624     todayBtn: false,
18625     
18626     language: 'en',
18627     
18628     keyboardNavigation: true,
18629     
18630     calendarWeeks: false,
18631     
18632     startDate: -Infinity,
18633     
18634     endDate: Infinity,
18635     
18636     daysOfWeekDisabled: [],
18637     
18638     _events: [],
18639     
18640     singleMode : false,
18641     
18642     UTCDate: function()
18643     {
18644         return new Date(Date.UTC.apply(Date, arguments));
18645     },
18646     
18647     UTCToday: function()
18648     {
18649         var today = new Date();
18650         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18651     },
18652     
18653     getDate: function() {
18654             var d = this.getUTCDate();
18655             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18656     },
18657     
18658     getUTCDate: function() {
18659             return this.date;
18660     },
18661     
18662     setDate: function(d) {
18663             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18664     },
18665     
18666     setUTCDate: function(d) {
18667             this.date = d;
18668             this.setValue(this.formatDate(this.date));
18669     },
18670         
18671     onRender: function(ct, position)
18672     {
18673         
18674         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18675         
18676         this.language = this.language || 'en';
18677         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18678         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18679         
18680         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18681         this.format = this.format || 'm/d/y';
18682         this.isInline = false;
18683         this.isInput = true;
18684         this.component = this.el.select('.add-on', true).first() || false;
18685         this.component = (this.component && this.component.length === 0) ? false : this.component;
18686         this.hasInput = this.component && this.inputEl().length;
18687         
18688         if (typeof(this.minViewMode === 'string')) {
18689             switch (this.minViewMode) {
18690                 case 'months':
18691                     this.minViewMode = 1;
18692                     break;
18693                 case 'years':
18694                     this.minViewMode = 2;
18695                     break;
18696                 default:
18697                     this.minViewMode = 0;
18698                     break;
18699             }
18700         }
18701         
18702         if (typeof(this.viewMode === 'string')) {
18703             switch (this.viewMode) {
18704                 case 'months':
18705                     this.viewMode = 1;
18706                     break;
18707                 case 'years':
18708                     this.viewMode = 2;
18709                     break;
18710                 default:
18711                     this.viewMode = 0;
18712                     break;
18713             }
18714         }
18715                 
18716         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18717         
18718 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18719         
18720         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18721         
18722         this.picker().on('mousedown', this.onMousedown, this);
18723         this.picker().on('click', this.onClick, this);
18724         
18725         this.picker().addClass('datepicker-dropdown');
18726         
18727         this.startViewMode = this.viewMode;
18728         
18729         if(this.singleMode){
18730             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18731                 v.setVisibilityMode(Roo.Element.DISPLAY);
18732                 v.hide();
18733             });
18734             
18735             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18736                 v.setStyle('width', '189px');
18737             });
18738         }
18739         
18740         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18741             if(!this.calendarWeeks){
18742                 v.remove();
18743                 return;
18744             }
18745             
18746             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18747             v.attr('colspan', function(i, val){
18748                 return parseInt(val) + 1;
18749             });
18750         });
18751                         
18752         
18753         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18754         
18755         this.setStartDate(this.startDate);
18756         this.setEndDate(this.endDate);
18757         
18758         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18759         
18760         this.fillDow();
18761         this.fillMonths();
18762         this.update();
18763         this.showMode();
18764         
18765         if(this.isInline) {
18766             this.showPopup();
18767         }
18768     },
18769     
18770     picker : function()
18771     {
18772         return this.pickerEl;
18773 //        return this.el.select('.datepicker', true).first();
18774     },
18775     
18776     fillDow: function()
18777     {
18778         var dowCnt = this.weekStart;
18779         
18780         var dow = {
18781             tag: 'tr',
18782             cn: [
18783                 
18784             ]
18785         };
18786         
18787         if(this.calendarWeeks){
18788             dow.cn.push({
18789                 tag: 'th',
18790                 cls: 'cw',
18791                 html: '&nbsp;'
18792             })
18793         }
18794         
18795         while (dowCnt < this.weekStart + 7) {
18796             dow.cn.push({
18797                 tag: 'th',
18798                 cls: 'dow',
18799                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18800             });
18801         }
18802         
18803         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18804     },
18805     
18806     fillMonths: function()
18807     {    
18808         var i = 0;
18809         var months = this.picker().select('>.datepicker-months td', true).first();
18810         
18811         months.dom.innerHTML = '';
18812         
18813         while (i < 12) {
18814             var month = {
18815                 tag: 'span',
18816                 cls: 'month',
18817                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18818             };
18819             
18820             months.createChild(month);
18821         }
18822         
18823     },
18824     
18825     update: function()
18826     {
18827         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;
18828         
18829         if (this.date < this.startDate) {
18830             this.viewDate = new Date(this.startDate);
18831         } else if (this.date > this.endDate) {
18832             this.viewDate = new Date(this.endDate);
18833         } else {
18834             this.viewDate = new Date(this.date);
18835         }
18836         
18837         this.fill();
18838     },
18839     
18840     fill: function() 
18841     {
18842         var d = new Date(this.viewDate),
18843                 year = d.getUTCFullYear(),
18844                 month = d.getUTCMonth(),
18845                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18846                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18847                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18848                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18849                 currentDate = this.date && this.date.valueOf(),
18850                 today = this.UTCToday();
18851         
18852         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18853         
18854 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18855         
18856 //        this.picker.select('>tfoot th.today').
18857 //                                              .text(dates[this.language].today)
18858 //                                              .toggle(this.todayBtn !== false);
18859     
18860         this.updateNavArrows();
18861         this.fillMonths();
18862                                                 
18863         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18864         
18865         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18866          
18867         prevMonth.setUTCDate(day);
18868         
18869         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18870         
18871         var nextMonth = new Date(prevMonth);
18872         
18873         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18874         
18875         nextMonth = nextMonth.valueOf();
18876         
18877         var fillMonths = false;
18878         
18879         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18880         
18881         while(prevMonth.valueOf() <= nextMonth) {
18882             var clsName = '';
18883             
18884             if (prevMonth.getUTCDay() === this.weekStart) {
18885                 if(fillMonths){
18886                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18887                 }
18888                     
18889                 fillMonths = {
18890                     tag: 'tr',
18891                     cn: []
18892                 };
18893                 
18894                 if(this.calendarWeeks){
18895                     // ISO 8601: First week contains first thursday.
18896                     // ISO also states week starts on Monday, but we can be more abstract here.
18897                     var
18898                     // Start of current week: based on weekstart/current date
18899                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18900                     // Thursday of this week
18901                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18902                     // First Thursday of year, year from thursday
18903                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18904                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18905                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18906                     
18907                     fillMonths.cn.push({
18908                         tag: 'td',
18909                         cls: 'cw',
18910                         html: calWeek
18911                     });
18912                 }
18913             }
18914             
18915             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18916                 clsName += ' old';
18917             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18918                 clsName += ' new';
18919             }
18920             if (this.todayHighlight &&
18921                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18922                 prevMonth.getUTCMonth() == today.getMonth() &&
18923                 prevMonth.getUTCDate() == today.getDate()) {
18924                 clsName += ' today';
18925             }
18926             
18927             if (currentDate && prevMonth.valueOf() === currentDate) {
18928                 clsName += ' active';
18929             }
18930             
18931             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18932                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18933                     clsName += ' disabled';
18934             }
18935             
18936             fillMonths.cn.push({
18937                 tag: 'td',
18938                 cls: 'day ' + clsName,
18939                 html: prevMonth.getDate()
18940             });
18941             
18942             prevMonth.setDate(prevMonth.getDate()+1);
18943         }
18944           
18945         var currentYear = this.date && this.date.getUTCFullYear();
18946         var currentMonth = this.date && this.date.getUTCMonth();
18947         
18948         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18949         
18950         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18951             v.removeClass('active');
18952             
18953             if(currentYear === year && k === currentMonth){
18954                 v.addClass('active');
18955             }
18956             
18957             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18958                 v.addClass('disabled');
18959             }
18960             
18961         });
18962         
18963         
18964         year = parseInt(year/10, 10) * 10;
18965         
18966         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18967         
18968         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18969         
18970         year -= 1;
18971         for (var i = -1; i < 11; i++) {
18972             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18973                 tag: 'span',
18974                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18975                 html: year
18976             });
18977             
18978             year += 1;
18979         }
18980     },
18981     
18982     showMode: function(dir) 
18983     {
18984         if (dir) {
18985             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18986         }
18987         
18988         Roo.each(this.picker().select('>div',true).elements, function(v){
18989             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18990             v.hide();
18991         });
18992         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18993     },
18994     
18995     place: function()
18996     {
18997         if(this.isInline) {
18998             return;
18999         }
19000         
19001         this.picker().removeClass(['bottom', 'top']);
19002         
19003         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19004             /*
19005              * place to the top of element!
19006              *
19007              */
19008             
19009             this.picker().addClass('top');
19010             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19011             
19012             return;
19013         }
19014         
19015         this.picker().addClass('bottom');
19016         
19017         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19018     },
19019     
19020     parseDate : function(value)
19021     {
19022         if(!value || value instanceof Date){
19023             return value;
19024         }
19025         var v = Date.parseDate(value, this.format);
19026         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19027             v = Date.parseDate(value, 'Y-m-d');
19028         }
19029         if(!v && this.altFormats){
19030             if(!this.altFormatsArray){
19031                 this.altFormatsArray = this.altFormats.split("|");
19032             }
19033             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19034                 v = Date.parseDate(value, this.altFormatsArray[i]);
19035             }
19036         }
19037         return v;
19038     },
19039     
19040     formatDate : function(date, fmt)
19041     {   
19042         return (!date || !(date instanceof Date)) ?
19043         date : date.dateFormat(fmt || this.format);
19044     },
19045     
19046     onFocus : function()
19047     {
19048         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19049         this.showPopup();
19050     },
19051     
19052     onBlur : function()
19053     {
19054         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19055         
19056         var d = this.inputEl().getValue();
19057         
19058         this.setValue(d);
19059                 
19060         this.hidePopup();
19061     },
19062     
19063     showPopup : function()
19064     {
19065         this.picker().show();
19066         this.update();
19067         this.place();
19068         
19069         this.fireEvent('showpopup', this, this.date);
19070     },
19071     
19072     hidePopup : function()
19073     {
19074         if(this.isInline) {
19075             return;
19076         }
19077         this.picker().hide();
19078         this.viewMode = this.startViewMode;
19079         this.showMode();
19080         
19081         this.fireEvent('hidepopup', this, this.date);
19082         
19083     },
19084     
19085     onMousedown: function(e)
19086     {
19087         e.stopPropagation();
19088         e.preventDefault();
19089     },
19090     
19091     keyup: function(e)
19092     {
19093         Roo.bootstrap.DateField.superclass.keyup.call(this);
19094         this.update();
19095     },
19096
19097     setValue: function(v)
19098     {
19099         if(this.fireEvent('beforeselect', this, v) !== false){
19100             var d = new Date(this.parseDate(v) ).clearTime();
19101         
19102             if(isNaN(d.getTime())){
19103                 this.date = this.viewDate = '';
19104                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19105                 return;
19106             }
19107
19108             v = this.formatDate(d);
19109
19110             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19111
19112             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19113
19114             this.update();
19115
19116             this.fireEvent('select', this, this.date);
19117         }
19118     },
19119     
19120     getValue: function()
19121     {
19122         return this.formatDate(this.date);
19123     },
19124     
19125     fireKey: function(e)
19126     {
19127         if (!this.picker().isVisible()){
19128             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19129                 this.showPopup();
19130             }
19131             return;
19132         }
19133         
19134         var dateChanged = false,
19135         dir, day, month,
19136         newDate, newViewDate;
19137         
19138         switch(e.keyCode){
19139             case 27: // escape
19140                 this.hidePopup();
19141                 e.preventDefault();
19142                 break;
19143             case 37: // left
19144             case 39: // right
19145                 if (!this.keyboardNavigation) {
19146                     break;
19147                 }
19148                 dir = e.keyCode == 37 ? -1 : 1;
19149                 
19150                 if (e.ctrlKey){
19151                     newDate = this.moveYear(this.date, dir);
19152                     newViewDate = this.moveYear(this.viewDate, dir);
19153                 } else if (e.shiftKey){
19154                     newDate = this.moveMonth(this.date, dir);
19155                     newViewDate = this.moveMonth(this.viewDate, dir);
19156                 } else {
19157                     newDate = new Date(this.date);
19158                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19159                     newViewDate = new Date(this.viewDate);
19160                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19161                 }
19162                 if (this.dateWithinRange(newDate)){
19163                     this.date = newDate;
19164                     this.viewDate = newViewDate;
19165                     this.setValue(this.formatDate(this.date));
19166 //                    this.update();
19167                     e.preventDefault();
19168                     dateChanged = true;
19169                 }
19170                 break;
19171             case 38: // up
19172             case 40: // down
19173                 if (!this.keyboardNavigation) {
19174                     break;
19175                 }
19176                 dir = e.keyCode == 38 ? -1 : 1;
19177                 if (e.ctrlKey){
19178                     newDate = this.moveYear(this.date, dir);
19179                     newViewDate = this.moveYear(this.viewDate, dir);
19180                 } else if (e.shiftKey){
19181                     newDate = this.moveMonth(this.date, dir);
19182                     newViewDate = this.moveMonth(this.viewDate, dir);
19183                 } else {
19184                     newDate = new Date(this.date);
19185                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19186                     newViewDate = new Date(this.viewDate);
19187                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19188                 }
19189                 if (this.dateWithinRange(newDate)){
19190                     this.date = newDate;
19191                     this.viewDate = newViewDate;
19192                     this.setValue(this.formatDate(this.date));
19193 //                    this.update();
19194                     e.preventDefault();
19195                     dateChanged = true;
19196                 }
19197                 break;
19198             case 13: // enter
19199                 this.setValue(this.formatDate(this.date));
19200                 this.hidePopup();
19201                 e.preventDefault();
19202                 break;
19203             case 9: // tab
19204                 this.setValue(this.formatDate(this.date));
19205                 this.hidePopup();
19206                 break;
19207             case 16: // shift
19208             case 17: // ctrl
19209             case 18: // alt
19210                 break;
19211             default :
19212                 this.hidePopup();
19213                 
19214         }
19215     },
19216     
19217     
19218     onClick: function(e) 
19219     {
19220         e.stopPropagation();
19221         e.preventDefault();
19222         
19223         var target = e.getTarget();
19224         
19225         if(target.nodeName.toLowerCase() === 'i'){
19226             target = Roo.get(target).dom.parentNode;
19227         }
19228         
19229         var nodeName = target.nodeName;
19230         var className = target.className;
19231         var html = target.innerHTML;
19232         //Roo.log(nodeName);
19233         
19234         switch(nodeName.toLowerCase()) {
19235             case 'th':
19236                 switch(className) {
19237                     case 'switch':
19238                         this.showMode(1);
19239                         break;
19240                     case 'prev':
19241                     case 'next':
19242                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19243                         switch(this.viewMode){
19244                                 case 0:
19245                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19246                                         break;
19247                                 case 1:
19248                                 case 2:
19249                                         this.viewDate = this.moveYear(this.viewDate, dir);
19250                                         break;
19251                         }
19252                         this.fill();
19253                         break;
19254                     case 'today':
19255                         var date = new Date();
19256                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19257 //                        this.fill()
19258                         this.setValue(this.formatDate(this.date));
19259                         
19260                         this.hidePopup();
19261                         break;
19262                 }
19263                 break;
19264             case 'span':
19265                 if (className.indexOf('disabled') < 0) {
19266                     this.viewDate.setUTCDate(1);
19267                     if (className.indexOf('month') > -1) {
19268                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19269                     } else {
19270                         var year = parseInt(html, 10) || 0;
19271                         this.viewDate.setUTCFullYear(year);
19272                         
19273                     }
19274                     
19275                     if(this.singleMode){
19276                         this.setValue(this.formatDate(this.viewDate));
19277                         this.hidePopup();
19278                         return;
19279                     }
19280                     
19281                     this.showMode(-1);
19282                     this.fill();
19283                 }
19284                 break;
19285                 
19286             case 'td':
19287                 //Roo.log(className);
19288                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19289                     var day = parseInt(html, 10) || 1;
19290                     var year = this.viewDate.getUTCFullYear(),
19291                         month = this.viewDate.getUTCMonth();
19292
19293                     if (className.indexOf('old') > -1) {
19294                         if(month === 0 ){
19295                             month = 11;
19296                             year -= 1;
19297                         }else{
19298                             month -= 1;
19299                         }
19300                     } else if (className.indexOf('new') > -1) {
19301                         if (month == 11) {
19302                             month = 0;
19303                             year += 1;
19304                         } else {
19305                             month += 1;
19306                         }
19307                     }
19308                     //Roo.log([year,month,day]);
19309                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19310                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19311 //                    this.fill();
19312                     //Roo.log(this.formatDate(this.date));
19313                     this.setValue(this.formatDate(this.date));
19314                     this.hidePopup();
19315                 }
19316                 break;
19317         }
19318     },
19319     
19320     setStartDate: function(startDate)
19321     {
19322         this.startDate = startDate || -Infinity;
19323         if (this.startDate !== -Infinity) {
19324             this.startDate = this.parseDate(this.startDate);
19325         }
19326         this.update();
19327         this.updateNavArrows();
19328     },
19329
19330     setEndDate: function(endDate)
19331     {
19332         this.endDate = endDate || Infinity;
19333         if (this.endDate !== Infinity) {
19334             this.endDate = this.parseDate(this.endDate);
19335         }
19336         this.update();
19337         this.updateNavArrows();
19338     },
19339     
19340     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19341     {
19342         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19343         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19344             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19345         }
19346         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19347             return parseInt(d, 10);
19348         });
19349         this.update();
19350         this.updateNavArrows();
19351     },
19352     
19353     updateNavArrows: function() 
19354     {
19355         if(this.singleMode){
19356             return;
19357         }
19358         
19359         var d = new Date(this.viewDate),
19360         year = d.getUTCFullYear(),
19361         month = d.getUTCMonth();
19362         
19363         Roo.each(this.picker().select('.prev', true).elements, function(v){
19364             v.show();
19365             switch (this.viewMode) {
19366                 case 0:
19367
19368                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19369                         v.hide();
19370                     }
19371                     break;
19372                 case 1:
19373                 case 2:
19374                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19375                         v.hide();
19376                     }
19377                     break;
19378             }
19379         });
19380         
19381         Roo.each(this.picker().select('.next', true).elements, function(v){
19382             v.show();
19383             switch (this.viewMode) {
19384                 case 0:
19385
19386                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19387                         v.hide();
19388                     }
19389                     break;
19390                 case 1:
19391                 case 2:
19392                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19393                         v.hide();
19394                     }
19395                     break;
19396             }
19397         })
19398     },
19399     
19400     moveMonth: function(date, dir)
19401     {
19402         if (!dir) {
19403             return date;
19404         }
19405         var new_date = new Date(date.valueOf()),
19406         day = new_date.getUTCDate(),
19407         month = new_date.getUTCMonth(),
19408         mag = Math.abs(dir),
19409         new_month, test;
19410         dir = dir > 0 ? 1 : -1;
19411         if (mag == 1){
19412             test = dir == -1
19413             // If going back one month, make sure month is not current month
19414             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19415             ? function(){
19416                 return new_date.getUTCMonth() == month;
19417             }
19418             // If going forward one month, make sure month is as expected
19419             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19420             : function(){
19421                 return new_date.getUTCMonth() != new_month;
19422             };
19423             new_month = month + dir;
19424             new_date.setUTCMonth(new_month);
19425             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19426             if (new_month < 0 || new_month > 11) {
19427                 new_month = (new_month + 12) % 12;
19428             }
19429         } else {
19430             // For magnitudes >1, move one month at a time...
19431             for (var i=0; i<mag; i++) {
19432                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19433                 new_date = this.moveMonth(new_date, dir);
19434             }
19435             // ...then reset the day, keeping it in the new month
19436             new_month = new_date.getUTCMonth();
19437             new_date.setUTCDate(day);
19438             test = function(){
19439                 return new_month != new_date.getUTCMonth();
19440             };
19441         }
19442         // Common date-resetting loop -- if date is beyond end of month, make it
19443         // end of month
19444         while (test()){
19445             new_date.setUTCDate(--day);
19446             new_date.setUTCMonth(new_month);
19447         }
19448         return new_date;
19449     },
19450
19451     moveYear: function(date, dir)
19452     {
19453         return this.moveMonth(date, dir*12);
19454     },
19455
19456     dateWithinRange: function(date)
19457     {
19458         return date >= this.startDate && date <= this.endDate;
19459     },
19460
19461     
19462     remove: function() 
19463     {
19464         this.picker().remove();
19465     },
19466     
19467     validateValue : function(value)
19468     {
19469         if(this.getVisibilityEl().hasClass('hidden')){
19470             return true;
19471         }
19472         
19473         if(value.length < 1)  {
19474             if(this.allowBlank){
19475                 return true;
19476             }
19477             return false;
19478         }
19479         
19480         if(value.length < this.minLength){
19481             return false;
19482         }
19483         if(value.length > this.maxLength){
19484             return false;
19485         }
19486         if(this.vtype){
19487             var vt = Roo.form.VTypes;
19488             if(!vt[this.vtype](value, this)){
19489                 return false;
19490             }
19491         }
19492         if(typeof this.validator == "function"){
19493             var msg = this.validator(value);
19494             if(msg !== true){
19495                 return false;
19496             }
19497         }
19498         
19499         if(this.regex && !this.regex.test(value)){
19500             return false;
19501         }
19502         
19503         if(typeof(this.parseDate(value)) == 'undefined'){
19504             return false;
19505         }
19506         
19507         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19508             return false;
19509         }      
19510         
19511         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19512             return false;
19513         } 
19514         
19515         
19516         return true;
19517     },
19518     
19519     reset : function()
19520     {
19521         this.date = this.viewDate = '';
19522         
19523         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19524     }
19525    
19526 });
19527
19528 Roo.apply(Roo.bootstrap.DateField,  {
19529     
19530     head : {
19531         tag: 'thead',
19532         cn: [
19533         {
19534             tag: 'tr',
19535             cn: [
19536             {
19537                 tag: 'th',
19538                 cls: 'prev',
19539                 html: '<i class="fa fa-arrow-left"/>'
19540             },
19541             {
19542                 tag: 'th',
19543                 cls: 'switch',
19544                 colspan: '5'
19545             },
19546             {
19547                 tag: 'th',
19548                 cls: 'next',
19549                 html: '<i class="fa fa-arrow-right"/>'
19550             }
19551
19552             ]
19553         }
19554         ]
19555     },
19556     
19557     content : {
19558         tag: 'tbody',
19559         cn: [
19560         {
19561             tag: 'tr',
19562             cn: [
19563             {
19564                 tag: 'td',
19565                 colspan: '7'
19566             }
19567             ]
19568         }
19569         ]
19570     },
19571     
19572     footer : {
19573         tag: 'tfoot',
19574         cn: [
19575         {
19576             tag: 'tr',
19577             cn: [
19578             {
19579                 tag: 'th',
19580                 colspan: '7',
19581                 cls: 'today'
19582             }
19583                     
19584             ]
19585         }
19586         ]
19587     },
19588     
19589     dates:{
19590         en: {
19591             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19592             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19593             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19594             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19595             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19596             today: "Today"
19597         }
19598     },
19599     
19600     modes: [
19601     {
19602         clsName: 'days',
19603         navFnc: 'Month',
19604         navStep: 1
19605     },
19606     {
19607         clsName: 'months',
19608         navFnc: 'FullYear',
19609         navStep: 1
19610     },
19611     {
19612         clsName: 'years',
19613         navFnc: 'FullYear',
19614         navStep: 10
19615     }]
19616 });
19617
19618 Roo.apply(Roo.bootstrap.DateField,  {
19619   
19620     template : {
19621         tag: 'div',
19622         cls: 'datepicker dropdown-menu roo-dynamic',
19623         cn: [
19624         {
19625             tag: 'div',
19626             cls: 'datepicker-days',
19627             cn: [
19628             {
19629                 tag: 'table',
19630                 cls: 'table-condensed',
19631                 cn:[
19632                 Roo.bootstrap.DateField.head,
19633                 {
19634                     tag: 'tbody'
19635                 },
19636                 Roo.bootstrap.DateField.footer
19637                 ]
19638             }
19639             ]
19640         },
19641         {
19642             tag: 'div',
19643             cls: 'datepicker-months',
19644             cn: [
19645             {
19646                 tag: 'table',
19647                 cls: 'table-condensed',
19648                 cn:[
19649                 Roo.bootstrap.DateField.head,
19650                 Roo.bootstrap.DateField.content,
19651                 Roo.bootstrap.DateField.footer
19652                 ]
19653             }
19654             ]
19655         },
19656         {
19657             tag: 'div',
19658             cls: 'datepicker-years',
19659             cn: [
19660             {
19661                 tag: 'table',
19662                 cls: 'table-condensed',
19663                 cn:[
19664                 Roo.bootstrap.DateField.head,
19665                 Roo.bootstrap.DateField.content,
19666                 Roo.bootstrap.DateField.footer
19667                 ]
19668             }
19669             ]
19670         }
19671         ]
19672     }
19673 });
19674
19675  
19676
19677  /*
19678  * - LGPL
19679  *
19680  * TimeField
19681  * 
19682  */
19683
19684 /**
19685  * @class Roo.bootstrap.TimeField
19686  * @extends Roo.bootstrap.Input
19687  * Bootstrap DateField class
19688  * 
19689  * 
19690  * @constructor
19691  * Create a new TimeField
19692  * @param {Object} config The config object
19693  */
19694
19695 Roo.bootstrap.TimeField = function(config){
19696     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19697     this.addEvents({
19698             /**
19699              * @event show
19700              * Fires when this field show.
19701              * @param {Roo.bootstrap.DateField} thisthis
19702              * @param {Mixed} date The date value
19703              */
19704             show : true,
19705             /**
19706              * @event show
19707              * Fires when this field hide.
19708              * @param {Roo.bootstrap.DateField} this
19709              * @param {Mixed} date The date value
19710              */
19711             hide : true,
19712             /**
19713              * @event select
19714              * Fires when select a date.
19715              * @param {Roo.bootstrap.DateField} this
19716              * @param {Mixed} date The date value
19717              */
19718             select : true
19719         });
19720 };
19721
19722 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19723     
19724     /**
19725      * @cfg {String} format
19726      * The default time format string which can be overriden for localization support.  The format must be
19727      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19728      */
19729     format : "H:i",
19730        
19731     onRender: function(ct, position)
19732     {
19733         
19734         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19735                 
19736         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19737         
19738         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19739         
19740         this.pop = this.picker().select('>.datepicker-time',true).first();
19741         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19742         
19743         this.picker().on('mousedown', this.onMousedown, this);
19744         this.picker().on('click', this.onClick, this);
19745         
19746         this.picker().addClass('datepicker-dropdown');
19747     
19748         this.fillTime();
19749         this.update();
19750             
19751         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19752         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19753         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19754         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19755         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19756         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19757
19758     },
19759     
19760     fireKey: function(e){
19761         if (!this.picker().isVisible()){
19762             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19763                 this.show();
19764             }
19765             return;
19766         }
19767
19768         e.preventDefault();
19769         
19770         switch(e.keyCode){
19771             case 27: // escape
19772                 this.hide();
19773                 break;
19774             case 37: // left
19775             case 39: // right
19776                 this.onTogglePeriod();
19777                 break;
19778             case 38: // up
19779                 this.onIncrementMinutes();
19780                 break;
19781             case 40: // down
19782                 this.onDecrementMinutes();
19783                 break;
19784             case 13: // enter
19785             case 9: // tab
19786                 this.setTime();
19787                 break;
19788         }
19789     },
19790     
19791     onClick: function(e) {
19792         e.stopPropagation();
19793         e.preventDefault();
19794     },
19795     
19796     picker : function()
19797     {
19798         return this.el.select('.datepicker', true).first();
19799     },
19800     
19801     fillTime: function()
19802     {    
19803         var time = this.pop.select('tbody', true).first();
19804         
19805         time.dom.innerHTML = '';
19806         
19807         time.createChild({
19808             tag: 'tr',
19809             cn: [
19810                 {
19811                     tag: 'td',
19812                     cn: [
19813                         {
19814                             tag: 'a',
19815                             href: '#',
19816                             cls: 'btn',
19817                             cn: [
19818                                 {
19819                                     tag: 'span',
19820                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19821                                 }
19822                             ]
19823                         } 
19824                     ]
19825                 },
19826                 {
19827                     tag: 'td',
19828                     cls: 'separator'
19829                 },
19830                 {
19831                     tag: 'td',
19832                     cn: [
19833                         {
19834                             tag: 'a',
19835                             href: '#',
19836                             cls: 'btn',
19837                             cn: [
19838                                 {
19839                                     tag: 'span',
19840                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19841                                 }
19842                             ]
19843                         }
19844                     ]
19845                 },
19846                 {
19847                     tag: 'td',
19848                     cls: 'separator'
19849                 }
19850             ]
19851         });
19852         
19853         time.createChild({
19854             tag: 'tr',
19855             cn: [
19856                 {
19857                     tag: 'td',
19858                     cn: [
19859                         {
19860                             tag: 'span',
19861                             cls: 'timepicker-hour',
19862                             html: '00'
19863                         }  
19864                     ]
19865                 },
19866                 {
19867                     tag: 'td',
19868                     cls: 'separator',
19869                     html: ':'
19870                 },
19871                 {
19872                     tag: 'td',
19873                     cn: [
19874                         {
19875                             tag: 'span',
19876                             cls: 'timepicker-minute',
19877                             html: '00'
19878                         }  
19879                     ]
19880                 },
19881                 {
19882                     tag: 'td',
19883                     cls: 'separator'
19884                 },
19885                 {
19886                     tag: 'td',
19887                     cn: [
19888                         {
19889                             tag: 'button',
19890                             type: 'button',
19891                             cls: 'btn btn-primary period',
19892                             html: 'AM'
19893                             
19894                         }
19895                     ]
19896                 }
19897             ]
19898         });
19899         
19900         time.createChild({
19901             tag: 'tr',
19902             cn: [
19903                 {
19904                     tag: 'td',
19905                     cn: [
19906                         {
19907                             tag: 'a',
19908                             href: '#',
19909                             cls: 'btn',
19910                             cn: [
19911                                 {
19912                                     tag: 'span',
19913                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19914                                 }
19915                             ]
19916                         }
19917                     ]
19918                 },
19919                 {
19920                     tag: 'td',
19921                     cls: 'separator'
19922                 },
19923                 {
19924                     tag: 'td',
19925                     cn: [
19926                         {
19927                             tag: 'a',
19928                             href: '#',
19929                             cls: 'btn',
19930                             cn: [
19931                                 {
19932                                     tag: 'span',
19933                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19934                                 }
19935                             ]
19936                         }
19937                     ]
19938                 },
19939                 {
19940                     tag: 'td',
19941                     cls: 'separator'
19942                 }
19943             ]
19944         });
19945         
19946     },
19947     
19948     update: function()
19949     {
19950         
19951         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19952         
19953         this.fill();
19954     },
19955     
19956     fill: function() 
19957     {
19958         var hours = this.time.getHours();
19959         var minutes = this.time.getMinutes();
19960         var period = 'AM';
19961         
19962         if(hours > 11){
19963             period = 'PM';
19964         }
19965         
19966         if(hours == 0){
19967             hours = 12;
19968         }
19969         
19970         
19971         if(hours > 12){
19972             hours = hours - 12;
19973         }
19974         
19975         if(hours < 10){
19976             hours = '0' + hours;
19977         }
19978         
19979         if(minutes < 10){
19980             minutes = '0' + minutes;
19981         }
19982         
19983         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19984         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19985         this.pop.select('button', true).first().dom.innerHTML = period;
19986         
19987     },
19988     
19989     place: function()
19990     {   
19991         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19992         
19993         var cls = ['bottom'];
19994         
19995         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19996             cls.pop();
19997             cls.push('top');
19998         }
19999         
20000         cls.push('right');
20001         
20002         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20003             cls.pop();
20004             cls.push('left');
20005         }
20006         
20007         this.picker().addClass(cls.join('-'));
20008         
20009         var _this = this;
20010         
20011         Roo.each(cls, function(c){
20012             if(c == 'bottom'){
20013                 _this.picker().setTop(_this.inputEl().getHeight());
20014                 return;
20015             }
20016             if(c == 'top'){
20017                 _this.picker().setTop(0 - _this.picker().getHeight());
20018                 return;
20019             }
20020             
20021             if(c == 'left'){
20022                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20023                 return;
20024             }
20025             if(c == 'right'){
20026                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20027                 return;
20028             }
20029         });
20030         
20031     },
20032   
20033     onFocus : function()
20034     {
20035         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20036         this.show();
20037     },
20038     
20039     onBlur : function()
20040     {
20041         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20042         this.hide();
20043     },
20044     
20045     show : function()
20046     {
20047         this.picker().show();
20048         this.pop.show();
20049         this.update();
20050         this.place();
20051         
20052         this.fireEvent('show', this, this.date);
20053     },
20054     
20055     hide : function()
20056     {
20057         this.picker().hide();
20058         this.pop.hide();
20059         
20060         this.fireEvent('hide', this, this.date);
20061     },
20062     
20063     setTime : function()
20064     {
20065         this.hide();
20066         this.setValue(this.time.format(this.format));
20067         
20068         this.fireEvent('select', this, this.date);
20069         
20070         
20071     },
20072     
20073     onMousedown: function(e){
20074         e.stopPropagation();
20075         e.preventDefault();
20076     },
20077     
20078     onIncrementHours: function()
20079     {
20080         Roo.log('onIncrementHours');
20081         this.time = this.time.add(Date.HOUR, 1);
20082         this.update();
20083         
20084     },
20085     
20086     onDecrementHours: function()
20087     {
20088         Roo.log('onDecrementHours');
20089         this.time = this.time.add(Date.HOUR, -1);
20090         this.update();
20091     },
20092     
20093     onIncrementMinutes: function()
20094     {
20095         Roo.log('onIncrementMinutes');
20096         this.time = this.time.add(Date.MINUTE, 1);
20097         this.update();
20098     },
20099     
20100     onDecrementMinutes: function()
20101     {
20102         Roo.log('onDecrementMinutes');
20103         this.time = this.time.add(Date.MINUTE, -1);
20104         this.update();
20105     },
20106     
20107     onTogglePeriod: function()
20108     {
20109         Roo.log('onTogglePeriod');
20110         this.time = this.time.add(Date.HOUR, 12);
20111         this.update();
20112     }
20113     
20114    
20115 });
20116
20117 Roo.apply(Roo.bootstrap.TimeField,  {
20118     
20119     content : {
20120         tag: 'tbody',
20121         cn: [
20122             {
20123                 tag: 'tr',
20124                 cn: [
20125                 {
20126                     tag: 'td',
20127                     colspan: '7'
20128                 }
20129                 ]
20130             }
20131         ]
20132     },
20133     
20134     footer : {
20135         tag: 'tfoot',
20136         cn: [
20137             {
20138                 tag: 'tr',
20139                 cn: [
20140                 {
20141                     tag: 'th',
20142                     colspan: '7',
20143                     cls: '',
20144                     cn: [
20145                         {
20146                             tag: 'button',
20147                             cls: 'btn btn-info ok',
20148                             html: 'OK'
20149                         }
20150                     ]
20151                 }
20152
20153                 ]
20154             }
20155         ]
20156     }
20157 });
20158
20159 Roo.apply(Roo.bootstrap.TimeField,  {
20160   
20161     template : {
20162         tag: 'div',
20163         cls: 'datepicker dropdown-menu',
20164         cn: [
20165             {
20166                 tag: 'div',
20167                 cls: 'datepicker-time',
20168                 cn: [
20169                 {
20170                     tag: 'table',
20171                     cls: 'table-condensed',
20172                     cn:[
20173                     Roo.bootstrap.TimeField.content,
20174                     Roo.bootstrap.TimeField.footer
20175                     ]
20176                 }
20177                 ]
20178             }
20179         ]
20180     }
20181 });
20182
20183  
20184
20185  /*
20186  * - LGPL
20187  *
20188  * MonthField
20189  * 
20190  */
20191
20192 /**
20193  * @class Roo.bootstrap.MonthField
20194  * @extends Roo.bootstrap.Input
20195  * Bootstrap MonthField class
20196  * 
20197  * @cfg {String} language default en
20198  * 
20199  * @constructor
20200  * Create a new MonthField
20201  * @param {Object} config The config object
20202  */
20203
20204 Roo.bootstrap.MonthField = function(config){
20205     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20206     
20207     this.addEvents({
20208         /**
20209          * @event show
20210          * Fires when this field show.
20211          * @param {Roo.bootstrap.MonthField} this
20212          * @param {Mixed} date The date value
20213          */
20214         show : true,
20215         /**
20216          * @event show
20217          * Fires when this field hide.
20218          * @param {Roo.bootstrap.MonthField} this
20219          * @param {Mixed} date The date value
20220          */
20221         hide : true,
20222         /**
20223          * @event select
20224          * Fires when select a date.
20225          * @param {Roo.bootstrap.MonthField} this
20226          * @param {String} oldvalue The old value
20227          * @param {String} newvalue The new value
20228          */
20229         select : true
20230     });
20231 };
20232
20233 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20234     
20235     onRender: function(ct, position)
20236     {
20237         
20238         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20239         
20240         this.language = this.language || 'en';
20241         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20242         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20243         
20244         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20245         this.isInline = false;
20246         this.isInput = true;
20247         this.component = this.el.select('.add-on', true).first() || false;
20248         this.component = (this.component && this.component.length === 0) ? false : this.component;
20249         this.hasInput = this.component && this.inputEL().length;
20250         
20251         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20252         
20253         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20254         
20255         this.picker().on('mousedown', this.onMousedown, this);
20256         this.picker().on('click', this.onClick, this);
20257         
20258         this.picker().addClass('datepicker-dropdown');
20259         
20260         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20261             v.setStyle('width', '189px');
20262         });
20263         
20264         this.fillMonths();
20265         
20266         this.update();
20267         
20268         if(this.isInline) {
20269             this.show();
20270         }
20271         
20272     },
20273     
20274     setValue: function(v, suppressEvent)
20275     {   
20276         var o = this.getValue();
20277         
20278         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20279         
20280         this.update();
20281
20282         if(suppressEvent !== true){
20283             this.fireEvent('select', this, o, v);
20284         }
20285         
20286     },
20287     
20288     getValue: function()
20289     {
20290         return this.value;
20291     },
20292     
20293     onClick: function(e) 
20294     {
20295         e.stopPropagation();
20296         e.preventDefault();
20297         
20298         var target = e.getTarget();
20299         
20300         if(target.nodeName.toLowerCase() === 'i'){
20301             target = Roo.get(target).dom.parentNode;
20302         }
20303         
20304         var nodeName = target.nodeName;
20305         var className = target.className;
20306         var html = target.innerHTML;
20307         
20308         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20309             return;
20310         }
20311         
20312         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20313         
20314         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20315         
20316         this.hide();
20317                         
20318     },
20319     
20320     picker : function()
20321     {
20322         return this.pickerEl;
20323     },
20324     
20325     fillMonths: function()
20326     {    
20327         var i = 0;
20328         var months = this.picker().select('>.datepicker-months td', true).first();
20329         
20330         months.dom.innerHTML = '';
20331         
20332         while (i < 12) {
20333             var month = {
20334                 tag: 'span',
20335                 cls: 'month',
20336                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20337             };
20338             
20339             months.createChild(month);
20340         }
20341         
20342     },
20343     
20344     update: function()
20345     {
20346         var _this = this;
20347         
20348         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20349             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20350         }
20351         
20352         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20353             e.removeClass('active');
20354             
20355             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20356                 e.addClass('active');
20357             }
20358         })
20359     },
20360     
20361     place: function()
20362     {
20363         if(this.isInline) {
20364             return;
20365         }
20366         
20367         this.picker().removeClass(['bottom', 'top']);
20368         
20369         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20370             /*
20371              * place to the top of element!
20372              *
20373              */
20374             
20375             this.picker().addClass('top');
20376             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20377             
20378             return;
20379         }
20380         
20381         this.picker().addClass('bottom');
20382         
20383         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20384     },
20385     
20386     onFocus : function()
20387     {
20388         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20389         this.show();
20390     },
20391     
20392     onBlur : function()
20393     {
20394         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20395         
20396         var d = this.inputEl().getValue();
20397         
20398         this.setValue(d);
20399                 
20400         this.hide();
20401     },
20402     
20403     show : function()
20404     {
20405         this.picker().show();
20406         this.picker().select('>.datepicker-months', true).first().show();
20407         this.update();
20408         this.place();
20409         
20410         this.fireEvent('show', this, this.date);
20411     },
20412     
20413     hide : function()
20414     {
20415         if(this.isInline) {
20416             return;
20417         }
20418         this.picker().hide();
20419         this.fireEvent('hide', this, this.date);
20420         
20421     },
20422     
20423     onMousedown: function(e)
20424     {
20425         e.stopPropagation();
20426         e.preventDefault();
20427     },
20428     
20429     keyup: function(e)
20430     {
20431         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20432         this.update();
20433     },
20434
20435     fireKey: function(e)
20436     {
20437         if (!this.picker().isVisible()){
20438             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20439                 this.show();
20440             }
20441             return;
20442         }
20443         
20444         var dir;
20445         
20446         switch(e.keyCode){
20447             case 27: // escape
20448                 this.hide();
20449                 e.preventDefault();
20450                 break;
20451             case 37: // left
20452             case 39: // right
20453                 dir = e.keyCode == 37 ? -1 : 1;
20454                 
20455                 this.vIndex = this.vIndex + dir;
20456                 
20457                 if(this.vIndex < 0){
20458                     this.vIndex = 0;
20459                 }
20460                 
20461                 if(this.vIndex > 11){
20462                     this.vIndex = 11;
20463                 }
20464                 
20465                 if(isNaN(this.vIndex)){
20466                     this.vIndex = 0;
20467                 }
20468                 
20469                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20470                 
20471                 break;
20472             case 38: // up
20473             case 40: // down
20474                 
20475                 dir = e.keyCode == 38 ? -1 : 1;
20476                 
20477                 this.vIndex = this.vIndex + dir * 4;
20478                 
20479                 if(this.vIndex < 0){
20480                     this.vIndex = 0;
20481                 }
20482                 
20483                 if(this.vIndex > 11){
20484                     this.vIndex = 11;
20485                 }
20486                 
20487                 if(isNaN(this.vIndex)){
20488                     this.vIndex = 0;
20489                 }
20490                 
20491                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20492                 break;
20493                 
20494             case 13: // enter
20495                 
20496                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20497                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20498                 }
20499                 
20500                 this.hide();
20501                 e.preventDefault();
20502                 break;
20503             case 9: // tab
20504                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20505                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20506                 }
20507                 this.hide();
20508                 break;
20509             case 16: // shift
20510             case 17: // ctrl
20511             case 18: // alt
20512                 break;
20513             default :
20514                 this.hide();
20515                 
20516         }
20517     },
20518     
20519     remove: function() 
20520     {
20521         this.picker().remove();
20522     }
20523    
20524 });
20525
20526 Roo.apply(Roo.bootstrap.MonthField,  {
20527     
20528     content : {
20529         tag: 'tbody',
20530         cn: [
20531         {
20532             tag: 'tr',
20533             cn: [
20534             {
20535                 tag: 'td',
20536                 colspan: '7'
20537             }
20538             ]
20539         }
20540         ]
20541     },
20542     
20543     dates:{
20544         en: {
20545             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20546             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20547         }
20548     }
20549 });
20550
20551 Roo.apply(Roo.bootstrap.MonthField,  {
20552   
20553     template : {
20554         tag: 'div',
20555         cls: 'datepicker dropdown-menu roo-dynamic',
20556         cn: [
20557             {
20558                 tag: 'div',
20559                 cls: 'datepicker-months',
20560                 cn: [
20561                 {
20562                     tag: 'table',
20563                     cls: 'table-condensed',
20564                     cn:[
20565                         Roo.bootstrap.DateField.content
20566                     ]
20567                 }
20568                 ]
20569             }
20570         ]
20571     }
20572 });
20573
20574  
20575
20576  
20577  /*
20578  * - LGPL
20579  *
20580  * CheckBox
20581  * 
20582  */
20583
20584 /**
20585  * @class Roo.bootstrap.CheckBox
20586  * @extends Roo.bootstrap.Input
20587  * Bootstrap CheckBox class
20588  * 
20589  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20590  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20591  * @cfg {String} boxLabel The text that appears beside the checkbox
20592  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20593  * @cfg {Boolean} checked initnal the element
20594  * @cfg {Boolean} inline inline the element (default false)
20595  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20596  * @cfg {String} tooltip label tooltip
20597  * 
20598  * @constructor
20599  * Create a new CheckBox
20600  * @param {Object} config The config object
20601  */
20602
20603 Roo.bootstrap.CheckBox = function(config){
20604     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20605    
20606     this.addEvents({
20607         /**
20608         * @event check
20609         * Fires when the element is checked or unchecked.
20610         * @param {Roo.bootstrap.CheckBox} this This input
20611         * @param {Boolean} checked The new checked value
20612         */
20613        check : true,
20614        /**
20615         * @event click
20616         * Fires when the element is click.
20617         * @param {Roo.bootstrap.CheckBox} this This input
20618         */
20619        click : true
20620     });
20621     
20622 };
20623
20624 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20625   
20626     inputType: 'checkbox',
20627     inputValue: 1,
20628     valueOff: 0,
20629     boxLabel: false,
20630     checked: false,
20631     weight : false,
20632     inline: false,
20633     tooltip : '',
20634     
20635     getAutoCreate : function()
20636     {
20637         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20638         
20639         var id = Roo.id();
20640         
20641         var cfg = {};
20642         
20643         cfg.cls = 'form-group ' + this.inputType; //input-group
20644         
20645         if(this.inline){
20646             cfg.cls += ' ' + this.inputType + '-inline';
20647         }
20648         
20649         var input =  {
20650             tag: 'input',
20651             id : id,
20652             type : this.inputType,
20653             value : this.inputValue,
20654             cls : 'roo-' + this.inputType, //'form-box',
20655             placeholder : this.placeholder || ''
20656             
20657         };
20658         
20659         if(this.inputType != 'radio'){
20660             var hidden =  {
20661                 tag: 'input',
20662                 type : 'hidden',
20663                 cls : 'roo-hidden-value',
20664                 value : this.checked ? this.inputValue : this.valueOff
20665             };
20666         }
20667         
20668             
20669         if (this.weight) { // Validity check?
20670             cfg.cls += " " + this.inputType + "-" + this.weight;
20671         }
20672         
20673         if (this.disabled) {
20674             input.disabled=true;
20675         }
20676         
20677         if(this.checked){
20678             input.checked = this.checked;
20679         }
20680         
20681         if (this.name) {
20682             
20683             input.name = this.name;
20684             
20685             if(this.inputType != 'radio'){
20686                 hidden.name = this.name;
20687                 input.name = '_hidden_' + this.name;
20688             }
20689         }
20690         
20691         if (this.size) {
20692             input.cls += ' input-' + this.size;
20693         }
20694         
20695         var settings=this;
20696         
20697         ['xs','sm','md','lg'].map(function(size){
20698             if (settings[size]) {
20699                 cfg.cls += ' col-' + size + '-' + settings[size];
20700             }
20701         });
20702         
20703         var inputblock = input;
20704          
20705         if (this.before || this.after) {
20706             
20707             inputblock = {
20708                 cls : 'input-group',
20709                 cn :  [] 
20710             };
20711             
20712             if (this.before) {
20713                 inputblock.cn.push({
20714                     tag :'span',
20715                     cls : 'input-group-addon',
20716                     html : this.before
20717                 });
20718             }
20719             
20720             inputblock.cn.push(input);
20721             
20722             if(this.inputType != 'radio'){
20723                 inputblock.cn.push(hidden);
20724             }
20725             
20726             if (this.after) {
20727                 inputblock.cn.push({
20728                     tag :'span',
20729                     cls : 'input-group-addon',
20730                     html : this.after
20731                 });
20732             }
20733             
20734         }
20735         
20736         if (align ==='left' && this.fieldLabel.length) {
20737 //                Roo.log("left and has label");
20738             cfg.cn = [
20739                 {
20740                     tag: 'label',
20741                     'for' :  id,
20742                     cls : 'control-label',
20743                     html : this.fieldLabel
20744                 },
20745                 {
20746                     cls : "", 
20747                     cn: [
20748                         inputblock
20749                     ]
20750                 }
20751             ];
20752             
20753             if(this.labelWidth > 12){
20754                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20755             }
20756             
20757             if(this.labelWidth < 13 && this.labelmd == 0){
20758                 this.labelmd = this.labelWidth;
20759             }
20760             
20761             if(this.labellg > 0){
20762                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20763                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20764             }
20765             
20766             if(this.labelmd > 0){
20767                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20768                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20769             }
20770             
20771             if(this.labelsm > 0){
20772                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20773                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20774             }
20775             
20776             if(this.labelxs > 0){
20777                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20778                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20779             }
20780             
20781         } else if ( this.fieldLabel.length) {
20782 //                Roo.log(" label");
20783                 cfg.cn = [
20784                    
20785                     {
20786                         tag: this.boxLabel ? 'span' : 'label',
20787                         'for': id,
20788                         cls: 'control-label box-input-label',
20789                         //cls : 'input-group-addon',
20790                         html : this.fieldLabel
20791                     },
20792                     
20793                     inputblock
20794                     
20795                 ];
20796
20797         } else {
20798             
20799 //                Roo.log(" no label && no align");
20800                 cfg.cn = [  inputblock ] ;
20801                 
20802                 
20803         }
20804         
20805         if(this.boxLabel){
20806              var boxLabelCfg = {
20807                 tag: 'label',
20808                 //'for': id, // box label is handled by onclick - so no for...
20809                 cls: 'box-label',
20810                 html: this.boxLabel
20811             };
20812             
20813             if(this.tooltip){
20814                 boxLabelCfg.tooltip = this.tooltip;
20815             }
20816              
20817             cfg.cn.push(boxLabelCfg);
20818         }
20819         
20820         if(this.inputType != 'radio'){
20821             cfg.cn.push(hidden);
20822         }
20823         
20824         return cfg;
20825         
20826     },
20827     
20828     /**
20829      * return the real input element.
20830      */
20831     inputEl: function ()
20832     {
20833         return this.el.select('input.roo-' + this.inputType,true).first();
20834     },
20835     hiddenEl: function ()
20836     {
20837         return this.el.select('input.roo-hidden-value',true).first();
20838     },
20839     
20840     labelEl: function()
20841     {
20842         return this.el.select('label.control-label',true).first();
20843     },
20844     /* depricated... */
20845     
20846     label: function()
20847     {
20848         return this.labelEl();
20849     },
20850     
20851     boxLabelEl: function()
20852     {
20853         return this.el.select('label.box-label',true).first();
20854     },
20855     
20856     initEvents : function()
20857     {
20858 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20859         
20860         this.inputEl().on('click', this.onClick,  this);
20861         
20862         if (this.boxLabel) { 
20863             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20864         }
20865         
20866         this.startValue = this.getValue();
20867         
20868         if(this.groupId){
20869             Roo.bootstrap.CheckBox.register(this);
20870         }
20871     },
20872     
20873     onClick : function(e)
20874     {   
20875         if(this.fireEvent('click', this, e) !== false){
20876             this.setChecked(!this.checked);
20877         }
20878         
20879     },
20880     
20881     setChecked : function(state,suppressEvent)
20882     {
20883         this.startValue = this.getValue();
20884
20885         if(this.inputType == 'radio'){
20886             
20887             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20888                 e.dom.checked = false;
20889             });
20890             
20891             this.inputEl().dom.checked = true;
20892             
20893             this.inputEl().dom.value = this.inputValue;
20894             
20895             if(suppressEvent !== true){
20896                 this.fireEvent('check', this, true);
20897             }
20898             
20899             this.validate();
20900             
20901             return;
20902         }
20903         
20904         this.checked = state;
20905         
20906         this.inputEl().dom.checked = state;
20907         
20908         
20909         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20910         
20911         if(suppressEvent !== true){
20912             this.fireEvent('check', this, state);
20913         }
20914         
20915         this.validate();
20916     },
20917     
20918     getValue : function()
20919     {
20920         if(this.inputType == 'radio'){
20921             return this.getGroupValue();
20922         }
20923         
20924         return this.hiddenEl().dom.value;
20925         
20926     },
20927     
20928     getGroupValue : function()
20929     {
20930         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20931             return '';
20932         }
20933         
20934         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20935     },
20936     
20937     setValue : function(v,suppressEvent)
20938     {
20939         if(this.inputType == 'radio'){
20940             this.setGroupValue(v, suppressEvent);
20941             return;
20942         }
20943         
20944         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20945         
20946         this.validate();
20947     },
20948     
20949     setGroupValue : function(v, suppressEvent)
20950     {
20951         this.startValue = this.getValue();
20952         
20953         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20954             e.dom.checked = false;
20955             
20956             if(e.dom.value == v){
20957                 e.dom.checked = true;
20958             }
20959         });
20960         
20961         if(suppressEvent !== true){
20962             this.fireEvent('check', this, true);
20963         }
20964
20965         this.validate();
20966         
20967         return;
20968     },
20969     
20970     validate : function()
20971     {
20972         if(this.getVisibilityEl().hasClass('hidden')){
20973             return true;
20974         }
20975         
20976         if(
20977                 this.disabled || 
20978                 (this.inputType == 'radio' && this.validateRadio()) ||
20979                 (this.inputType == 'checkbox' && this.validateCheckbox())
20980         ){
20981             this.markValid();
20982             return true;
20983         }
20984         
20985         this.markInvalid();
20986         return false;
20987     },
20988     
20989     validateRadio : function()
20990     {
20991         if(this.getVisibilityEl().hasClass('hidden')){
20992             return true;
20993         }
20994         
20995         if(this.allowBlank){
20996             return true;
20997         }
20998         
20999         var valid = false;
21000         
21001         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21002             if(!e.dom.checked){
21003                 return;
21004             }
21005             
21006             valid = true;
21007             
21008             return false;
21009         });
21010         
21011         return valid;
21012     },
21013     
21014     validateCheckbox : function()
21015     {
21016         if(!this.groupId){
21017             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21018             //return (this.getValue() == this.inputValue) ? true : false;
21019         }
21020         
21021         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21022         
21023         if(!group){
21024             return false;
21025         }
21026         
21027         var r = false;
21028         
21029         for(var i in group){
21030             if(group[i].el.isVisible(true)){
21031                 r = false;
21032                 break;
21033             }
21034             
21035             r = true;
21036         }
21037         
21038         for(var i in group){
21039             if(r){
21040                 break;
21041             }
21042             
21043             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21044         }
21045         
21046         return r;
21047     },
21048     
21049     /**
21050      * Mark this field as valid
21051      */
21052     markValid : function()
21053     {
21054         var _this = this;
21055         
21056         this.fireEvent('valid', this);
21057         
21058         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21059         
21060         if(this.groupId){
21061             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21062         }
21063         
21064         if(label){
21065             label.markValid();
21066         }
21067
21068         if(this.inputType == 'radio'){
21069             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21070                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21071                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21072             });
21073             
21074             return;
21075         }
21076
21077         if(!this.groupId){
21078             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21079             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21080             return;
21081         }
21082         
21083         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21084         
21085         if(!group){
21086             return;
21087         }
21088         
21089         for(var i in group){
21090             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21091             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21092         }
21093     },
21094     
21095      /**
21096      * Mark this field as invalid
21097      * @param {String} msg The validation message
21098      */
21099     markInvalid : function(msg)
21100     {
21101         if(this.allowBlank){
21102             return;
21103         }
21104         
21105         var _this = this;
21106         
21107         this.fireEvent('invalid', this, msg);
21108         
21109         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21110         
21111         if(this.groupId){
21112             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21113         }
21114         
21115         if(label){
21116             label.markInvalid();
21117         }
21118             
21119         if(this.inputType == 'radio'){
21120             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21121                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21122                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21123             });
21124             
21125             return;
21126         }
21127         
21128         if(!this.groupId){
21129             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21130             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21131             return;
21132         }
21133         
21134         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21135         
21136         if(!group){
21137             return;
21138         }
21139         
21140         for(var i in group){
21141             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21142             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21143         }
21144         
21145     },
21146     
21147     clearInvalid : function()
21148     {
21149         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21150         
21151         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21152         
21153         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21154         
21155         if (label && label.iconEl) {
21156             label.iconEl.removeClass(label.validClass);
21157             label.iconEl.removeClass(label.invalidClass);
21158         }
21159     },
21160     
21161     disable : function()
21162     {
21163         if(this.inputType != 'radio'){
21164             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21165             return;
21166         }
21167         
21168         var _this = this;
21169         
21170         if(this.rendered){
21171             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21172                 _this.getActionEl().addClass(this.disabledClass);
21173                 e.dom.disabled = true;
21174             });
21175         }
21176         
21177         this.disabled = true;
21178         this.fireEvent("disable", this);
21179         return this;
21180     },
21181
21182     enable : function()
21183     {
21184         if(this.inputType != 'radio'){
21185             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21186             return;
21187         }
21188         
21189         var _this = this;
21190         
21191         if(this.rendered){
21192             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21193                 _this.getActionEl().removeClass(this.disabledClass);
21194                 e.dom.disabled = false;
21195             });
21196         }
21197         
21198         this.disabled = false;
21199         this.fireEvent("enable", this);
21200         return this;
21201     },
21202     
21203     setBoxLabel : function(v)
21204     {
21205         this.boxLabel = v;
21206         
21207         if(this.rendered){
21208             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21209         }
21210     }
21211
21212 });
21213
21214 Roo.apply(Roo.bootstrap.CheckBox, {
21215     
21216     groups: {},
21217     
21218      /**
21219     * register a CheckBox Group
21220     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21221     */
21222     register : function(checkbox)
21223     {
21224         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21225             this.groups[checkbox.groupId] = {};
21226         }
21227         
21228         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21229             return;
21230         }
21231         
21232         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21233         
21234     },
21235     /**
21236     * fetch a CheckBox Group based on the group ID
21237     * @param {string} the group ID
21238     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21239     */
21240     get: function(groupId) {
21241         if (typeof(this.groups[groupId]) == 'undefined') {
21242             return false;
21243         }
21244         
21245         return this.groups[groupId] ;
21246     }
21247     
21248     
21249 });
21250 /*
21251  * - LGPL
21252  *
21253  * RadioItem
21254  * 
21255  */
21256
21257 /**
21258  * @class Roo.bootstrap.Radio
21259  * @extends Roo.bootstrap.Component
21260  * Bootstrap Radio class
21261  * @cfg {String} boxLabel - the label associated
21262  * @cfg {String} value - the value of radio
21263  * 
21264  * @constructor
21265  * Create a new Radio
21266  * @param {Object} config The config object
21267  */
21268 Roo.bootstrap.Radio = function(config){
21269     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21270     
21271 };
21272
21273 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21274     
21275     boxLabel : '',
21276     
21277     value : '',
21278     
21279     getAutoCreate : function()
21280     {
21281         var cfg = {
21282             tag : 'div',
21283             cls : 'form-group radio',
21284             cn : [
21285                 {
21286                     tag : 'label',
21287                     cls : 'box-label',
21288                     html : this.boxLabel
21289                 }
21290             ]
21291         };
21292         
21293         return cfg;
21294     },
21295     
21296     initEvents : function() 
21297     {
21298         this.parent().register(this);
21299         
21300         this.el.on('click', this.onClick, this);
21301         
21302     },
21303     
21304     onClick : function(e)
21305     {
21306         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21307             this.setChecked(true);
21308         }
21309     },
21310     
21311     setChecked : function(state, suppressEvent)
21312     {
21313         this.parent().setValue(this.value, suppressEvent);
21314         
21315     },
21316     
21317     setBoxLabel : function(v)
21318     {
21319         this.boxLabel = v;
21320         
21321         if(this.rendered){
21322             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21323         }
21324     }
21325     
21326 });
21327  
21328
21329  /*
21330  * - LGPL
21331  *
21332  * Input
21333  * 
21334  */
21335
21336 /**
21337  * @class Roo.bootstrap.SecurePass
21338  * @extends Roo.bootstrap.Input
21339  * Bootstrap SecurePass class
21340  *
21341  * 
21342  * @constructor
21343  * Create a new SecurePass
21344  * @param {Object} config The config object
21345  */
21346  
21347 Roo.bootstrap.SecurePass = function (config) {
21348     // these go here, so the translation tool can replace them..
21349     this.errors = {
21350         PwdEmpty: "Please type a password, and then retype it to confirm.",
21351         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21352         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21353         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21354         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21355         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21356         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21357         TooWeak: "Your password is Too Weak."
21358     },
21359     this.meterLabel = "Password strength:";
21360     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21361     this.meterClass = [
21362         "roo-password-meter-tooweak", 
21363         "roo-password-meter-weak", 
21364         "roo-password-meter-medium", 
21365         "roo-password-meter-strong", 
21366         "roo-password-meter-grey"
21367     ];
21368     
21369     this.errors = {};
21370     
21371     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21372 }
21373
21374 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21375     /**
21376      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21377      * {
21378      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21379      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21380      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21381      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21382      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21383      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21384      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21385      * })
21386      */
21387     // private
21388     
21389     meterWidth: 300,
21390     errorMsg :'',    
21391     errors: false,
21392     imageRoot: '/',
21393     /**
21394      * @cfg {String/Object} Label for the strength meter (defaults to
21395      * 'Password strength:')
21396      */
21397     // private
21398     meterLabel: '',
21399     /**
21400      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21401      * ['Weak', 'Medium', 'Strong'])
21402      */
21403     // private    
21404     pwdStrengths: false,    
21405     // private
21406     strength: 0,
21407     // private
21408     _lastPwd: null,
21409     // private
21410     kCapitalLetter: 0,
21411     kSmallLetter: 1,
21412     kDigit: 2,
21413     kPunctuation: 3,
21414     
21415     insecure: false,
21416     // private
21417     initEvents: function ()
21418     {
21419         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21420
21421         if (this.el.is('input[type=password]') && Roo.isSafari) {
21422             this.el.on('keydown', this.SafariOnKeyDown, this);
21423         }
21424
21425         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21426     },
21427     // private
21428     onRender: function (ct, position)
21429     {
21430         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21431         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21432         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21433
21434         this.trigger.createChild({
21435                    cn: [
21436                     {
21437                     //id: 'PwdMeter',
21438                     tag: 'div',
21439                     cls: 'roo-password-meter-grey col-xs-12',
21440                     style: {
21441                         //width: 0,
21442                         //width: this.meterWidth + 'px'                                                
21443                         }
21444                     },
21445                     {                            
21446                          cls: 'roo-password-meter-text'                          
21447                     }
21448                 ]            
21449         });
21450
21451          
21452         if (this.hideTrigger) {
21453             this.trigger.setDisplayed(false);
21454         }
21455         this.setSize(this.width || '', this.height || '');
21456     },
21457     // private
21458     onDestroy: function ()
21459     {
21460         if (this.trigger) {
21461             this.trigger.removeAllListeners();
21462             this.trigger.remove();
21463         }
21464         if (this.wrap) {
21465             this.wrap.remove();
21466         }
21467         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21468     },
21469     // private
21470     checkStrength: function ()
21471     {
21472         var pwd = this.inputEl().getValue();
21473         if (pwd == this._lastPwd) {
21474             return;
21475         }
21476
21477         var strength;
21478         if (this.ClientSideStrongPassword(pwd)) {
21479             strength = 3;
21480         } else if (this.ClientSideMediumPassword(pwd)) {
21481             strength = 2;
21482         } else if (this.ClientSideWeakPassword(pwd)) {
21483             strength = 1;
21484         } else {
21485             strength = 0;
21486         }
21487         
21488         Roo.log('strength1: ' + strength);
21489         
21490         //var pm = this.trigger.child('div/div/div').dom;
21491         var pm = this.trigger.child('div/div');
21492         pm.removeClass(this.meterClass);
21493         pm.addClass(this.meterClass[strength]);
21494                 
21495         
21496         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21497                 
21498         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21499         
21500         this._lastPwd = pwd;
21501     },
21502     reset: function ()
21503     {
21504         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21505         
21506         this._lastPwd = '';
21507         
21508         var pm = this.trigger.child('div/div');
21509         pm.removeClass(this.meterClass);
21510         pm.addClass('roo-password-meter-grey');        
21511         
21512         
21513         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21514         
21515         pt.innerHTML = '';
21516         this.inputEl().dom.type='password';
21517     },
21518     // private
21519     validateValue: function (value)
21520     {
21521         
21522         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21523             return false;
21524         }
21525         if (value.length == 0) {
21526             if (this.allowBlank) {
21527                 this.clearInvalid();
21528                 return true;
21529             }
21530
21531             this.markInvalid(this.errors.PwdEmpty);
21532             this.errorMsg = this.errors.PwdEmpty;
21533             return false;
21534         }
21535         
21536         if(this.insecure){
21537             return true;
21538         }
21539         
21540         if ('[\x21-\x7e]*'.match(value)) {
21541             this.markInvalid(this.errors.PwdBadChar);
21542             this.errorMsg = this.errors.PwdBadChar;
21543             return false;
21544         }
21545         if (value.length < 6) {
21546             this.markInvalid(this.errors.PwdShort);
21547             this.errorMsg = this.errors.PwdShort;
21548             return false;
21549         }
21550         if (value.length > 16) {
21551             this.markInvalid(this.errors.PwdLong);
21552             this.errorMsg = this.errors.PwdLong;
21553             return false;
21554         }
21555         var strength;
21556         if (this.ClientSideStrongPassword(value)) {
21557             strength = 3;
21558         } else if (this.ClientSideMediumPassword(value)) {
21559             strength = 2;
21560         } else if (this.ClientSideWeakPassword(value)) {
21561             strength = 1;
21562         } else {
21563             strength = 0;
21564         }
21565
21566         
21567         if (strength < 2) {
21568             //this.markInvalid(this.errors.TooWeak);
21569             this.errorMsg = this.errors.TooWeak;
21570             //return false;
21571         }
21572         
21573         
21574         console.log('strength2: ' + strength);
21575         
21576         //var pm = this.trigger.child('div/div/div').dom;
21577         
21578         var pm = this.trigger.child('div/div');
21579         pm.removeClass(this.meterClass);
21580         pm.addClass(this.meterClass[strength]);
21581                 
21582         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21583                 
21584         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21585         
21586         this.errorMsg = ''; 
21587         return true;
21588     },
21589     // private
21590     CharacterSetChecks: function (type)
21591     {
21592         this.type = type;
21593         this.fResult = false;
21594     },
21595     // private
21596     isctype: function (character, type)
21597     {
21598         switch (type) {  
21599             case this.kCapitalLetter:
21600                 if (character >= 'A' && character <= 'Z') {
21601                     return true;
21602                 }
21603                 break;
21604             
21605             case this.kSmallLetter:
21606                 if (character >= 'a' && character <= 'z') {
21607                     return true;
21608                 }
21609                 break;
21610             
21611             case this.kDigit:
21612                 if (character >= '0' && character <= '9') {
21613                     return true;
21614                 }
21615                 break;
21616             
21617             case this.kPunctuation:
21618                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21619                     return true;
21620                 }
21621                 break;
21622             
21623             default:
21624                 return false;
21625         }
21626
21627     },
21628     // private
21629     IsLongEnough: function (pwd, size)
21630     {
21631         return !(pwd == null || isNaN(size) || pwd.length < size);
21632     },
21633     // private
21634     SpansEnoughCharacterSets: function (word, nb)
21635     {
21636         if (!this.IsLongEnough(word, nb))
21637         {
21638             return false;
21639         }
21640
21641         var characterSetChecks = new Array(
21642             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21643             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21644         );
21645         
21646         for (var index = 0; index < word.length; ++index) {
21647             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21648                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21649                     characterSetChecks[nCharSet].fResult = true;
21650                     break;
21651                 }
21652             }
21653         }
21654
21655         var nCharSets = 0;
21656         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21657             if (characterSetChecks[nCharSet].fResult) {
21658                 ++nCharSets;
21659             }
21660         }
21661
21662         if (nCharSets < nb) {
21663             return false;
21664         }
21665         return true;
21666     },
21667     // private
21668     ClientSideStrongPassword: function (pwd)
21669     {
21670         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21671     },
21672     // private
21673     ClientSideMediumPassword: function (pwd)
21674     {
21675         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21676     },
21677     // private
21678     ClientSideWeakPassword: function (pwd)
21679     {
21680         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21681     }
21682           
21683 })//<script type="text/javascript">
21684
21685 /*
21686  * Based  Ext JS Library 1.1.1
21687  * Copyright(c) 2006-2007, Ext JS, LLC.
21688  * LGPL
21689  *
21690  */
21691  
21692 /**
21693  * @class Roo.HtmlEditorCore
21694  * @extends Roo.Component
21695  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21696  *
21697  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21698  */
21699
21700 Roo.HtmlEditorCore = function(config){
21701     
21702     
21703     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21704     
21705     
21706     this.addEvents({
21707         /**
21708          * @event initialize
21709          * Fires when the editor is fully initialized (including the iframe)
21710          * @param {Roo.HtmlEditorCore} this
21711          */
21712         initialize: true,
21713         /**
21714          * @event activate
21715          * Fires when the editor is first receives the focus. Any insertion must wait
21716          * until after this event.
21717          * @param {Roo.HtmlEditorCore} this
21718          */
21719         activate: true,
21720          /**
21721          * @event beforesync
21722          * Fires before the textarea is updated with content from the editor iframe. Return false
21723          * to cancel the sync.
21724          * @param {Roo.HtmlEditorCore} this
21725          * @param {String} html
21726          */
21727         beforesync: true,
21728          /**
21729          * @event beforepush
21730          * Fires before the iframe editor is updated with content from the textarea. Return false
21731          * to cancel the push.
21732          * @param {Roo.HtmlEditorCore} this
21733          * @param {String} html
21734          */
21735         beforepush: true,
21736          /**
21737          * @event sync
21738          * Fires when the textarea is updated with content from the editor iframe.
21739          * @param {Roo.HtmlEditorCore} this
21740          * @param {String} html
21741          */
21742         sync: true,
21743          /**
21744          * @event push
21745          * Fires when the iframe editor is updated with content from the textarea.
21746          * @param {Roo.HtmlEditorCore} this
21747          * @param {String} html
21748          */
21749         push: true,
21750         
21751         /**
21752          * @event editorevent
21753          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21754          * @param {Roo.HtmlEditorCore} this
21755          */
21756         editorevent: true
21757         
21758     });
21759     
21760     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21761     
21762     // defaults : white / black...
21763     this.applyBlacklists();
21764     
21765     
21766     
21767 };
21768
21769
21770 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21771
21772
21773      /**
21774      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21775      */
21776     
21777     owner : false,
21778     
21779      /**
21780      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21781      *                        Roo.resizable.
21782      */
21783     resizable : false,
21784      /**
21785      * @cfg {Number} height (in pixels)
21786      */   
21787     height: 300,
21788    /**
21789      * @cfg {Number} width (in pixels)
21790      */   
21791     width: 500,
21792     
21793     /**
21794      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21795      * 
21796      */
21797     stylesheets: false,
21798     
21799     // id of frame..
21800     frameId: false,
21801     
21802     // private properties
21803     validationEvent : false,
21804     deferHeight: true,
21805     initialized : false,
21806     activated : false,
21807     sourceEditMode : false,
21808     onFocus : Roo.emptyFn,
21809     iframePad:3,
21810     hideMode:'offsets',
21811     
21812     clearUp: true,
21813     
21814     // blacklist + whitelisted elements..
21815     black: false,
21816     white: false,
21817      
21818     bodyCls : '',
21819
21820     /**
21821      * Protected method that will not generally be called directly. It
21822      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21823      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21824      */
21825     getDocMarkup : function(){
21826         // body styles..
21827         var st = '';
21828         
21829         // inherit styels from page...?? 
21830         if (this.stylesheets === false) {
21831             
21832             Roo.get(document.head).select('style').each(function(node) {
21833                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21834             });
21835             
21836             Roo.get(document.head).select('link').each(function(node) { 
21837                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21838             });
21839             
21840         } else if (!this.stylesheets.length) {
21841                 // simple..
21842                 st = '<style type="text/css">' +
21843                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21844                    '</style>';
21845         } else { 
21846             st = '<style type="text/css">' +
21847                     this.stylesheets +
21848                 '</style>';
21849         }
21850         
21851         st +=  '<style type="text/css">' +
21852             'IMG { cursor: pointer } ' +
21853         '</style>';
21854
21855         var cls = 'roo-htmleditor-body';
21856         
21857         if(this.bodyCls.length){
21858             cls += ' ' + this.bodyCls;
21859         }
21860         
21861         return '<html><head>' + st  +
21862             //<style type="text/css">' +
21863             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21864             //'</style>' +
21865             ' </head><body class="' +  cls + '"></body></html>';
21866     },
21867
21868     // private
21869     onRender : function(ct, position)
21870     {
21871         var _t = this;
21872         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21873         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21874         
21875         
21876         this.el.dom.style.border = '0 none';
21877         this.el.dom.setAttribute('tabIndex', -1);
21878         this.el.addClass('x-hidden hide');
21879         
21880         
21881         
21882         if(Roo.isIE){ // fix IE 1px bogus margin
21883             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21884         }
21885        
21886         
21887         this.frameId = Roo.id();
21888         
21889          
21890         
21891         var iframe = this.owner.wrap.createChild({
21892             tag: 'iframe',
21893             cls: 'form-control', // bootstrap..
21894             id: this.frameId,
21895             name: this.frameId,
21896             frameBorder : 'no',
21897             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21898         }, this.el
21899         );
21900         
21901         
21902         this.iframe = iframe.dom;
21903
21904          this.assignDocWin();
21905         
21906         this.doc.designMode = 'on';
21907        
21908         this.doc.open();
21909         this.doc.write(this.getDocMarkup());
21910         this.doc.close();
21911
21912         
21913         var task = { // must defer to wait for browser to be ready
21914             run : function(){
21915                 //console.log("run task?" + this.doc.readyState);
21916                 this.assignDocWin();
21917                 if(this.doc.body || this.doc.readyState == 'complete'){
21918                     try {
21919                         this.doc.designMode="on";
21920                     } catch (e) {
21921                         return;
21922                     }
21923                     Roo.TaskMgr.stop(task);
21924                     this.initEditor.defer(10, this);
21925                 }
21926             },
21927             interval : 10,
21928             duration: 10000,
21929             scope: this
21930         };
21931         Roo.TaskMgr.start(task);
21932
21933     },
21934
21935     // private
21936     onResize : function(w, h)
21937     {
21938          Roo.log('resize: ' +w + ',' + h );
21939         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21940         if(!this.iframe){
21941             return;
21942         }
21943         if(typeof w == 'number'){
21944             
21945             this.iframe.style.width = w + 'px';
21946         }
21947         if(typeof h == 'number'){
21948             
21949             this.iframe.style.height = h + 'px';
21950             if(this.doc){
21951                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21952             }
21953         }
21954         
21955     },
21956
21957     /**
21958      * Toggles the editor between standard and source edit mode.
21959      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21960      */
21961     toggleSourceEdit : function(sourceEditMode){
21962         
21963         this.sourceEditMode = sourceEditMode === true;
21964         
21965         if(this.sourceEditMode){
21966  
21967             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21968             
21969         }else{
21970             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21971             //this.iframe.className = '';
21972             this.deferFocus();
21973         }
21974         //this.setSize(this.owner.wrap.getSize());
21975         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21976     },
21977
21978     
21979   
21980
21981     /**
21982      * Protected method that will not generally be called directly. If you need/want
21983      * custom HTML cleanup, this is the method you should override.
21984      * @param {String} html The HTML to be cleaned
21985      * return {String} The cleaned HTML
21986      */
21987     cleanHtml : function(html){
21988         html = String(html);
21989         if(html.length > 5){
21990             if(Roo.isSafari){ // strip safari nonsense
21991                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21992             }
21993         }
21994         if(html == '&nbsp;'){
21995             html = '';
21996         }
21997         return html;
21998     },
21999
22000     /**
22001      * HTML Editor -> Textarea
22002      * Protected method that will not generally be called directly. Syncs the contents
22003      * of the editor iframe with the textarea.
22004      */
22005     syncValue : function(){
22006         if(this.initialized){
22007             var bd = (this.doc.body || this.doc.documentElement);
22008             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22009             var html = bd.innerHTML;
22010             if(Roo.isSafari){
22011                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22012                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22013                 if(m && m[1]){
22014                     html = '<div style="'+m[0]+'">' + html + '</div>';
22015                 }
22016             }
22017             html = this.cleanHtml(html);
22018             // fix up the special chars.. normaly like back quotes in word...
22019             // however we do not want to do this with chinese..
22020             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22021                 var cc = b.charCodeAt();
22022                 if (
22023                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22024                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22025                     (cc >= 0xf900 && cc < 0xfb00 )
22026                 ) {
22027                         return b;
22028                 }
22029                 return "&#"+cc+";" 
22030             });
22031             if(this.owner.fireEvent('beforesync', this, html) !== false){
22032                 this.el.dom.value = html;
22033                 this.owner.fireEvent('sync', this, html);
22034             }
22035         }
22036     },
22037
22038     /**
22039      * Protected method that will not generally be called directly. Pushes the value of the textarea
22040      * into the iframe editor.
22041      */
22042     pushValue : function(){
22043         if(this.initialized){
22044             var v = this.el.dom.value.trim();
22045             
22046 //            if(v.length < 1){
22047 //                v = '&#160;';
22048 //            }
22049             
22050             if(this.owner.fireEvent('beforepush', this, v) !== false){
22051                 var d = (this.doc.body || this.doc.documentElement);
22052                 d.innerHTML = v;
22053                 this.cleanUpPaste();
22054                 this.el.dom.value = d.innerHTML;
22055                 this.owner.fireEvent('push', this, v);
22056             }
22057         }
22058     },
22059
22060     // private
22061     deferFocus : function(){
22062         this.focus.defer(10, this);
22063     },
22064
22065     // doc'ed in Field
22066     focus : function(){
22067         if(this.win && !this.sourceEditMode){
22068             this.win.focus();
22069         }else{
22070             this.el.focus();
22071         }
22072     },
22073     
22074     assignDocWin: function()
22075     {
22076         var iframe = this.iframe;
22077         
22078          if(Roo.isIE){
22079             this.doc = iframe.contentWindow.document;
22080             this.win = iframe.contentWindow;
22081         } else {
22082 //            if (!Roo.get(this.frameId)) {
22083 //                return;
22084 //            }
22085 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22086 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22087             
22088             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22089                 return;
22090             }
22091             
22092             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22093             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22094         }
22095     },
22096     
22097     // private
22098     initEditor : function(){
22099         //console.log("INIT EDITOR");
22100         this.assignDocWin();
22101         
22102         
22103         
22104         this.doc.designMode="on";
22105         this.doc.open();
22106         this.doc.write(this.getDocMarkup());
22107         this.doc.close();
22108         
22109         var dbody = (this.doc.body || this.doc.documentElement);
22110         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22111         // this copies styles from the containing element into thsi one..
22112         // not sure why we need all of this..
22113         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22114         
22115         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22116         //ss['background-attachment'] = 'fixed'; // w3c
22117         dbody.bgProperties = 'fixed'; // ie
22118         //Roo.DomHelper.applyStyles(dbody, ss);
22119         Roo.EventManager.on(this.doc, {
22120             //'mousedown': this.onEditorEvent,
22121             'mouseup': this.onEditorEvent,
22122             'dblclick': this.onEditorEvent,
22123             'click': this.onEditorEvent,
22124             'keyup': this.onEditorEvent,
22125             buffer:100,
22126             scope: this
22127         });
22128         if(Roo.isGecko){
22129             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22130         }
22131         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22132             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22133         }
22134         this.initialized = true;
22135
22136         this.owner.fireEvent('initialize', this);
22137         this.pushValue();
22138     },
22139
22140     // private
22141     onDestroy : function(){
22142         
22143         
22144         
22145         if(this.rendered){
22146             
22147             //for (var i =0; i < this.toolbars.length;i++) {
22148             //    // fixme - ask toolbars for heights?
22149             //    this.toolbars[i].onDestroy();
22150            // }
22151             
22152             //this.wrap.dom.innerHTML = '';
22153             //this.wrap.remove();
22154         }
22155     },
22156
22157     // private
22158     onFirstFocus : function(){
22159         
22160         this.assignDocWin();
22161         
22162         
22163         this.activated = true;
22164          
22165     
22166         if(Roo.isGecko){ // prevent silly gecko errors
22167             this.win.focus();
22168             var s = this.win.getSelection();
22169             if(!s.focusNode || s.focusNode.nodeType != 3){
22170                 var r = s.getRangeAt(0);
22171                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22172                 r.collapse(true);
22173                 this.deferFocus();
22174             }
22175             try{
22176                 this.execCmd('useCSS', true);
22177                 this.execCmd('styleWithCSS', false);
22178             }catch(e){}
22179         }
22180         this.owner.fireEvent('activate', this);
22181     },
22182
22183     // private
22184     adjustFont: function(btn){
22185         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22186         //if(Roo.isSafari){ // safari
22187         //    adjust *= 2;
22188        // }
22189         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22190         if(Roo.isSafari){ // safari
22191             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22192             v =  (v < 10) ? 10 : v;
22193             v =  (v > 48) ? 48 : v;
22194             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22195             
22196         }
22197         
22198         
22199         v = Math.max(1, v+adjust);
22200         
22201         this.execCmd('FontSize', v  );
22202     },
22203
22204     onEditorEvent : function(e)
22205     {
22206         this.owner.fireEvent('editorevent', this, e);
22207       //  this.updateToolbar();
22208         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22209     },
22210
22211     insertTag : function(tg)
22212     {
22213         // could be a bit smarter... -> wrap the current selected tRoo..
22214         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22215             
22216             range = this.createRange(this.getSelection());
22217             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22218             wrappingNode.appendChild(range.extractContents());
22219             range.insertNode(wrappingNode);
22220
22221             return;
22222             
22223             
22224             
22225         }
22226         this.execCmd("formatblock",   tg);
22227         
22228     },
22229     
22230     insertText : function(txt)
22231     {
22232         
22233         
22234         var range = this.createRange();
22235         range.deleteContents();
22236                //alert(Sender.getAttribute('label'));
22237                
22238         range.insertNode(this.doc.createTextNode(txt));
22239     } ,
22240     
22241      
22242
22243     /**
22244      * Executes a Midas editor command on the editor document and performs necessary focus and
22245      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22246      * @param {String} cmd The Midas command
22247      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22248      */
22249     relayCmd : function(cmd, value){
22250         this.win.focus();
22251         this.execCmd(cmd, value);
22252         this.owner.fireEvent('editorevent', this);
22253         //this.updateToolbar();
22254         this.owner.deferFocus();
22255     },
22256
22257     /**
22258      * Executes a Midas editor command directly on the editor document.
22259      * For visual commands, you should use {@link #relayCmd} instead.
22260      * <b>This should only be called after the editor is initialized.</b>
22261      * @param {String} cmd The Midas command
22262      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22263      */
22264     execCmd : function(cmd, value){
22265         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22266         this.syncValue();
22267     },
22268  
22269  
22270    
22271     /**
22272      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22273      * to insert tRoo.
22274      * @param {String} text | dom node.. 
22275      */
22276     insertAtCursor : function(text)
22277     {
22278         
22279         if(!this.activated){
22280             return;
22281         }
22282         /*
22283         if(Roo.isIE){
22284             this.win.focus();
22285             var r = this.doc.selection.createRange();
22286             if(r){
22287                 r.collapse(true);
22288                 r.pasteHTML(text);
22289                 this.syncValue();
22290                 this.deferFocus();
22291             
22292             }
22293             return;
22294         }
22295         */
22296         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22297             this.win.focus();
22298             
22299             
22300             // from jquery ui (MIT licenced)
22301             var range, node;
22302             var win = this.win;
22303             
22304             if (win.getSelection && win.getSelection().getRangeAt) {
22305                 range = win.getSelection().getRangeAt(0);
22306                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22307                 range.insertNode(node);
22308             } else if (win.document.selection && win.document.selection.createRange) {
22309                 // no firefox support
22310                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22311                 win.document.selection.createRange().pasteHTML(txt);
22312             } else {
22313                 // no firefox support
22314                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22315                 this.execCmd('InsertHTML', txt);
22316             } 
22317             
22318             this.syncValue();
22319             
22320             this.deferFocus();
22321         }
22322     },
22323  // private
22324     mozKeyPress : function(e){
22325         if(e.ctrlKey){
22326             var c = e.getCharCode(), cmd;
22327           
22328             if(c > 0){
22329                 c = String.fromCharCode(c).toLowerCase();
22330                 switch(c){
22331                     case 'b':
22332                         cmd = 'bold';
22333                         break;
22334                     case 'i':
22335                         cmd = 'italic';
22336                         break;
22337                     
22338                     case 'u':
22339                         cmd = 'underline';
22340                         break;
22341                     
22342                     case 'v':
22343                         this.cleanUpPaste.defer(100, this);
22344                         return;
22345                         
22346                 }
22347                 if(cmd){
22348                     this.win.focus();
22349                     this.execCmd(cmd);
22350                     this.deferFocus();
22351                     e.preventDefault();
22352                 }
22353                 
22354             }
22355         }
22356     },
22357
22358     // private
22359     fixKeys : function(){ // load time branching for fastest keydown performance
22360         if(Roo.isIE){
22361             return function(e){
22362                 var k = e.getKey(), r;
22363                 if(k == e.TAB){
22364                     e.stopEvent();
22365                     r = this.doc.selection.createRange();
22366                     if(r){
22367                         r.collapse(true);
22368                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22369                         this.deferFocus();
22370                     }
22371                     return;
22372                 }
22373                 
22374                 if(k == e.ENTER){
22375                     r = this.doc.selection.createRange();
22376                     if(r){
22377                         var target = r.parentElement();
22378                         if(!target || target.tagName.toLowerCase() != 'li'){
22379                             e.stopEvent();
22380                             r.pasteHTML('<br />');
22381                             r.collapse(false);
22382                             r.select();
22383                         }
22384                     }
22385                 }
22386                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22387                     this.cleanUpPaste.defer(100, this);
22388                     return;
22389                 }
22390                 
22391                 
22392             };
22393         }else if(Roo.isOpera){
22394             return function(e){
22395                 var k = e.getKey();
22396                 if(k == e.TAB){
22397                     e.stopEvent();
22398                     this.win.focus();
22399                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22400                     this.deferFocus();
22401                 }
22402                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22403                     this.cleanUpPaste.defer(100, this);
22404                     return;
22405                 }
22406                 
22407             };
22408         }else if(Roo.isSafari){
22409             return function(e){
22410                 var k = e.getKey();
22411                 
22412                 if(k == e.TAB){
22413                     e.stopEvent();
22414                     this.execCmd('InsertText','\t');
22415                     this.deferFocus();
22416                     return;
22417                 }
22418                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22419                     this.cleanUpPaste.defer(100, this);
22420                     return;
22421                 }
22422                 
22423              };
22424         }
22425     }(),
22426     
22427     getAllAncestors: function()
22428     {
22429         var p = this.getSelectedNode();
22430         var a = [];
22431         if (!p) {
22432             a.push(p); // push blank onto stack..
22433             p = this.getParentElement();
22434         }
22435         
22436         
22437         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22438             a.push(p);
22439             p = p.parentNode;
22440         }
22441         a.push(this.doc.body);
22442         return a;
22443     },
22444     lastSel : false,
22445     lastSelNode : false,
22446     
22447     
22448     getSelection : function() 
22449     {
22450         this.assignDocWin();
22451         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22452     },
22453     
22454     getSelectedNode: function() 
22455     {
22456         // this may only work on Gecko!!!
22457         
22458         // should we cache this!!!!
22459         
22460         
22461         
22462          
22463         var range = this.createRange(this.getSelection()).cloneRange();
22464         
22465         if (Roo.isIE) {
22466             var parent = range.parentElement();
22467             while (true) {
22468                 var testRange = range.duplicate();
22469                 testRange.moveToElementText(parent);
22470                 if (testRange.inRange(range)) {
22471                     break;
22472                 }
22473                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22474                     break;
22475                 }
22476                 parent = parent.parentElement;
22477             }
22478             return parent;
22479         }
22480         
22481         // is ancestor a text element.
22482         var ac =  range.commonAncestorContainer;
22483         if (ac.nodeType == 3) {
22484             ac = ac.parentNode;
22485         }
22486         
22487         var ar = ac.childNodes;
22488          
22489         var nodes = [];
22490         var other_nodes = [];
22491         var has_other_nodes = false;
22492         for (var i=0;i<ar.length;i++) {
22493             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22494                 continue;
22495             }
22496             // fullly contained node.
22497             
22498             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22499                 nodes.push(ar[i]);
22500                 continue;
22501             }
22502             
22503             // probably selected..
22504             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22505                 other_nodes.push(ar[i]);
22506                 continue;
22507             }
22508             // outer..
22509             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22510                 continue;
22511             }
22512             
22513             
22514             has_other_nodes = true;
22515         }
22516         if (!nodes.length && other_nodes.length) {
22517             nodes= other_nodes;
22518         }
22519         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22520             return false;
22521         }
22522         
22523         return nodes[0];
22524     },
22525     createRange: function(sel)
22526     {
22527         // this has strange effects when using with 
22528         // top toolbar - not sure if it's a great idea.
22529         //this.editor.contentWindow.focus();
22530         if (typeof sel != "undefined") {
22531             try {
22532                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22533             } catch(e) {
22534                 return this.doc.createRange();
22535             }
22536         } else {
22537             return this.doc.createRange();
22538         }
22539     },
22540     getParentElement: function()
22541     {
22542         
22543         this.assignDocWin();
22544         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22545         
22546         var range = this.createRange(sel);
22547          
22548         try {
22549             var p = range.commonAncestorContainer;
22550             while (p.nodeType == 3) { // text node
22551                 p = p.parentNode;
22552             }
22553             return p;
22554         } catch (e) {
22555             return null;
22556         }
22557     
22558     },
22559     /***
22560      *
22561      * Range intersection.. the hard stuff...
22562      *  '-1' = before
22563      *  '0' = hits..
22564      *  '1' = after.
22565      *         [ -- selected range --- ]
22566      *   [fail]                        [fail]
22567      *
22568      *    basically..
22569      *      if end is before start or  hits it. fail.
22570      *      if start is after end or hits it fail.
22571      *
22572      *   if either hits (but other is outside. - then it's not 
22573      *   
22574      *    
22575      **/
22576     
22577     
22578     // @see http://www.thismuchiknow.co.uk/?p=64.
22579     rangeIntersectsNode : function(range, node)
22580     {
22581         var nodeRange = node.ownerDocument.createRange();
22582         try {
22583             nodeRange.selectNode(node);
22584         } catch (e) {
22585             nodeRange.selectNodeContents(node);
22586         }
22587     
22588         var rangeStartRange = range.cloneRange();
22589         rangeStartRange.collapse(true);
22590     
22591         var rangeEndRange = range.cloneRange();
22592         rangeEndRange.collapse(false);
22593     
22594         var nodeStartRange = nodeRange.cloneRange();
22595         nodeStartRange.collapse(true);
22596     
22597         var nodeEndRange = nodeRange.cloneRange();
22598         nodeEndRange.collapse(false);
22599     
22600         return rangeStartRange.compareBoundaryPoints(
22601                  Range.START_TO_START, nodeEndRange) == -1 &&
22602                rangeEndRange.compareBoundaryPoints(
22603                  Range.START_TO_START, nodeStartRange) == 1;
22604         
22605          
22606     },
22607     rangeCompareNode : function(range, node)
22608     {
22609         var nodeRange = node.ownerDocument.createRange();
22610         try {
22611             nodeRange.selectNode(node);
22612         } catch (e) {
22613             nodeRange.selectNodeContents(node);
22614         }
22615         
22616         
22617         range.collapse(true);
22618     
22619         nodeRange.collapse(true);
22620      
22621         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22622         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22623          
22624         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22625         
22626         var nodeIsBefore   =  ss == 1;
22627         var nodeIsAfter    = ee == -1;
22628         
22629         if (nodeIsBefore && nodeIsAfter) {
22630             return 0; // outer
22631         }
22632         if (!nodeIsBefore && nodeIsAfter) {
22633             return 1; //right trailed.
22634         }
22635         
22636         if (nodeIsBefore && !nodeIsAfter) {
22637             return 2;  // left trailed.
22638         }
22639         // fully contined.
22640         return 3;
22641     },
22642
22643     // private? - in a new class?
22644     cleanUpPaste :  function()
22645     {
22646         // cleans up the whole document..
22647         Roo.log('cleanuppaste');
22648         
22649         this.cleanUpChildren(this.doc.body);
22650         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22651         if (clean != this.doc.body.innerHTML) {
22652             this.doc.body.innerHTML = clean;
22653         }
22654         
22655     },
22656     
22657     cleanWordChars : function(input) {// change the chars to hex code
22658         var he = Roo.HtmlEditorCore;
22659         
22660         var output = input;
22661         Roo.each(he.swapCodes, function(sw) { 
22662             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22663             
22664             output = output.replace(swapper, sw[1]);
22665         });
22666         
22667         return output;
22668     },
22669     
22670     
22671     cleanUpChildren : function (n)
22672     {
22673         if (!n.childNodes.length) {
22674             return;
22675         }
22676         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22677            this.cleanUpChild(n.childNodes[i]);
22678         }
22679     },
22680     
22681     
22682         
22683     
22684     cleanUpChild : function (node)
22685     {
22686         var ed = this;
22687         //console.log(node);
22688         if (node.nodeName == "#text") {
22689             // clean up silly Windows -- stuff?
22690             return; 
22691         }
22692         if (node.nodeName == "#comment") {
22693             node.parentNode.removeChild(node);
22694             // clean up silly Windows -- stuff?
22695             return; 
22696         }
22697         var lcname = node.tagName.toLowerCase();
22698         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22699         // whitelist of tags..
22700         
22701         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22702             // remove node.
22703             node.parentNode.removeChild(node);
22704             return;
22705             
22706         }
22707         
22708         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22709         
22710         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22711         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22712         
22713         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22714         //    remove_keep_children = true;
22715         //}
22716         
22717         if (remove_keep_children) {
22718             this.cleanUpChildren(node);
22719             // inserts everything just before this node...
22720             while (node.childNodes.length) {
22721                 var cn = node.childNodes[0];
22722                 node.removeChild(cn);
22723                 node.parentNode.insertBefore(cn, node);
22724             }
22725             node.parentNode.removeChild(node);
22726             return;
22727         }
22728         
22729         if (!node.attributes || !node.attributes.length) {
22730             this.cleanUpChildren(node);
22731             return;
22732         }
22733         
22734         function cleanAttr(n,v)
22735         {
22736             
22737             if (v.match(/^\./) || v.match(/^\//)) {
22738                 return;
22739             }
22740             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22741                 return;
22742             }
22743             if (v.match(/^#/)) {
22744                 return;
22745             }
22746 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22747             node.removeAttribute(n);
22748             
22749         }
22750         
22751         var cwhite = this.cwhite;
22752         var cblack = this.cblack;
22753             
22754         function cleanStyle(n,v)
22755         {
22756             if (v.match(/expression/)) { //XSS?? should we even bother..
22757                 node.removeAttribute(n);
22758                 return;
22759             }
22760             
22761             var parts = v.split(/;/);
22762             var clean = [];
22763             
22764             Roo.each(parts, function(p) {
22765                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22766                 if (!p.length) {
22767                     return true;
22768                 }
22769                 var l = p.split(':').shift().replace(/\s+/g,'');
22770                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22771                 
22772                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22773 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22774                     //node.removeAttribute(n);
22775                     return true;
22776                 }
22777                 //Roo.log()
22778                 // only allow 'c whitelisted system attributes'
22779                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22780 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22781                     //node.removeAttribute(n);
22782                     return true;
22783                 }
22784                 
22785                 
22786                  
22787                 
22788                 clean.push(p);
22789                 return true;
22790             });
22791             if (clean.length) { 
22792                 node.setAttribute(n, clean.join(';'));
22793             } else {
22794                 node.removeAttribute(n);
22795             }
22796             
22797         }
22798         
22799         
22800         for (var i = node.attributes.length-1; i > -1 ; i--) {
22801             var a = node.attributes[i];
22802             //console.log(a);
22803             
22804             if (a.name.toLowerCase().substr(0,2)=='on')  {
22805                 node.removeAttribute(a.name);
22806                 continue;
22807             }
22808             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22809                 node.removeAttribute(a.name);
22810                 continue;
22811             }
22812             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22813                 cleanAttr(a.name,a.value); // fixme..
22814                 continue;
22815             }
22816             if (a.name == 'style') {
22817                 cleanStyle(a.name,a.value);
22818                 continue;
22819             }
22820             /// clean up MS crap..
22821             // tecnically this should be a list of valid class'es..
22822             
22823             
22824             if (a.name == 'class') {
22825                 if (a.value.match(/^Mso/)) {
22826                     node.className = '';
22827                 }
22828                 
22829                 if (a.value.match(/^body$/)) {
22830                     node.className = '';
22831                 }
22832                 continue;
22833             }
22834             
22835             // style cleanup!?
22836             // class cleanup?
22837             
22838         }
22839         
22840         
22841         this.cleanUpChildren(node);
22842         
22843         
22844     },
22845     
22846     /**
22847      * Clean up MS wordisms...
22848      */
22849     cleanWord : function(node)
22850     {
22851         
22852         
22853         if (!node) {
22854             this.cleanWord(this.doc.body);
22855             return;
22856         }
22857         if (node.nodeName == "#text") {
22858             // clean up silly Windows -- stuff?
22859             return; 
22860         }
22861         if (node.nodeName == "#comment") {
22862             node.parentNode.removeChild(node);
22863             // clean up silly Windows -- stuff?
22864             return; 
22865         }
22866         
22867         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22868             node.parentNode.removeChild(node);
22869             return;
22870         }
22871         
22872         // remove - but keep children..
22873         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22874             while (node.childNodes.length) {
22875                 var cn = node.childNodes[0];
22876                 node.removeChild(cn);
22877                 node.parentNode.insertBefore(cn, node);
22878             }
22879             node.parentNode.removeChild(node);
22880             this.iterateChildren(node, this.cleanWord);
22881             return;
22882         }
22883         // clean styles
22884         if (node.className.length) {
22885             
22886             var cn = node.className.split(/\W+/);
22887             var cna = [];
22888             Roo.each(cn, function(cls) {
22889                 if (cls.match(/Mso[a-zA-Z]+/)) {
22890                     return;
22891                 }
22892                 cna.push(cls);
22893             });
22894             node.className = cna.length ? cna.join(' ') : '';
22895             if (!cna.length) {
22896                 node.removeAttribute("class");
22897             }
22898         }
22899         
22900         if (node.hasAttribute("lang")) {
22901             node.removeAttribute("lang");
22902         }
22903         
22904         if (node.hasAttribute("style")) {
22905             
22906             var styles = node.getAttribute("style").split(";");
22907             var nstyle = [];
22908             Roo.each(styles, function(s) {
22909                 if (!s.match(/:/)) {
22910                     return;
22911                 }
22912                 var kv = s.split(":");
22913                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22914                     return;
22915                 }
22916                 // what ever is left... we allow.
22917                 nstyle.push(s);
22918             });
22919             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22920             if (!nstyle.length) {
22921                 node.removeAttribute('style');
22922             }
22923         }
22924         this.iterateChildren(node, this.cleanWord);
22925         
22926         
22927         
22928     },
22929     /**
22930      * iterateChildren of a Node, calling fn each time, using this as the scole..
22931      * @param {DomNode} node node to iterate children of.
22932      * @param {Function} fn method of this class to call on each item.
22933      */
22934     iterateChildren : function(node, fn)
22935     {
22936         if (!node.childNodes.length) {
22937                 return;
22938         }
22939         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22940            fn.call(this, node.childNodes[i])
22941         }
22942     },
22943     
22944     
22945     /**
22946      * cleanTableWidths.
22947      *
22948      * Quite often pasting from word etc.. results in tables with column and widths.
22949      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22950      *
22951      */
22952     cleanTableWidths : function(node)
22953     {
22954          
22955          
22956         if (!node) {
22957             this.cleanTableWidths(this.doc.body);
22958             return;
22959         }
22960         
22961         // ignore list...
22962         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22963             return; 
22964         }
22965         Roo.log(node.tagName);
22966         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22967             this.iterateChildren(node, this.cleanTableWidths);
22968             return;
22969         }
22970         if (node.hasAttribute('width')) {
22971             node.removeAttribute('width');
22972         }
22973         
22974          
22975         if (node.hasAttribute("style")) {
22976             // pretty basic...
22977             
22978             var styles = node.getAttribute("style").split(";");
22979             var nstyle = [];
22980             Roo.each(styles, function(s) {
22981                 if (!s.match(/:/)) {
22982                     return;
22983                 }
22984                 var kv = s.split(":");
22985                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22986                     return;
22987                 }
22988                 // what ever is left... we allow.
22989                 nstyle.push(s);
22990             });
22991             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22992             if (!nstyle.length) {
22993                 node.removeAttribute('style');
22994             }
22995         }
22996         
22997         this.iterateChildren(node, this.cleanTableWidths);
22998         
22999         
23000     },
23001     
23002     
23003     
23004     
23005     domToHTML : function(currentElement, depth, nopadtext) {
23006         
23007         depth = depth || 0;
23008         nopadtext = nopadtext || false;
23009     
23010         if (!currentElement) {
23011             return this.domToHTML(this.doc.body);
23012         }
23013         
23014         //Roo.log(currentElement);
23015         var j;
23016         var allText = false;
23017         var nodeName = currentElement.nodeName;
23018         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23019         
23020         if  (nodeName == '#text') {
23021             
23022             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23023         }
23024         
23025         
23026         var ret = '';
23027         if (nodeName != 'BODY') {
23028              
23029             var i = 0;
23030             // Prints the node tagName, such as <A>, <IMG>, etc
23031             if (tagName) {
23032                 var attr = [];
23033                 for(i = 0; i < currentElement.attributes.length;i++) {
23034                     // quoting?
23035                     var aname = currentElement.attributes.item(i).name;
23036                     if (!currentElement.attributes.item(i).value.length) {
23037                         continue;
23038                     }
23039                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23040                 }
23041                 
23042                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23043             } 
23044             else {
23045                 
23046                 // eack
23047             }
23048         } else {
23049             tagName = false;
23050         }
23051         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23052             return ret;
23053         }
23054         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23055             nopadtext = true;
23056         }
23057         
23058         
23059         // Traverse the tree
23060         i = 0;
23061         var currentElementChild = currentElement.childNodes.item(i);
23062         var allText = true;
23063         var innerHTML  = '';
23064         lastnode = '';
23065         while (currentElementChild) {
23066             // Formatting code (indent the tree so it looks nice on the screen)
23067             var nopad = nopadtext;
23068             if (lastnode == 'SPAN') {
23069                 nopad  = true;
23070             }
23071             // text
23072             if  (currentElementChild.nodeName == '#text') {
23073                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23074                 toadd = nopadtext ? toadd : toadd.trim();
23075                 if (!nopad && toadd.length > 80) {
23076                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23077                 }
23078                 innerHTML  += toadd;
23079                 
23080                 i++;
23081                 currentElementChild = currentElement.childNodes.item(i);
23082                 lastNode = '';
23083                 continue;
23084             }
23085             allText = false;
23086             
23087             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23088                 
23089             // Recursively traverse the tree structure of the child node
23090             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23091             lastnode = currentElementChild.nodeName;
23092             i++;
23093             currentElementChild=currentElement.childNodes.item(i);
23094         }
23095         
23096         ret += innerHTML;
23097         
23098         if (!allText) {
23099                 // The remaining code is mostly for formatting the tree
23100             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23101         }
23102         
23103         
23104         if (tagName) {
23105             ret+= "</"+tagName+">";
23106         }
23107         return ret;
23108         
23109     },
23110         
23111     applyBlacklists : function()
23112     {
23113         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23114         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23115         
23116         this.white = [];
23117         this.black = [];
23118         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23119             if (b.indexOf(tag) > -1) {
23120                 return;
23121             }
23122             this.white.push(tag);
23123             
23124         }, this);
23125         
23126         Roo.each(w, function(tag) {
23127             if (b.indexOf(tag) > -1) {
23128                 return;
23129             }
23130             if (this.white.indexOf(tag) > -1) {
23131                 return;
23132             }
23133             this.white.push(tag);
23134             
23135         }, this);
23136         
23137         
23138         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23139             if (w.indexOf(tag) > -1) {
23140                 return;
23141             }
23142             this.black.push(tag);
23143             
23144         }, this);
23145         
23146         Roo.each(b, function(tag) {
23147             if (w.indexOf(tag) > -1) {
23148                 return;
23149             }
23150             if (this.black.indexOf(tag) > -1) {
23151                 return;
23152             }
23153             this.black.push(tag);
23154             
23155         }, this);
23156         
23157         
23158         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23159         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23160         
23161         this.cwhite = [];
23162         this.cblack = [];
23163         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23164             if (b.indexOf(tag) > -1) {
23165                 return;
23166             }
23167             this.cwhite.push(tag);
23168             
23169         }, this);
23170         
23171         Roo.each(w, function(tag) {
23172             if (b.indexOf(tag) > -1) {
23173                 return;
23174             }
23175             if (this.cwhite.indexOf(tag) > -1) {
23176                 return;
23177             }
23178             this.cwhite.push(tag);
23179             
23180         }, this);
23181         
23182         
23183         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23184             if (w.indexOf(tag) > -1) {
23185                 return;
23186             }
23187             this.cblack.push(tag);
23188             
23189         }, this);
23190         
23191         Roo.each(b, function(tag) {
23192             if (w.indexOf(tag) > -1) {
23193                 return;
23194             }
23195             if (this.cblack.indexOf(tag) > -1) {
23196                 return;
23197             }
23198             this.cblack.push(tag);
23199             
23200         }, this);
23201     },
23202     
23203     setStylesheets : function(stylesheets)
23204     {
23205         if(typeof(stylesheets) == 'string'){
23206             Roo.get(this.iframe.contentDocument.head).createChild({
23207                 tag : 'link',
23208                 rel : 'stylesheet',
23209                 type : 'text/css',
23210                 href : stylesheets
23211             });
23212             
23213             return;
23214         }
23215         var _this = this;
23216      
23217         Roo.each(stylesheets, function(s) {
23218             if(!s.length){
23219                 return;
23220             }
23221             
23222             Roo.get(_this.iframe.contentDocument.head).createChild({
23223                 tag : 'link',
23224                 rel : 'stylesheet',
23225                 type : 'text/css',
23226                 href : s
23227             });
23228         });
23229
23230         
23231     },
23232     
23233     removeStylesheets : function()
23234     {
23235         var _this = this;
23236         
23237         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23238             s.remove();
23239         });
23240     },
23241     
23242     setStyle : function(style)
23243     {
23244         Roo.get(this.iframe.contentDocument.head).createChild({
23245             tag : 'style',
23246             type : 'text/css',
23247             html : style
23248         });
23249
23250         return;
23251     }
23252     
23253     // hide stuff that is not compatible
23254     /**
23255      * @event blur
23256      * @hide
23257      */
23258     /**
23259      * @event change
23260      * @hide
23261      */
23262     /**
23263      * @event focus
23264      * @hide
23265      */
23266     /**
23267      * @event specialkey
23268      * @hide
23269      */
23270     /**
23271      * @cfg {String} fieldClass @hide
23272      */
23273     /**
23274      * @cfg {String} focusClass @hide
23275      */
23276     /**
23277      * @cfg {String} autoCreate @hide
23278      */
23279     /**
23280      * @cfg {String} inputType @hide
23281      */
23282     /**
23283      * @cfg {String} invalidClass @hide
23284      */
23285     /**
23286      * @cfg {String} invalidText @hide
23287      */
23288     /**
23289      * @cfg {String} msgFx @hide
23290      */
23291     /**
23292      * @cfg {String} validateOnBlur @hide
23293      */
23294 });
23295
23296 Roo.HtmlEditorCore.white = [
23297         'area', 'br', 'img', 'input', 'hr', 'wbr',
23298         
23299        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23300        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23301        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23302        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23303        'table',   'ul',         'xmp', 
23304        
23305        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23306       'thead',   'tr', 
23307      
23308       'dir', 'menu', 'ol', 'ul', 'dl',
23309        
23310       'embed',  'object'
23311 ];
23312
23313
23314 Roo.HtmlEditorCore.black = [
23315     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23316         'applet', // 
23317         'base',   'basefont', 'bgsound', 'blink',  'body', 
23318         'frame',  'frameset', 'head',    'html',   'ilayer', 
23319         'iframe', 'layer',  'link',     'meta',    'object',   
23320         'script', 'style' ,'title',  'xml' // clean later..
23321 ];
23322 Roo.HtmlEditorCore.clean = [
23323     'script', 'style', 'title', 'xml'
23324 ];
23325 Roo.HtmlEditorCore.remove = [
23326     'font'
23327 ];
23328 // attributes..
23329
23330 Roo.HtmlEditorCore.ablack = [
23331     'on'
23332 ];
23333     
23334 Roo.HtmlEditorCore.aclean = [ 
23335     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23336 ];
23337
23338 // protocols..
23339 Roo.HtmlEditorCore.pwhite= [
23340         'http',  'https',  'mailto'
23341 ];
23342
23343 // white listed style attributes.
23344 Roo.HtmlEditorCore.cwhite= [
23345       //  'text-align', /// default is to allow most things..
23346       
23347          
23348 //        'font-size'//??
23349 ];
23350
23351 // black listed style attributes.
23352 Roo.HtmlEditorCore.cblack= [
23353       //  'font-size' -- this can be set by the project 
23354 ];
23355
23356
23357 Roo.HtmlEditorCore.swapCodes   =[ 
23358     [    8211, "--" ], 
23359     [    8212, "--" ], 
23360     [    8216,  "'" ],  
23361     [    8217, "'" ],  
23362     [    8220, '"' ],  
23363     [    8221, '"' ],  
23364     [    8226, "*" ],  
23365     [    8230, "..." ]
23366 ]; 
23367
23368     /*
23369  * - LGPL
23370  *
23371  * HtmlEditor
23372  * 
23373  */
23374
23375 /**
23376  * @class Roo.bootstrap.HtmlEditor
23377  * @extends Roo.bootstrap.TextArea
23378  * Bootstrap HtmlEditor class
23379
23380  * @constructor
23381  * Create a new HtmlEditor
23382  * @param {Object} config The config object
23383  */
23384
23385 Roo.bootstrap.HtmlEditor = function(config){
23386     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23387     if (!this.toolbars) {
23388         this.toolbars = [];
23389     }
23390     
23391     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23392     this.addEvents({
23393             /**
23394              * @event initialize
23395              * Fires when the editor is fully initialized (including the iframe)
23396              * @param {HtmlEditor} this
23397              */
23398             initialize: true,
23399             /**
23400              * @event activate
23401              * Fires when the editor is first receives the focus. Any insertion must wait
23402              * until after this event.
23403              * @param {HtmlEditor} this
23404              */
23405             activate: true,
23406              /**
23407              * @event beforesync
23408              * Fires before the textarea is updated with content from the editor iframe. Return false
23409              * to cancel the sync.
23410              * @param {HtmlEditor} this
23411              * @param {String} html
23412              */
23413             beforesync: true,
23414              /**
23415              * @event beforepush
23416              * Fires before the iframe editor is updated with content from the textarea. Return false
23417              * to cancel the push.
23418              * @param {HtmlEditor} this
23419              * @param {String} html
23420              */
23421             beforepush: true,
23422              /**
23423              * @event sync
23424              * Fires when the textarea is updated with content from the editor iframe.
23425              * @param {HtmlEditor} this
23426              * @param {String} html
23427              */
23428             sync: true,
23429              /**
23430              * @event push
23431              * Fires when the iframe editor is updated with content from the textarea.
23432              * @param {HtmlEditor} this
23433              * @param {String} html
23434              */
23435             push: true,
23436              /**
23437              * @event editmodechange
23438              * Fires when the editor switches edit modes
23439              * @param {HtmlEditor} this
23440              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23441              */
23442             editmodechange: true,
23443             /**
23444              * @event editorevent
23445              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23446              * @param {HtmlEditor} this
23447              */
23448             editorevent: true,
23449             /**
23450              * @event firstfocus
23451              * Fires when on first focus - needed by toolbars..
23452              * @param {HtmlEditor} this
23453              */
23454             firstfocus: true,
23455             /**
23456              * @event autosave
23457              * Auto save the htmlEditor value as a file into Events
23458              * @param {HtmlEditor} this
23459              */
23460             autosave: true,
23461             /**
23462              * @event savedpreview
23463              * preview the saved version of htmlEditor
23464              * @param {HtmlEditor} this
23465              */
23466             savedpreview: true
23467         });
23468 };
23469
23470
23471 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23472     
23473     
23474       /**
23475      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23476      */
23477     toolbars : false,
23478     
23479      /**
23480     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23481     */
23482     btns : [],
23483    
23484      /**
23485      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23486      *                        Roo.resizable.
23487      */
23488     resizable : false,
23489      /**
23490      * @cfg {Number} height (in pixels)
23491      */   
23492     height: 300,
23493    /**
23494      * @cfg {Number} width (in pixels)
23495      */   
23496     width: false,
23497     
23498     /**
23499      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23500      * 
23501      */
23502     stylesheets: false,
23503     
23504     // id of frame..
23505     frameId: false,
23506     
23507     // private properties
23508     validationEvent : false,
23509     deferHeight: true,
23510     initialized : false,
23511     activated : false,
23512     
23513     onFocus : Roo.emptyFn,
23514     iframePad:3,
23515     hideMode:'offsets',
23516     
23517     tbContainer : false,
23518     
23519     bodyCls : '',
23520     
23521     toolbarContainer :function() {
23522         return this.wrap.select('.x-html-editor-tb',true).first();
23523     },
23524
23525     /**
23526      * Protected method that will not generally be called directly. It
23527      * is called when the editor creates its toolbar. Override this method if you need to
23528      * add custom toolbar buttons.
23529      * @param {HtmlEditor} editor
23530      */
23531     createToolbar : function(){
23532         Roo.log('renewing');
23533         Roo.log("create toolbars");
23534         
23535         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23536         this.toolbars[0].render(this.toolbarContainer());
23537         
23538         return;
23539         
23540 //        if (!editor.toolbars || !editor.toolbars.length) {
23541 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23542 //        }
23543 //        
23544 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23545 //            editor.toolbars[i] = Roo.factory(
23546 //                    typeof(editor.toolbars[i]) == 'string' ?
23547 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23548 //                Roo.bootstrap.HtmlEditor);
23549 //            editor.toolbars[i].init(editor);
23550 //        }
23551     },
23552
23553      
23554     // private
23555     onRender : function(ct, position)
23556     {
23557        // Roo.log("Call onRender: " + this.xtype);
23558         var _t = this;
23559         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23560       
23561         this.wrap = this.inputEl().wrap({
23562             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23563         });
23564         
23565         this.editorcore.onRender(ct, position);
23566          
23567         if (this.resizable) {
23568             this.resizeEl = new Roo.Resizable(this.wrap, {
23569                 pinned : true,
23570                 wrap: true,
23571                 dynamic : true,
23572                 minHeight : this.height,
23573                 height: this.height,
23574                 handles : this.resizable,
23575                 width: this.width,
23576                 listeners : {
23577                     resize : function(r, w, h) {
23578                         _t.onResize(w,h); // -something
23579                     }
23580                 }
23581             });
23582             
23583         }
23584         this.createToolbar(this);
23585        
23586         
23587         if(!this.width && this.resizable){
23588             this.setSize(this.wrap.getSize());
23589         }
23590         if (this.resizeEl) {
23591             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23592             // should trigger onReize..
23593         }
23594         
23595     },
23596
23597     // private
23598     onResize : function(w, h)
23599     {
23600         Roo.log('resize: ' +w + ',' + h );
23601         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23602         var ew = false;
23603         var eh = false;
23604         
23605         if(this.inputEl() ){
23606             if(typeof w == 'number'){
23607                 var aw = w - this.wrap.getFrameWidth('lr');
23608                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23609                 ew = aw;
23610             }
23611             if(typeof h == 'number'){
23612                  var tbh = -11;  // fixme it needs to tool bar size!
23613                 for (var i =0; i < this.toolbars.length;i++) {
23614                     // fixme - ask toolbars for heights?
23615                     tbh += this.toolbars[i].el.getHeight();
23616                     //if (this.toolbars[i].footer) {
23617                     //    tbh += this.toolbars[i].footer.el.getHeight();
23618                     //}
23619                 }
23620               
23621                 
23622                 
23623                 
23624                 
23625                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23626                 ah -= 5; // knock a few pixes off for look..
23627                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23628                 var eh = ah;
23629             }
23630         }
23631         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23632         this.editorcore.onResize(ew,eh);
23633         
23634     },
23635
23636     /**
23637      * Toggles the editor between standard and source edit mode.
23638      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23639      */
23640     toggleSourceEdit : function(sourceEditMode)
23641     {
23642         this.editorcore.toggleSourceEdit(sourceEditMode);
23643         
23644         if(this.editorcore.sourceEditMode){
23645             Roo.log('editor - showing textarea');
23646             
23647 //            Roo.log('in');
23648 //            Roo.log(this.syncValue());
23649             this.syncValue();
23650             this.inputEl().removeClass(['hide', 'x-hidden']);
23651             this.inputEl().dom.removeAttribute('tabIndex');
23652             this.inputEl().focus();
23653         }else{
23654             Roo.log('editor - hiding textarea');
23655 //            Roo.log('out')
23656 //            Roo.log(this.pushValue()); 
23657             this.pushValue();
23658             
23659             this.inputEl().addClass(['hide', 'x-hidden']);
23660             this.inputEl().dom.setAttribute('tabIndex', -1);
23661             //this.deferFocus();
23662         }
23663          
23664         if(this.resizable){
23665             this.setSize(this.wrap.getSize());
23666         }
23667         
23668         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23669     },
23670  
23671     // private (for BoxComponent)
23672     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23673
23674     // private (for BoxComponent)
23675     getResizeEl : function(){
23676         return this.wrap;
23677     },
23678
23679     // private (for BoxComponent)
23680     getPositionEl : function(){
23681         return this.wrap;
23682     },
23683
23684     // private
23685     initEvents : function(){
23686         this.originalValue = this.getValue();
23687     },
23688
23689 //    /**
23690 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23691 //     * @method
23692 //     */
23693 //    markInvalid : Roo.emptyFn,
23694 //    /**
23695 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23696 //     * @method
23697 //     */
23698 //    clearInvalid : Roo.emptyFn,
23699
23700     setValue : function(v){
23701         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23702         this.editorcore.pushValue();
23703     },
23704
23705      
23706     // private
23707     deferFocus : function(){
23708         this.focus.defer(10, this);
23709     },
23710
23711     // doc'ed in Field
23712     focus : function(){
23713         this.editorcore.focus();
23714         
23715     },
23716       
23717
23718     // private
23719     onDestroy : function(){
23720         
23721         
23722         
23723         if(this.rendered){
23724             
23725             for (var i =0; i < this.toolbars.length;i++) {
23726                 // fixme - ask toolbars for heights?
23727                 this.toolbars[i].onDestroy();
23728             }
23729             
23730             this.wrap.dom.innerHTML = '';
23731             this.wrap.remove();
23732         }
23733     },
23734
23735     // private
23736     onFirstFocus : function(){
23737         //Roo.log("onFirstFocus");
23738         this.editorcore.onFirstFocus();
23739          for (var i =0; i < this.toolbars.length;i++) {
23740             this.toolbars[i].onFirstFocus();
23741         }
23742         
23743     },
23744     
23745     // private
23746     syncValue : function()
23747     {   
23748         this.editorcore.syncValue();
23749     },
23750     
23751     pushValue : function()
23752     {   
23753         this.editorcore.pushValue();
23754     }
23755      
23756     
23757     // hide stuff that is not compatible
23758     /**
23759      * @event blur
23760      * @hide
23761      */
23762     /**
23763      * @event change
23764      * @hide
23765      */
23766     /**
23767      * @event focus
23768      * @hide
23769      */
23770     /**
23771      * @event specialkey
23772      * @hide
23773      */
23774     /**
23775      * @cfg {String} fieldClass @hide
23776      */
23777     /**
23778      * @cfg {String} focusClass @hide
23779      */
23780     /**
23781      * @cfg {String} autoCreate @hide
23782      */
23783     /**
23784      * @cfg {String} inputType @hide
23785      */
23786     /**
23787      * @cfg {String} invalidClass @hide
23788      */
23789     /**
23790      * @cfg {String} invalidText @hide
23791      */
23792     /**
23793      * @cfg {String} msgFx @hide
23794      */
23795     /**
23796      * @cfg {String} validateOnBlur @hide
23797      */
23798 });
23799  
23800     
23801    
23802    
23803    
23804       
23805 Roo.namespace('Roo.bootstrap.htmleditor');
23806 /**
23807  * @class Roo.bootstrap.HtmlEditorToolbar1
23808  * Basic Toolbar
23809  * 
23810  * Usage:
23811  *
23812  new Roo.bootstrap.HtmlEditor({
23813     ....
23814     toolbars : [
23815         new Roo.bootstrap.HtmlEditorToolbar1({
23816             disable : { fonts: 1 , format: 1, ..., ... , ...],
23817             btns : [ .... ]
23818         })
23819     }
23820      
23821  * 
23822  * @cfg {Object} disable List of elements to disable..
23823  * @cfg {Array} btns List of additional buttons.
23824  * 
23825  * 
23826  * NEEDS Extra CSS? 
23827  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23828  */
23829  
23830 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23831 {
23832     
23833     Roo.apply(this, config);
23834     
23835     // default disabled, based on 'good practice'..
23836     this.disable = this.disable || {};
23837     Roo.applyIf(this.disable, {
23838         fontSize : true,
23839         colors : true,
23840         specialElements : true
23841     });
23842     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23843     
23844     this.editor = config.editor;
23845     this.editorcore = config.editor.editorcore;
23846     
23847     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23848     
23849     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23850     // dont call parent... till later.
23851 }
23852 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23853      
23854     bar : true,
23855     
23856     editor : false,
23857     editorcore : false,
23858     
23859     
23860     formats : [
23861         "p" ,  
23862         "h1","h2","h3","h4","h5","h6", 
23863         "pre", "code", 
23864         "abbr", "acronym", "address", "cite", "samp", "var",
23865         'div','span'
23866     ],
23867     
23868     onRender : function(ct, position)
23869     {
23870        // Roo.log("Call onRender: " + this.xtype);
23871         
23872        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23873        Roo.log(this.el);
23874        this.el.dom.style.marginBottom = '0';
23875        var _this = this;
23876        var editorcore = this.editorcore;
23877        var editor= this.editor;
23878        
23879        var children = [];
23880        var btn = function(id,cmd , toggle, handler, html){
23881        
23882             var  event = toggle ? 'toggle' : 'click';
23883        
23884             var a = {
23885                 size : 'sm',
23886                 xtype: 'Button',
23887                 xns: Roo.bootstrap,
23888                 glyphicon : id,
23889                 cmd : id || cmd,
23890                 enableToggle:toggle !== false,
23891                 html : html || '',
23892                 pressed : toggle ? false : null,
23893                 listeners : {}
23894             };
23895             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23896                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23897             };
23898             children.push(a);
23899             return a;
23900        }
23901        
23902     //    var cb_box = function...
23903         
23904         var style = {
23905                 xtype: 'Button',
23906                 size : 'sm',
23907                 xns: Roo.bootstrap,
23908                 glyphicon : 'font',
23909                 //html : 'submit'
23910                 menu : {
23911                     xtype: 'Menu',
23912                     xns: Roo.bootstrap,
23913                     items:  []
23914                 }
23915         };
23916         Roo.each(this.formats, function(f) {
23917             style.menu.items.push({
23918                 xtype :'MenuItem',
23919                 xns: Roo.bootstrap,
23920                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23921                 tagname : f,
23922                 listeners : {
23923                     click : function()
23924                     {
23925                         editorcore.insertTag(this.tagname);
23926                         editor.focus();
23927                     }
23928                 }
23929                 
23930             });
23931         });
23932         children.push(style);   
23933         
23934         btn('bold',false,true);
23935         btn('italic',false,true);
23936         btn('align-left', 'justifyleft',true);
23937         btn('align-center', 'justifycenter',true);
23938         btn('align-right' , 'justifyright',true);
23939         btn('link', false, false, function(btn) {
23940             //Roo.log("create link?");
23941             var url = prompt(this.createLinkText, this.defaultLinkValue);
23942             if(url && url != 'http:/'+'/'){
23943                 this.editorcore.relayCmd('createlink', url);
23944             }
23945         }),
23946         btn('list','insertunorderedlist',true);
23947         btn('pencil', false,true, function(btn){
23948                 Roo.log(this);
23949                 this.toggleSourceEdit(btn.pressed);
23950         });
23951         
23952         if (this.editor.btns.length > 0) {
23953             for (var i = 0; i<this.editor.btns.length; i++) {
23954                 children.push(this.editor.btns[i]);
23955             }
23956         }
23957         
23958         /*
23959         var cog = {
23960                 xtype: 'Button',
23961                 size : 'sm',
23962                 xns: Roo.bootstrap,
23963                 glyphicon : 'cog',
23964                 //html : 'submit'
23965                 menu : {
23966                     xtype: 'Menu',
23967                     xns: Roo.bootstrap,
23968                     items:  []
23969                 }
23970         };
23971         
23972         cog.menu.items.push({
23973             xtype :'MenuItem',
23974             xns: Roo.bootstrap,
23975             html : Clean styles,
23976             tagname : f,
23977             listeners : {
23978                 click : function()
23979                 {
23980                     editorcore.insertTag(this.tagname);
23981                     editor.focus();
23982                 }
23983             }
23984             
23985         });
23986        */
23987         
23988          
23989        this.xtype = 'NavSimplebar';
23990         
23991         for(var i=0;i< children.length;i++) {
23992             
23993             this.buttons.add(this.addxtypeChild(children[i]));
23994             
23995         }
23996         
23997         editor.on('editorevent', this.updateToolbar, this);
23998     },
23999     onBtnClick : function(id)
24000     {
24001        this.editorcore.relayCmd(id);
24002        this.editorcore.focus();
24003     },
24004     
24005     /**
24006      * Protected method that will not generally be called directly. It triggers
24007      * a toolbar update by reading the markup state of the current selection in the editor.
24008      */
24009     updateToolbar: function(){
24010
24011         if(!this.editorcore.activated){
24012             this.editor.onFirstFocus(); // is this neeed?
24013             return;
24014         }
24015
24016         var btns = this.buttons; 
24017         var doc = this.editorcore.doc;
24018         btns.get('bold').setActive(doc.queryCommandState('bold'));
24019         btns.get('italic').setActive(doc.queryCommandState('italic'));
24020         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24021         
24022         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24023         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24024         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24025         
24026         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24027         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24028          /*
24029         
24030         var ans = this.editorcore.getAllAncestors();
24031         if (this.formatCombo) {
24032             
24033             
24034             var store = this.formatCombo.store;
24035             this.formatCombo.setValue("");
24036             for (var i =0; i < ans.length;i++) {
24037                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24038                     // select it..
24039                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24040                     break;
24041                 }
24042             }
24043         }
24044         
24045         
24046         
24047         // hides menus... - so this cant be on a menu...
24048         Roo.bootstrap.MenuMgr.hideAll();
24049         */
24050         Roo.bootstrap.MenuMgr.hideAll();
24051         //this.editorsyncValue();
24052     },
24053     onFirstFocus: function() {
24054         this.buttons.each(function(item){
24055            item.enable();
24056         });
24057     },
24058     toggleSourceEdit : function(sourceEditMode){
24059         
24060           
24061         if(sourceEditMode){
24062             Roo.log("disabling buttons");
24063            this.buttons.each( function(item){
24064                 if(item.cmd != 'pencil'){
24065                     item.disable();
24066                 }
24067             });
24068           
24069         }else{
24070             Roo.log("enabling buttons");
24071             if(this.editorcore.initialized){
24072                 this.buttons.each( function(item){
24073                     item.enable();
24074                 });
24075             }
24076             
24077         }
24078         Roo.log("calling toggole on editor");
24079         // tell the editor that it's been pressed..
24080         this.editor.toggleSourceEdit(sourceEditMode);
24081        
24082     }
24083 });
24084
24085
24086
24087
24088
24089 /**
24090  * @class Roo.bootstrap.Table.AbstractSelectionModel
24091  * @extends Roo.util.Observable
24092  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24093  * implemented by descendant classes.  This class should not be directly instantiated.
24094  * @constructor
24095  */
24096 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24097     this.locked = false;
24098     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24099 };
24100
24101
24102 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24103     /** @ignore Called by the grid automatically. Do not call directly. */
24104     init : function(grid){
24105         this.grid = grid;
24106         this.initEvents();
24107     },
24108
24109     /**
24110      * Locks the selections.
24111      */
24112     lock : function(){
24113         this.locked = true;
24114     },
24115
24116     /**
24117      * Unlocks the selections.
24118      */
24119     unlock : function(){
24120         this.locked = false;
24121     },
24122
24123     /**
24124      * Returns true if the selections are locked.
24125      * @return {Boolean}
24126      */
24127     isLocked : function(){
24128         return this.locked;
24129     }
24130 });
24131 /**
24132  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24133  * @class Roo.bootstrap.Table.RowSelectionModel
24134  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24135  * It supports multiple selections and keyboard selection/navigation. 
24136  * @constructor
24137  * @param {Object} config
24138  */
24139
24140 Roo.bootstrap.Table.RowSelectionModel = function(config){
24141     Roo.apply(this, config);
24142     this.selections = new Roo.util.MixedCollection(false, function(o){
24143         return o.id;
24144     });
24145
24146     this.last = false;
24147     this.lastActive = false;
24148
24149     this.addEvents({
24150         /**
24151              * @event selectionchange
24152              * Fires when the selection changes
24153              * @param {SelectionModel} this
24154              */
24155             "selectionchange" : true,
24156         /**
24157              * @event afterselectionchange
24158              * Fires after the selection changes (eg. by key press or clicking)
24159              * @param {SelectionModel} this
24160              */
24161             "afterselectionchange" : true,
24162         /**
24163              * @event beforerowselect
24164              * Fires when a row is selected being selected, return false to cancel.
24165              * @param {SelectionModel} this
24166              * @param {Number} rowIndex The selected index
24167              * @param {Boolean} keepExisting False if other selections will be cleared
24168              */
24169             "beforerowselect" : true,
24170         /**
24171              * @event rowselect
24172              * Fires when a row is selected.
24173              * @param {SelectionModel} this
24174              * @param {Number} rowIndex The selected index
24175              * @param {Roo.data.Record} r The record
24176              */
24177             "rowselect" : true,
24178         /**
24179              * @event rowdeselect
24180              * Fires when a row is deselected.
24181              * @param {SelectionModel} this
24182              * @param {Number} rowIndex The selected index
24183              */
24184         "rowdeselect" : true
24185     });
24186     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24187     this.locked = false;
24188  };
24189
24190 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24191     /**
24192      * @cfg {Boolean} singleSelect
24193      * True to allow selection of only one row at a time (defaults to false)
24194      */
24195     singleSelect : false,
24196
24197     // private
24198     initEvents : function()
24199     {
24200
24201         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24202         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24203         //}else{ // allow click to work like normal
24204          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24205         //}
24206         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24207         this.grid.on("rowclick", this.handleMouseDown, this);
24208         
24209         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24210             "up" : function(e){
24211                 if(!e.shiftKey){
24212                     this.selectPrevious(e.shiftKey);
24213                 }else if(this.last !== false && this.lastActive !== false){
24214                     var last = this.last;
24215                     this.selectRange(this.last,  this.lastActive-1);
24216                     this.grid.getView().focusRow(this.lastActive);
24217                     if(last !== false){
24218                         this.last = last;
24219                     }
24220                 }else{
24221                     this.selectFirstRow();
24222                 }
24223                 this.fireEvent("afterselectionchange", this);
24224             },
24225             "down" : function(e){
24226                 if(!e.shiftKey){
24227                     this.selectNext(e.shiftKey);
24228                 }else if(this.last !== false && this.lastActive !== false){
24229                     var last = this.last;
24230                     this.selectRange(this.last,  this.lastActive+1);
24231                     this.grid.getView().focusRow(this.lastActive);
24232                     if(last !== false){
24233                         this.last = last;
24234                     }
24235                 }else{
24236                     this.selectFirstRow();
24237                 }
24238                 this.fireEvent("afterselectionchange", this);
24239             },
24240             scope: this
24241         });
24242         this.grid.store.on('load', function(){
24243             this.selections.clear();
24244         },this);
24245         /*
24246         var view = this.grid.view;
24247         view.on("refresh", this.onRefresh, this);
24248         view.on("rowupdated", this.onRowUpdated, this);
24249         view.on("rowremoved", this.onRemove, this);
24250         */
24251     },
24252
24253     // private
24254     onRefresh : function()
24255     {
24256         var ds = this.grid.store, i, v = this.grid.view;
24257         var s = this.selections;
24258         s.each(function(r){
24259             if((i = ds.indexOfId(r.id)) != -1){
24260                 v.onRowSelect(i);
24261             }else{
24262                 s.remove(r);
24263             }
24264         });
24265     },
24266
24267     // private
24268     onRemove : function(v, index, r){
24269         this.selections.remove(r);
24270     },
24271
24272     // private
24273     onRowUpdated : function(v, index, r){
24274         if(this.isSelected(r)){
24275             v.onRowSelect(index);
24276         }
24277     },
24278
24279     /**
24280      * Select records.
24281      * @param {Array} records The records to select
24282      * @param {Boolean} keepExisting (optional) True to keep existing selections
24283      */
24284     selectRecords : function(records, keepExisting)
24285     {
24286         if(!keepExisting){
24287             this.clearSelections();
24288         }
24289             var ds = this.grid.store;
24290         for(var i = 0, len = records.length; i < len; i++){
24291             this.selectRow(ds.indexOf(records[i]), true);
24292         }
24293     },
24294
24295     /**
24296      * Gets the number of selected rows.
24297      * @return {Number}
24298      */
24299     getCount : function(){
24300         return this.selections.length;
24301     },
24302
24303     /**
24304      * Selects the first row in the grid.
24305      */
24306     selectFirstRow : function(){
24307         this.selectRow(0);
24308     },
24309
24310     /**
24311      * Select the last row.
24312      * @param {Boolean} keepExisting (optional) True to keep existing selections
24313      */
24314     selectLastRow : function(keepExisting){
24315         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24316         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24317     },
24318
24319     /**
24320      * Selects the row immediately following the last selected row.
24321      * @param {Boolean} keepExisting (optional) True to keep existing selections
24322      */
24323     selectNext : function(keepExisting)
24324     {
24325             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24326             this.selectRow(this.last+1, keepExisting);
24327             this.grid.getView().focusRow(this.last);
24328         }
24329     },
24330
24331     /**
24332      * Selects the row that precedes the last selected row.
24333      * @param {Boolean} keepExisting (optional) True to keep existing selections
24334      */
24335     selectPrevious : function(keepExisting){
24336         if(this.last){
24337             this.selectRow(this.last-1, keepExisting);
24338             this.grid.getView().focusRow(this.last);
24339         }
24340     },
24341
24342     /**
24343      * Returns the selected records
24344      * @return {Array} Array of selected records
24345      */
24346     getSelections : function(){
24347         return [].concat(this.selections.items);
24348     },
24349
24350     /**
24351      * Returns the first selected record.
24352      * @return {Record}
24353      */
24354     getSelected : function(){
24355         return this.selections.itemAt(0);
24356     },
24357
24358
24359     /**
24360      * Clears all selections.
24361      */
24362     clearSelections : function(fast)
24363     {
24364         if(this.locked) {
24365             return;
24366         }
24367         if(fast !== true){
24368                 var ds = this.grid.store;
24369             var s = this.selections;
24370             s.each(function(r){
24371                 this.deselectRow(ds.indexOfId(r.id));
24372             }, this);
24373             s.clear();
24374         }else{
24375             this.selections.clear();
24376         }
24377         this.last = false;
24378     },
24379
24380
24381     /**
24382      * Selects all rows.
24383      */
24384     selectAll : function(){
24385         if(this.locked) {
24386             return;
24387         }
24388         this.selections.clear();
24389         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24390             this.selectRow(i, true);
24391         }
24392     },
24393
24394     /**
24395      * Returns True if there is a selection.
24396      * @return {Boolean}
24397      */
24398     hasSelection : function(){
24399         return this.selections.length > 0;
24400     },
24401
24402     /**
24403      * Returns True if the specified row is selected.
24404      * @param {Number/Record} record The record or index of the record to check
24405      * @return {Boolean}
24406      */
24407     isSelected : function(index){
24408             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24409         return (r && this.selections.key(r.id) ? true : false);
24410     },
24411
24412     /**
24413      * Returns True if the specified record id is selected.
24414      * @param {String} id The id of record to check
24415      * @return {Boolean}
24416      */
24417     isIdSelected : function(id){
24418         return (this.selections.key(id) ? true : false);
24419     },
24420
24421
24422     // private
24423     handleMouseDBClick : function(e, t){
24424         
24425     },
24426     // private
24427     handleMouseDown : function(e, t)
24428     {
24429             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24430         if(this.isLocked() || rowIndex < 0 ){
24431             return;
24432         };
24433         if(e.shiftKey && this.last !== false){
24434             var last = this.last;
24435             this.selectRange(last, rowIndex, e.ctrlKey);
24436             this.last = last; // reset the last
24437             t.focus();
24438     
24439         }else{
24440             var isSelected = this.isSelected(rowIndex);
24441             //Roo.log("select row:" + rowIndex);
24442             if(isSelected){
24443                 this.deselectRow(rowIndex);
24444             } else {
24445                         this.selectRow(rowIndex, true);
24446             }
24447     
24448             /*
24449                 if(e.button !== 0 && isSelected){
24450                 alert('rowIndex 2: ' + rowIndex);
24451                     view.focusRow(rowIndex);
24452                 }else if(e.ctrlKey && isSelected){
24453                     this.deselectRow(rowIndex);
24454                 }else if(!isSelected){
24455                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24456                     view.focusRow(rowIndex);
24457                 }
24458             */
24459         }
24460         this.fireEvent("afterselectionchange", this);
24461     },
24462     // private
24463     handleDragableRowClick :  function(grid, rowIndex, e) 
24464     {
24465         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24466             this.selectRow(rowIndex, false);
24467             grid.view.focusRow(rowIndex);
24468              this.fireEvent("afterselectionchange", this);
24469         }
24470     },
24471     
24472     /**
24473      * Selects multiple rows.
24474      * @param {Array} rows Array of the indexes of the row to select
24475      * @param {Boolean} keepExisting (optional) True to keep existing selections
24476      */
24477     selectRows : function(rows, keepExisting){
24478         if(!keepExisting){
24479             this.clearSelections();
24480         }
24481         for(var i = 0, len = rows.length; i < len; i++){
24482             this.selectRow(rows[i], true);
24483         }
24484     },
24485
24486     /**
24487      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24488      * @param {Number} startRow The index of the first row in the range
24489      * @param {Number} endRow The index of the last row in the range
24490      * @param {Boolean} keepExisting (optional) True to retain existing selections
24491      */
24492     selectRange : function(startRow, endRow, keepExisting){
24493         if(this.locked) {
24494             return;
24495         }
24496         if(!keepExisting){
24497             this.clearSelections();
24498         }
24499         if(startRow <= endRow){
24500             for(var i = startRow; i <= endRow; i++){
24501                 this.selectRow(i, true);
24502             }
24503         }else{
24504             for(var i = startRow; i >= endRow; i--){
24505                 this.selectRow(i, true);
24506             }
24507         }
24508     },
24509
24510     /**
24511      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24512      * @param {Number} startRow The index of the first row in the range
24513      * @param {Number} endRow The index of the last row in the range
24514      */
24515     deselectRange : function(startRow, endRow, preventViewNotify){
24516         if(this.locked) {
24517             return;
24518         }
24519         for(var i = startRow; i <= endRow; i++){
24520             this.deselectRow(i, preventViewNotify);
24521         }
24522     },
24523
24524     /**
24525      * Selects a row.
24526      * @param {Number} row The index of the row to select
24527      * @param {Boolean} keepExisting (optional) True to keep existing selections
24528      */
24529     selectRow : function(index, keepExisting, preventViewNotify)
24530     {
24531             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24532             return;
24533         }
24534         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24535             if(!keepExisting || this.singleSelect){
24536                 this.clearSelections();
24537             }
24538             
24539             var r = this.grid.store.getAt(index);
24540             //console.log('selectRow - record id :' + r.id);
24541             
24542             this.selections.add(r);
24543             this.last = this.lastActive = index;
24544             if(!preventViewNotify){
24545                 var proxy = new Roo.Element(
24546                                 this.grid.getRowDom(index)
24547                 );
24548                 proxy.addClass('bg-info info');
24549             }
24550             this.fireEvent("rowselect", this, index, r);
24551             this.fireEvent("selectionchange", this);
24552         }
24553     },
24554
24555     /**
24556      * Deselects a row.
24557      * @param {Number} row The index of the row to deselect
24558      */
24559     deselectRow : function(index, preventViewNotify)
24560     {
24561         if(this.locked) {
24562             return;
24563         }
24564         if(this.last == index){
24565             this.last = false;
24566         }
24567         if(this.lastActive == index){
24568             this.lastActive = false;
24569         }
24570         
24571         var r = this.grid.store.getAt(index);
24572         if (!r) {
24573             return;
24574         }
24575         
24576         this.selections.remove(r);
24577         //.console.log('deselectRow - record id :' + r.id);
24578         if(!preventViewNotify){
24579         
24580             var proxy = new Roo.Element(
24581                 this.grid.getRowDom(index)
24582             );
24583             proxy.removeClass('bg-info info');
24584         }
24585         this.fireEvent("rowdeselect", this, index);
24586         this.fireEvent("selectionchange", this);
24587     },
24588
24589     // private
24590     restoreLast : function(){
24591         if(this._last){
24592             this.last = this._last;
24593         }
24594     },
24595
24596     // private
24597     acceptsNav : function(row, col, cm){
24598         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24599     },
24600
24601     // private
24602     onEditorKey : function(field, e){
24603         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24604         if(k == e.TAB){
24605             e.stopEvent();
24606             ed.completeEdit();
24607             if(e.shiftKey){
24608                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24609             }else{
24610                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24611             }
24612         }else if(k == e.ENTER && !e.ctrlKey){
24613             e.stopEvent();
24614             ed.completeEdit();
24615             if(e.shiftKey){
24616                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24617             }else{
24618                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24619             }
24620         }else if(k == e.ESC){
24621             ed.cancelEdit();
24622         }
24623         if(newCell){
24624             g.startEditing(newCell[0], newCell[1]);
24625         }
24626     }
24627 });
24628 /*
24629  * Based on:
24630  * Ext JS Library 1.1.1
24631  * Copyright(c) 2006-2007, Ext JS, LLC.
24632  *
24633  * Originally Released Under LGPL - original licence link has changed is not relivant.
24634  *
24635  * Fork - LGPL
24636  * <script type="text/javascript">
24637  */
24638  
24639 /**
24640  * @class Roo.bootstrap.PagingToolbar
24641  * @extends Roo.bootstrap.NavSimplebar
24642  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24643  * @constructor
24644  * Create a new PagingToolbar
24645  * @param {Object} config The config object
24646  * @param {Roo.data.Store} store
24647  */
24648 Roo.bootstrap.PagingToolbar = function(config)
24649 {
24650     // old args format still supported... - xtype is prefered..
24651         // created from xtype...
24652     
24653     this.ds = config.dataSource;
24654     
24655     if (config.store && !this.ds) {
24656         this.store= Roo.factory(config.store, Roo.data);
24657         this.ds = this.store;
24658         this.ds.xmodule = this.xmodule || false;
24659     }
24660     
24661     this.toolbarItems = [];
24662     if (config.items) {
24663         this.toolbarItems = config.items;
24664     }
24665     
24666     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24667     
24668     this.cursor = 0;
24669     
24670     if (this.ds) { 
24671         this.bind(this.ds);
24672     }
24673     
24674     if (Roo.bootstrap.version == 4) {
24675         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24676     } else {
24677         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24678     }
24679     
24680 };
24681
24682 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24683     /**
24684      * @cfg {Roo.data.Store} dataSource
24685      * The underlying data store providing the paged data
24686      */
24687     /**
24688      * @cfg {String/HTMLElement/Element} container
24689      * container The id or element that will contain the toolbar
24690      */
24691     /**
24692      * @cfg {Boolean} displayInfo
24693      * True to display the displayMsg (defaults to false)
24694      */
24695     /**
24696      * @cfg {Number} pageSize
24697      * The number of records to display per page (defaults to 20)
24698      */
24699     pageSize: 20,
24700     /**
24701      * @cfg {String} displayMsg
24702      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24703      */
24704     displayMsg : 'Displaying {0} - {1} of {2}',
24705     /**
24706      * @cfg {String} emptyMsg
24707      * The message to display when no records are found (defaults to "No data to display")
24708      */
24709     emptyMsg : 'No data to display',
24710     /**
24711      * Customizable piece of the default paging text (defaults to "Page")
24712      * @type String
24713      */
24714     beforePageText : "Page",
24715     /**
24716      * Customizable piece of the default paging text (defaults to "of %0")
24717      * @type String
24718      */
24719     afterPageText : "of {0}",
24720     /**
24721      * Customizable piece of the default paging text (defaults to "First Page")
24722      * @type String
24723      */
24724     firstText : "First Page",
24725     /**
24726      * Customizable piece of the default paging text (defaults to "Previous Page")
24727      * @type String
24728      */
24729     prevText : "Previous Page",
24730     /**
24731      * Customizable piece of the default paging text (defaults to "Next Page")
24732      * @type String
24733      */
24734     nextText : "Next Page",
24735     /**
24736      * Customizable piece of the default paging text (defaults to "Last Page")
24737      * @type String
24738      */
24739     lastText : "Last Page",
24740     /**
24741      * Customizable piece of the default paging text (defaults to "Refresh")
24742      * @type String
24743      */
24744     refreshText : "Refresh",
24745
24746     buttons : false,
24747     // private
24748     onRender : function(ct, position) 
24749     {
24750         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24751         this.navgroup.parentId = this.id;
24752         this.navgroup.onRender(this.el, null);
24753         // add the buttons to the navgroup
24754         
24755         if(this.displayInfo){
24756             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24757             this.displayEl = this.el.select('.x-paging-info', true).first();
24758 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24759 //            this.displayEl = navel.el.select('span',true).first();
24760         }
24761         
24762         var _this = this;
24763         
24764         if(this.buttons){
24765             Roo.each(_this.buttons, function(e){ // this might need to use render????
24766                Roo.factory(e).render(_this.el);
24767             });
24768         }
24769             
24770         Roo.each(_this.toolbarItems, function(e) {
24771             _this.navgroup.addItem(e);
24772         });
24773         
24774         
24775         this.first = this.navgroup.addItem({
24776             tooltip: this.firstText,
24777             cls: "prev btn-outline-secondary",
24778             html : ' <i class="fa fa-step-backward"></i>',
24779             disabled: true,
24780             preventDefault: true,
24781             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24782         });
24783         
24784         this.prev =  this.navgroup.addItem({
24785             tooltip: this.prevText,
24786             cls: "prev btn-outline-secondary",
24787             html : ' <i class="fa fa-backward"></i>',
24788             disabled: true,
24789             preventDefault: true,
24790             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24791         });
24792     //this.addSeparator();
24793         
24794         
24795         var field = this.navgroup.addItem( {
24796             tagtype : 'span',
24797             cls : 'x-paging-position  btn-outline-secondary',
24798              disabled: true,
24799             html : this.beforePageText  +
24800                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24801                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24802          } ); //?? escaped?
24803         
24804         this.field = field.el.select('input', true).first();
24805         this.field.on("keydown", this.onPagingKeydown, this);
24806         this.field.on("focus", function(){this.dom.select();});
24807     
24808     
24809         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24810         //this.field.setHeight(18);
24811         //this.addSeparator();
24812         this.next = this.navgroup.addItem({
24813             tooltip: this.nextText,
24814             cls: "next btn-outline-secondary",
24815             html : ' <i class="fa fa-forward"></i>',
24816             disabled: true,
24817             preventDefault: true,
24818             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24819         });
24820         this.last = this.navgroup.addItem({
24821             tooltip: this.lastText,
24822             html : ' <i class="fa fa-step-forward"></i>',
24823             cls: "next btn-outline-secondary",
24824             disabled: true,
24825             preventDefault: true,
24826             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24827         });
24828     //this.addSeparator();
24829         this.loading = this.navgroup.addItem({
24830             tooltip: this.refreshText,
24831             cls: "btn-outline-secondary",
24832             html : ' <i class="fa fa-refresh"></i>',
24833             preventDefault: true,
24834             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24835         });
24836         
24837     },
24838
24839     // private
24840     updateInfo : function(){
24841         if(this.displayEl){
24842             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24843             var msg = count == 0 ?
24844                 this.emptyMsg :
24845                 String.format(
24846                     this.displayMsg,
24847                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24848                 );
24849             this.displayEl.update(msg);
24850         }
24851     },
24852
24853     // private
24854     onLoad : function(ds, r, o)
24855     {
24856         this.cursor = o.params.start ? o.params.start : 0;
24857         
24858         var d = this.getPageData(),
24859             ap = d.activePage,
24860             ps = d.pages;
24861         
24862         
24863         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24864         this.field.dom.value = ap;
24865         this.first.setDisabled(ap == 1);
24866         this.prev.setDisabled(ap == 1);
24867         this.next.setDisabled(ap == ps);
24868         this.last.setDisabled(ap == ps);
24869         this.loading.enable();
24870         this.updateInfo();
24871     },
24872
24873     // private
24874     getPageData : function(){
24875         var total = this.ds.getTotalCount();
24876         return {
24877             total : total,
24878             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24879             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24880         };
24881     },
24882
24883     // private
24884     onLoadError : function(){
24885         this.loading.enable();
24886     },
24887
24888     // private
24889     onPagingKeydown : function(e){
24890         var k = e.getKey();
24891         var d = this.getPageData();
24892         if(k == e.RETURN){
24893             var v = this.field.dom.value, pageNum;
24894             if(!v || isNaN(pageNum = parseInt(v, 10))){
24895                 this.field.dom.value = d.activePage;
24896                 return;
24897             }
24898             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24899             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24900             e.stopEvent();
24901         }
24902         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))
24903         {
24904           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24905           this.field.dom.value = pageNum;
24906           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24907           e.stopEvent();
24908         }
24909         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24910         {
24911           var v = this.field.dom.value, pageNum; 
24912           var increment = (e.shiftKey) ? 10 : 1;
24913           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24914                 increment *= -1;
24915           }
24916           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24917             this.field.dom.value = d.activePage;
24918             return;
24919           }
24920           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24921           {
24922             this.field.dom.value = parseInt(v, 10) + increment;
24923             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24924             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24925           }
24926           e.stopEvent();
24927         }
24928     },
24929
24930     // private
24931     beforeLoad : function(){
24932         if(this.loading){
24933             this.loading.disable();
24934         }
24935     },
24936
24937     // private
24938     onClick : function(which){
24939         
24940         var ds = this.ds;
24941         if (!ds) {
24942             return;
24943         }
24944         
24945         switch(which){
24946             case "first":
24947                 ds.load({params:{start: 0, limit: this.pageSize}});
24948             break;
24949             case "prev":
24950                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24951             break;
24952             case "next":
24953                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24954             break;
24955             case "last":
24956                 var total = ds.getTotalCount();
24957                 var extra = total % this.pageSize;
24958                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24959                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24960             break;
24961             case "refresh":
24962                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24963             break;
24964         }
24965     },
24966
24967     /**
24968      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24969      * @param {Roo.data.Store} store The data store to unbind
24970      */
24971     unbind : function(ds){
24972         ds.un("beforeload", this.beforeLoad, this);
24973         ds.un("load", this.onLoad, this);
24974         ds.un("loadexception", this.onLoadError, this);
24975         ds.un("remove", this.updateInfo, this);
24976         ds.un("add", this.updateInfo, this);
24977         this.ds = undefined;
24978     },
24979
24980     /**
24981      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24982      * @param {Roo.data.Store} store The data store to bind
24983      */
24984     bind : function(ds){
24985         ds.on("beforeload", this.beforeLoad, this);
24986         ds.on("load", this.onLoad, this);
24987         ds.on("loadexception", this.onLoadError, this);
24988         ds.on("remove", this.updateInfo, this);
24989         ds.on("add", this.updateInfo, this);
24990         this.ds = ds;
24991     }
24992 });/*
24993  * - LGPL
24994  *
24995  * element
24996  * 
24997  */
24998
24999 /**
25000  * @class Roo.bootstrap.MessageBar
25001  * @extends Roo.bootstrap.Component
25002  * Bootstrap MessageBar class
25003  * @cfg {String} html contents of the MessageBar
25004  * @cfg {String} weight (info | success | warning | danger) default info
25005  * @cfg {String} beforeClass insert the bar before the given class
25006  * @cfg {Boolean} closable (true | false) default false
25007  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25008  * 
25009  * @constructor
25010  * Create a new Element
25011  * @param {Object} config The config object
25012  */
25013
25014 Roo.bootstrap.MessageBar = function(config){
25015     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25016 };
25017
25018 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25019     
25020     html: '',
25021     weight: 'info',
25022     closable: false,
25023     fixed: false,
25024     beforeClass: 'bootstrap-sticky-wrap',
25025     
25026     getAutoCreate : function(){
25027         
25028         var cfg = {
25029             tag: 'div',
25030             cls: 'alert alert-dismissable alert-' + this.weight,
25031             cn: [
25032                 {
25033                     tag: 'span',
25034                     cls: 'message',
25035                     html: this.html || ''
25036                 }
25037             ]
25038         };
25039         
25040         if(this.fixed){
25041             cfg.cls += ' alert-messages-fixed';
25042         }
25043         
25044         if(this.closable){
25045             cfg.cn.push({
25046                 tag: 'button',
25047                 cls: 'close',
25048                 html: 'x'
25049             });
25050         }
25051         
25052         return cfg;
25053     },
25054     
25055     onRender : function(ct, position)
25056     {
25057         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25058         
25059         if(!this.el){
25060             var cfg = Roo.apply({},  this.getAutoCreate());
25061             cfg.id = Roo.id();
25062             
25063             if (this.cls) {
25064                 cfg.cls += ' ' + this.cls;
25065             }
25066             if (this.style) {
25067                 cfg.style = this.style;
25068             }
25069             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25070             
25071             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25072         }
25073         
25074         this.el.select('>button.close').on('click', this.hide, this);
25075         
25076     },
25077     
25078     show : function()
25079     {
25080         if (!this.rendered) {
25081             this.render();
25082         }
25083         
25084         this.el.show();
25085         
25086         this.fireEvent('show', this);
25087         
25088     },
25089     
25090     hide : function()
25091     {
25092         if (!this.rendered) {
25093             this.render();
25094         }
25095         
25096         this.el.hide();
25097         
25098         this.fireEvent('hide', this);
25099     },
25100     
25101     update : function()
25102     {
25103 //        var e = this.el.dom.firstChild;
25104 //        
25105 //        if(this.closable){
25106 //            e = e.nextSibling;
25107 //        }
25108 //        
25109 //        e.data = this.html || '';
25110
25111         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25112     }
25113    
25114 });
25115
25116  
25117
25118      /*
25119  * - LGPL
25120  *
25121  * Graph
25122  * 
25123  */
25124
25125
25126 /**
25127  * @class Roo.bootstrap.Graph
25128  * @extends Roo.bootstrap.Component
25129  * Bootstrap Graph class
25130 > Prameters
25131  -sm {number} sm 4
25132  -md {number} md 5
25133  @cfg {String} graphtype  bar | vbar | pie
25134  @cfg {number} g_x coodinator | centre x (pie)
25135  @cfg {number} g_y coodinator | centre y (pie)
25136  @cfg {number} g_r radius (pie)
25137  @cfg {number} g_height height of the chart (respected by all elements in the set)
25138  @cfg {number} g_width width of the chart (respected by all elements in the set)
25139  @cfg {Object} title The title of the chart
25140     
25141  -{Array}  values
25142  -opts (object) options for the chart 
25143      o {
25144      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25145      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25146      o vgutter (number)
25147      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.
25148      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25149      o to
25150      o stretch (boolean)
25151      o }
25152  -opts (object) options for the pie
25153      o{
25154      o cut
25155      o startAngle (number)
25156      o endAngle (number)
25157      } 
25158  *
25159  * @constructor
25160  * Create a new Input
25161  * @param {Object} config The config object
25162  */
25163
25164 Roo.bootstrap.Graph = function(config){
25165     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25166     
25167     this.addEvents({
25168         // img events
25169         /**
25170          * @event click
25171          * The img click event for the img.
25172          * @param {Roo.EventObject} e
25173          */
25174         "click" : true
25175     });
25176 };
25177
25178 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25179     
25180     sm: 4,
25181     md: 5,
25182     graphtype: 'bar',
25183     g_height: 250,
25184     g_width: 400,
25185     g_x: 50,
25186     g_y: 50,
25187     g_r: 30,
25188     opts:{
25189         //g_colors: this.colors,
25190         g_type: 'soft',
25191         g_gutter: '20%'
25192
25193     },
25194     title : false,
25195
25196     getAutoCreate : function(){
25197         
25198         var cfg = {
25199             tag: 'div',
25200             html : null
25201         };
25202         
25203         
25204         return  cfg;
25205     },
25206
25207     onRender : function(ct,position){
25208         
25209         
25210         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25211         
25212         if (typeof(Raphael) == 'undefined') {
25213             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25214             return;
25215         }
25216         
25217         this.raphael = Raphael(this.el.dom);
25218         
25219                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25220                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25221                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25222                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25223                 /*
25224                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25225                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25226                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25227                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25228                 
25229                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25230                 r.barchart(330, 10, 300, 220, data1);
25231                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25232                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25233                 */
25234                 
25235                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25236                 // r.barchart(30, 30, 560, 250,  xdata, {
25237                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25238                 //     axis : "0 0 1 1",
25239                 //     axisxlabels :  xdata
25240                 //     //yvalues : cols,
25241                    
25242                 // });
25243 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25244 //        
25245 //        this.load(null,xdata,{
25246 //                axis : "0 0 1 1",
25247 //                axisxlabels :  xdata
25248 //                });
25249
25250     },
25251
25252     load : function(graphtype,xdata,opts)
25253     {
25254         this.raphael.clear();
25255         if(!graphtype) {
25256             graphtype = this.graphtype;
25257         }
25258         if(!opts){
25259             opts = this.opts;
25260         }
25261         var r = this.raphael,
25262             fin = function () {
25263                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25264             },
25265             fout = function () {
25266                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25267             },
25268             pfin = function() {
25269                 this.sector.stop();
25270                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25271
25272                 if (this.label) {
25273                     this.label[0].stop();
25274                     this.label[0].attr({ r: 7.5 });
25275                     this.label[1].attr({ "font-weight": 800 });
25276                 }
25277             },
25278             pfout = function() {
25279                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25280
25281                 if (this.label) {
25282                     this.label[0].animate({ r: 5 }, 500, "bounce");
25283                     this.label[1].attr({ "font-weight": 400 });
25284                 }
25285             };
25286
25287         switch(graphtype){
25288             case 'bar':
25289                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25290                 break;
25291             case 'hbar':
25292                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25293                 break;
25294             case 'pie':
25295 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25296 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25297 //            
25298                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25299                 
25300                 break;
25301
25302         }
25303         
25304         if(this.title){
25305             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25306         }
25307         
25308     },
25309     
25310     setTitle: function(o)
25311     {
25312         this.title = o;
25313     },
25314     
25315     initEvents: function() {
25316         
25317         if(!this.href){
25318             this.el.on('click', this.onClick, this);
25319         }
25320     },
25321     
25322     onClick : function(e)
25323     {
25324         Roo.log('img onclick');
25325         this.fireEvent('click', this, e);
25326     }
25327    
25328 });
25329
25330  
25331 /*
25332  * - LGPL
25333  *
25334  * numberBox
25335  * 
25336  */
25337 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25338
25339 /**
25340  * @class Roo.bootstrap.dash.NumberBox
25341  * @extends Roo.bootstrap.Component
25342  * Bootstrap NumberBox class
25343  * @cfg {String} headline Box headline
25344  * @cfg {String} content Box content
25345  * @cfg {String} icon Box icon
25346  * @cfg {String} footer Footer text
25347  * @cfg {String} fhref Footer href
25348  * 
25349  * @constructor
25350  * Create a new NumberBox
25351  * @param {Object} config The config object
25352  */
25353
25354
25355 Roo.bootstrap.dash.NumberBox = function(config){
25356     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25357     
25358 };
25359
25360 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25361     
25362     headline : '',
25363     content : '',
25364     icon : '',
25365     footer : '',
25366     fhref : '',
25367     ficon : '',
25368     
25369     getAutoCreate : function(){
25370         
25371         var cfg = {
25372             tag : 'div',
25373             cls : 'small-box ',
25374             cn : [
25375                 {
25376                     tag : 'div',
25377                     cls : 'inner',
25378                     cn :[
25379                         {
25380                             tag : 'h3',
25381                             cls : 'roo-headline',
25382                             html : this.headline
25383                         },
25384                         {
25385                             tag : 'p',
25386                             cls : 'roo-content',
25387                             html : this.content
25388                         }
25389                     ]
25390                 }
25391             ]
25392         };
25393         
25394         if(this.icon){
25395             cfg.cn.push({
25396                 tag : 'div',
25397                 cls : 'icon',
25398                 cn :[
25399                     {
25400                         tag : 'i',
25401                         cls : 'ion ' + this.icon
25402                     }
25403                 ]
25404             });
25405         }
25406         
25407         if(this.footer){
25408             var footer = {
25409                 tag : 'a',
25410                 cls : 'small-box-footer',
25411                 href : this.fhref || '#',
25412                 html : this.footer
25413             };
25414             
25415             cfg.cn.push(footer);
25416             
25417         }
25418         
25419         return  cfg;
25420     },
25421
25422     onRender : function(ct,position){
25423         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25424
25425
25426        
25427                 
25428     },
25429
25430     setHeadline: function (value)
25431     {
25432         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25433     },
25434     
25435     setFooter: function (value, href)
25436     {
25437         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25438         
25439         if(href){
25440             this.el.select('a.small-box-footer',true).first().attr('href', href);
25441         }
25442         
25443     },
25444
25445     setContent: function (value)
25446     {
25447         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25448     },
25449
25450     initEvents: function() 
25451     {   
25452         
25453     }
25454     
25455 });
25456
25457  
25458 /*
25459  * - LGPL
25460  *
25461  * TabBox
25462  * 
25463  */
25464 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25465
25466 /**
25467  * @class Roo.bootstrap.dash.TabBox
25468  * @extends Roo.bootstrap.Component
25469  * Bootstrap TabBox class
25470  * @cfg {String} title Title of the TabBox
25471  * @cfg {String} icon Icon of the TabBox
25472  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25473  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25474  * 
25475  * @constructor
25476  * Create a new TabBox
25477  * @param {Object} config The config object
25478  */
25479
25480
25481 Roo.bootstrap.dash.TabBox = function(config){
25482     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25483     this.addEvents({
25484         // raw events
25485         /**
25486          * @event addpane
25487          * When a pane is added
25488          * @param {Roo.bootstrap.dash.TabPane} pane
25489          */
25490         "addpane" : true,
25491         /**
25492          * @event activatepane
25493          * When a pane is activated
25494          * @param {Roo.bootstrap.dash.TabPane} pane
25495          */
25496         "activatepane" : true
25497         
25498          
25499     });
25500     
25501     this.panes = [];
25502 };
25503
25504 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25505
25506     title : '',
25507     icon : false,
25508     showtabs : true,
25509     tabScrollable : false,
25510     
25511     getChildContainer : function()
25512     {
25513         return this.el.select('.tab-content', true).first();
25514     },
25515     
25516     getAutoCreate : function(){
25517         
25518         var header = {
25519             tag: 'li',
25520             cls: 'pull-left header',
25521             html: this.title,
25522             cn : []
25523         };
25524         
25525         if(this.icon){
25526             header.cn.push({
25527                 tag: 'i',
25528                 cls: 'fa ' + this.icon
25529             });
25530         }
25531         
25532         var h = {
25533             tag: 'ul',
25534             cls: 'nav nav-tabs pull-right',
25535             cn: [
25536                 header
25537             ]
25538         };
25539         
25540         if(this.tabScrollable){
25541             h = {
25542                 tag: 'div',
25543                 cls: 'tab-header',
25544                 cn: [
25545                     {
25546                         tag: 'ul',
25547                         cls: 'nav nav-tabs pull-right',
25548                         cn: [
25549                             header
25550                         ]
25551                     }
25552                 ]
25553             };
25554         }
25555         
25556         var cfg = {
25557             tag: 'div',
25558             cls: 'nav-tabs-custom',
25559             cn: [
25560                 h,
25561                 {
25562                     tag: 'div',
25563                     cls: 'tab-content no-padding',
25564                     cn: []
25565                 }
25566             ]
25567         };
25568
25569         return  cfg;
25570     },
25571     initEvents : function()
25572     {
25573         //Roo.log('add add pane handler');
25574         this.on('addpane', this.onAddPane, this);
25575     },
25576      /**
25577      * Updates the box title
25578      * @param {String} html to set the title to.
25579      */
25580     setTitle : function(value)
25581     {
25582         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25583     },
25584     onAddPane : function(pane)
25585     {
25586         this.panes.push(pane);
25587         //Roo.log('addpane');
25588         //Roo.log(pane);
25589         // tabs are rendere left to right..
25590         if(!this.showtabs){
25591             return;
25592         }
25593         
25594         var ctr = this.el.select('.nav-tabs', true).first();
25595          
25596          
25597         var existing = ctr.select('.nav-tab',true);
25598         var qty = existing.getCount();;
25599         
25600         
25601         var tab = ctr.createChild({
25602             tag : 'li',
25603             cls : 'nav-tab' + (qty ? '' : ' active'),
25604             cn : [
25605                 {
25606                     tag : 'a',
25607                     href:'#',
25608                     html : pane.title
25609                 }
25610             ]
25611         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25612         pane.tab = tab;
25613         
25614         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25615         if (!qty) {
25616             pane.el.addClass('active');
25617         }
25618         
25619                 
25620     },
25621     onTabClick : function(ev,un,ob,pane)
25622     {
25623         //Roo.log('tab - prev default');
25624         ev.preventDefault();
25625         
25626         
25627         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25628         pane.tab.addClass('active');
25629         //Roo.log(pane.title);
25630         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25631         // technically we should have a deactivate event.. but maybe add later.
25632         // and it should not de-activate the selected tab...
25633         this.fireEvent('activatepane', pane);
25634         pane.el.addClass('active');
25635         pane.fireEvent('activate');
25636         
25637         
25638     },
25639     
25640     getActivePane : function()
25641     {
25642         var r = false;
25643         Roo.each(this.panes, function(p) {
25644             if(p.el.hasClass('active')){
25645                 r = p;
25646                 return false;
25647             }
25648             
25649             return;
25650         });
25651         
25652         return r;
25653     }
25654     
25655     
25656 });
25657
25658  
25659 /*
25660  * - LGPL
25661  *
25662  * Tab pane
25663  * 
25664  */
25665 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25666 /**
25667  * @class Roo.bootstrap.TabPane
25668  * @extends Roo.bootstrap.Component
25669  * Bootstrap TabPane class
25670  * @cfg {Boolean} active (false | true) Default false
25671  * @cfg {String} title title of panel
25672
25673  * 
25674  * @constructor
25675  * Create a new TabPane
25676  * @param {Object} config The config object
25677  */
25678
25679 Roo.bootstrap.dash.TabPane = function(config){
25680     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25681     
25682     this.addEvents({
25683         // raw events
25684         /**
25685          * @event activate
25686          * When a pane is activated
25687          * @param {Roo.bootstrap.dash.TabPane} pane
25688          */
25689         "activate" : true
25690          
25691     });
25692 };
25693
25694 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25695     
25696     active : false,
25697     title : '',
25698     
25699     // the tabBox that this is attached to.
25700     tab : false,
25701      
25702     getAutoCreate : function() 
25703     {
25704         var cfg = {
25705             tag: 'div',
25706             cls: 'tab-pane'
25707         };
25708         
25709         if(this.active){
25710             cfg.cls += ' active';
25711         }
25712         
25713         return cfg;
25714     },
25715     initEvents  : function()
25716     {
25717         //Roo.log('trigger add pane handler');
25718         this.parent().fireEvent('addpane', this)
25719     },
25720     
25721      /**
25722      * Updates the tab title 
25723      * @param {String} html to set the title to.
25724      */
25725     setTitle: function(str)
25726     {
25727         if (!this.tab) {
25728             return;
25729         }
25730         this.title = str;
25731         this.tab.select('a', true).first().dom.innerHTML = str;
25732         
25733     }
25734     
25735     
25736     
25737 });
25738
25739  
25740
25741
25742  /*
25743  * - LGPL
25744  *
25745  * menu
25746  * 
25747  */
25748 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25749
25750 /**
25751  * @class Roo.bootstrap.menu.Menu
25752  * @extends Roo.bootstrap.Component
25753  * Bootstrap Menu class - container for Menu
25754  * @cfg {String} html Text of the menu
25755  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25756  * @cfg {String} icon Font awesome icon
25757  * @cfg {String} pos Menu align to (top | bottom) default bottom
25758  * 
25759  * 
25760  * @constructor
25761  * Create a new Menu
25762  * @param {Object} config The config object
25763  */
25764
25765
25766 Roo.bootstrap.menu.Menu = function(config){
25767     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25768     
25769     this.addEvents({
25770         /**
25771          * @event beforeshow
25772          * Fires before this menu is displayed
25773          * @param {Roo.bootstrap.menu.Menu} this
25774          */
25775         beforeshow : true,
25776         /**
25777          * @event beforehide
25778          * Fires before this menu is hidden
25779          * @param {Roo.bootstrap.menu.Menu} this
25780          */
25781         beforehide : true,
25782         /**
25783          * @event show
25784          * Fires after this menu is displayed
25785          * @param {Roo.bootstrap.menu.Menu} this
25786          */
25787         show : true,
25788         /**
25789          * @event hide
25790          * Fires after this menu is hidden
25791          * @param {Roo.bootstrap.menu.Menu} this
25792          */
25793         hide : true,
25794         /**
25795          * @event click
25796          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25797          * @param {Roo.bootstrap.menu.Menu} this
25798          * @param {Roo.EventObject} e
25799          */
25800         click : true
25801     });
25802     
25803 };
25804
25805 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25806     
25807     submenu : false,
25808     html : '',
25809     weight : 'default',
25810     icon : false,
25811     pos : 'bottom',
25812     
25813     
25814     getChildContainer : function() {
25815         if(this.isSubMenu){
25816             return this.el;
25817         }
25818         
25819         return this.el.select('ul.dropdown-menu', true).first();  
25820     },
25821     
25822     getAutoCreate : function()
25823     {
25824         var text = [
25825             {
25826                 tag : 'span',
25827                 cls : 'roo-menu-text',
25828                 html : this.html
25829             }
25830         ];
25831         
25832         if(this.icon){
25833             text.unshift({
25834                 tag : 'i',
25835                 cls : 'fa ' + this.icon
25836             })
25837         }
25838         
25839         
25840         var cfg = {
25841             tag : 'div',
25842             cls : 'btn-group',
25843             cn : [
25844                 {
25845                     tag : 'button',
25846                     cls : 'dropdown-button btn btn-' + this.weight,
25847                     cn : text
25848                 },
25849                 {
25850                     tag : 'button',
25851                     cls : 'dropdown-toggle btn btn-' + this.weight,
25852                     cn : [
25853                         {
25854                             tag : 'span',
25855                             cls : 'caret'
25856                         }
25857                     ]
25858                 },
25859                 {
25860                     tag : 'ul',
25861                     cls : 'dropdown-menu'
25862                 }
25863             ]
25864             
25865         };
25866         
25867         if(this.pos == 'top'){
25868             cfg.cls += ' dropup';
25869         }
25870         
25871         if(this.isSubMenu){
25872             cfg = {
25873                 tag : 'ul',
25874                 cls : 'dropdown-menu'
25875             }
25876         }
25877         
25878         return cfg;
25879     },
25880     
25881     onRender : function(ct, position)
25882     {
25883         this.isSubMenu = ct.hasClass('dropdown-submenu');
25884         
25885         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25886     },
25887     
25888     initEvents : function() 
25889     {
25890         if(this.isSubMenu){
25891             return;
25892         }
25893         
25894         this.hidden = true;
25895         
25896         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25897         this.triggerEl.on('click', this.onTriggerPress, this);
25898         
25899         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25900         this.buttonEl.on('click', this.onClick, this);
25901         
25902     },
25903     
25904     list : function()
25905     {
25906         if(this.isSubMenu){
25907             return this.el;
25908         }
25909         
25910         return this.el.select('ul.dropdown-menu', true).first();
25911     },
25912     
25913     onClick : function(e)
25914     {
25915         this.fireEvent("click", this, e);
25916     },
25917     
25918     onTriggerPress  : function(e)
25919     {   
25920         if (this.isVisible()) {
25921             this.hide();
25922         } else {
25923             this.show();
25924         }
25925     },
25926     
25927     isVisible : function(){
25928         return !this.hidden;
25929     },
25930     
25931     show : function()
25932     {
25933         this.fireEvent("beforeshow", this);
25934         
25935         this.hidden = false;
25936         this.el.addClass('open');
25937         
25938         Roo.get(document).on("mouseup", this.onMouseUp, this);
25939         
25940         this.fireEvent("show", this);
25941         
25942         
25943     },
25944     
25945     hide : function()
25946     {
25947         this.fireEvent("beforehide", this);
25948         
25949         this.hidden = true;
25950         this.el.removeClass('open');
25951         
25952         Roo.get(document).un("mouseup", this.onMouseUp);
25953         
25954         this.fireEvent("hide", this);
25955     },
25956     
25957     onMouseUp : function()
25958     {
25959         this.hide();
25960     }
25961     
25962 });
25963
25964  
25965  /*
25966  * - LGPL
25967  *
25968  * menu item
25969  * 
25970  */
25971 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25972
25973 /**
25974  * @class Roo.bootstrap.menu.Item
25975  * @extends Roo.bootstrap.Component
25976  * Bootstrap MenuItem class
25977  * @cfg {Boolean} submenu (true | false) default false
25978  * @cfg {String} html text of the item
25979  * @cfg {String} href the link
25980  * @cfg {Boolean} disable (true | false) default false
25981  * @cfg {Boolean} preventDefault (true | false) default true
25982  * @cfg {String} icon Font awesome icon
25983  * @cfg {String} pos Submenu align to (left | right) default right 
25984  * 
25985  * 
25986  * @constructor
25987  * Create a new Item
25988  * @param {Object} config The config object
25989  */
25990
25991
25992 Roo.bootstrap.menu.Item = function(config){
25993     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25994     this.addEvents({
25995         /**
25996          * @event mouseover
25997          * Fires when the mouse is hovering over this menu
25998          * @param {Roo.bootstrap.menu.Item} this
25999          * @param {Roo.EventObject} e
26000          */
26001         mouseover : true,
26002         /**
26003          * @event mouseout
26004          * Fires when the mouse exits this menu
26005          * @param {Roo.bootstrap.menu.Item} this
26006          * @param {Roo.EventObject} e
26007          */
26008         mouseout : true,
26009         // raw events
26010         /**
26011          * @event click
26012          * The raw click event for the entire grid.
26013          * @param {Roo.EventObject} e
26014          */
26015         click : true
26016     });
26017 };
26018
26019 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26020     
26021     submenu : false,
26022     href : '',
26023     html : '',
26024     preventDefault: true,
26025     disable : false,
26026     icon : false,
26027     pos : 'right',
26028     
26029     getAutoCreate : function()
26030     {
26031         var text = [
26032             {
26033                 tag : 'span',
26034                 cls : 'roo-menu-item-text',
26035                 html : this.html
26036             }
26037         ];
26038         
26039         if(this.icon){
26040             text.unshift({
26041                 tag : 'i',
26042                 cls : 'fa ' + this.icon
26043             })
26044         }
26045         
26046         var cfg = {
26047             tag : 'li',
26048             cn : [
26049                 {
26050                     tag : 'a',
26051                     href : this.href || '#',
26052                     cn : text
26053                 }
26054             ]
26055         };
26056         
26057         if(this.disable){
26058             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26059         }
26060         
26061         if(this.submenu){
26062             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26063             
26064             if(this.pos == 'left'){
26065                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26066             }
26067         }
26068         
26069         return cfg;
26070     },
26071     
26072     initEvents : function() 
26073     {
26074         this.el.on('mouseover', this.onMouseOver, this);
26075         this.el.on('mouseout', this.onMouseOut, this);
26076         
26077         this.el.select('a', true).first().on('click', this.onClick, this);
26078         
26079     },
26080     
26081     onClick : function(e)
26082     {
26083         if(this.preventDefault){
26084             e.preventDefault();
26085         }
26086         
26087         this.fireEvent("click", this, e);
26088     },
26089     
26090     onMouseOver : function(e)
26091     {
26092         if(this.submenu && this.pos == 'left'){
26093             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26094         }
26095         
26096         this.fireEvent("mouseover", this, e);
26097     },
26098     
26099     onMouseOut : function(e)
26100     {
26101         this.fireEvent("mouseout", this, e);
26102     }
26103 });
26104
26105  
26106
26107  /*
26108  * - LGPL
26109  *
26110  * menu separator
26111  * 
26112  */
26113 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26114
26115 /**
26116  * @class Roo.bootstrap.menu.Separator
26117  * @extends Roo.bootstrap.Component
26118  * Bootstrap Separator class
26119  * 
26120  * @constructor
26121  * Create a new Separator
26122  * @param {Object} config The config object
26123  */
26124
26125
26126 Roo.bootstrap.menu.Separator = function(config){
26127     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26128 };
26129
26130 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26131     
26132     getAutoCreate : function(){
26133         var cfg = {
26134             tag : 'li',
26135             cls: 'divider'
26136         };
26137         
26138         return cfg;
26139     }
26140    
26141 });
26142
26143  
26144
26145  /*
26146  * - LGPL
26147  *
26148  * Tooltip
26149  * 
26150  */
26151
26152 /**
26153  * @class Roo.bootstrap.Tooltip
26154  * Bootstrap Tooltip class
26155  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26156  * to determine which dom element triggers the tooltip.
26157  * 
26158  * It needs to add support for additional attributes like tooltip-position
26159  * 
26160  * @constructor
26161  * Create a new Toolti
26162  * @param {Object} config The config object
26163  */
26164
26165 Roo.bootstrap.Tooltip = function(config){
26166     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26167     
26168     this.alignment = Roo.bootstrap.Tooltip.alignment;
26169     
26170     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26171         this.alignment = config.alignment;
26172     }
26173     
26174 };
26175
26176 Roo.apply(Roo.bootstrap.Tooltip, {
26177     /**
26178      * @function init initialize tooltip monitoring.
26179      * @static
26180      */
26181     currentEl : false,
26182     currentTip : false,
26183     currentRegion : false,
26184     
26185     //  init : delay?
26186     
26187     init : function()
26188     {
26189         Roo.get(document).on('mouseover', this.enter ,this);
26190         Roo.get(document).on('mouseout', this.leave, this);
26191          
26192         
26193         this.currentTip = new Roo.bootstrap.Tooltip();
26194     },
26195     
26196     enter : function(ev)
26197     {
26198         var dom = ev.getTarget();
26199         
26200         //Roo.log(['enter',dom]);
26201         var el = Roo.fly(dom);
26202         if (this.currentEl) {
26203             //Roo.log(dom);
26204             //Roo.log(this.currentEl);
26205             //Roo.log(this.currentEl.contains(dom));
26206             if (this.currentEl == el) {
26207                 return;
26208             }
26209             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26210                 return;
26211             }
26212
26213         }
26214         
26215         if (this.currentTip.el) {
26216             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26217         }    
26218         //Roo.log(ev);
26219         
26220         if(!el || el.dom == document){
26221             return;
26222         }
26223         
26224         var bindEl = el;
26225         
26226         // you can not look for children, as if el is the body.. then everythign is the child..
26227         if (!el.attr('tooltip')) { //
26228             if (!el.select("[tooltip]").elements.length) {
26229                 return;
26230             }
26231             // is the mouse over this child...?
26232             bindEl = el.select("[tooltip]").first();
26233             var xy = ev.getXY();
26234             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26235                 //Roo.log("not in region.");
26236                 return;
26237             }
26238             //Roo.log("child element over..");
26239             
26240         }
26241         this.currentEl = bindEl;
26242         this.currentTip.bind(bindEl);
26243         this.currentRegion = Roo.lib.Region.getRegion(dom);
26244         this.currentTip.enter();
26245         
26246     },
26247     leave : function(ev)
26248     {
26249         var dom = ev.getTarget();
26250         //Roo.log(['leave',dom]);
26251         if (!this.currentEl) {
26252             return;
26253         }
26254         
26255         
26256         if (dom != this.currentEl.dom) {
26257             return;
26258         }
26259         var xy = ev.getXY();
26260         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26261             return;
26262         }
26263         // only activate leave if mouse cursor is outside... bounding box..
26264         
26265         
26266         
26267         
26268         if (this.currentTip) {
26269             this.currentTip.leave();
26270         }
26271         //Roo.log('clear currentEl');
26272         this.currentEl = false;
26273         
26274         
26275     },
26276     alignment : {
26277         'left' : ['r-l', [-2,0], 'right'],
26278         'right' : ['l-r', [2,0], 'left'],
26279         'bottom' : ['t-b', [0,2], 'top'],
26280         'top' : [ 'b-t', [0,-2], 'bottom']
26281     }
26282     
26283 });
26284
26285
26286 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26287     
26288     
26289     bindEl : false,
26290     
26291     delay : null, // can be { show : 300 , hide: 500}
26292     
26293     timeout : null,
26294     
26295     hoverState : null, //???
26296     
26297     placement : 'bottom', 
26298     
26299     alignment : false,
26300     
26301     getAutoCreate : function(){
26302     
26303         var cfg = {
26304            cls : 'tooltip',
26305            role : 'tooltip',
26306            cn : [
26307                 {
26308                     cls : 'tooltip-arrow'
26309                 },
26310                 {
26311                     cls : 'tooltip-inner'
26312                 }
26313            ]
26314         };
26315         
26316         return cfg;
26317     },
26318     bind : function(el)
26319     {
26320         this.bindEl = el;
26321     },
26322       
26323     
26324     enter : function () {
26325        
26326         if (this.timeout != null) {
26327             clearTimeout(this.timeout);
26328         }
26329         
26330         this.hoverState = 'in';
26331          //Roo.log("enter - show");
26332         if (!this.delay || !this.delay.show) {
26333             this.show();
26334             return;
26335         }
26336         var _t = this;
26337         this.timeout = setTimeout(function () {
26338             if (_t.hoverState == 'in') {
26339                 _t.show();
26340             }
26341         }, this.delay.show);
26342     },
26343     leave : function()
26344     {
26345         clearTimeout(this.timeout);
26346     
26347         this.hoverState = 'out';
26348          if (!this.delay || !this.delay.hide) {
26349             this.hide();
26350             return;
26351         }
26352        
26353         var _t = this;
26354         this.timeout = setTimeout(function () {
26355             //Roo.log("leave - timeout");
26356             
26357             if (_t.hoverState == 'out') {
26358                 _t.hide();
26359                 Roo.bootstrap.Tooltip.currentEl = false;
26360             }
26361         }, delay);
26362     },
26363     
26364     show : function (msg)
26365     {
26366         if (!this.el) {
26367             this.render(document.body);
26368         }
26369         // set content.
26370         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26371         
26372         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26373         
26374         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26375         
26376         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26377         
26378         var placement = typeof this.placement == 'function' ?
26379             this.placement.call(this, this.el, on_el) :
26380             this.placement;
26381             
26382         var autoToken = /\s?auto?\s?/i;
26383         var autoPlace = autoToken.test(placement);
26384         if (autoPlace) {
26385             placement = placement.replace(autoToken, '') || 'top';
26386         }
26387         
26388         //this.el.detach()
26389         //this.el.setXY([0,0]);
26390         this.el.show();
26391         //this.el.dom.style.display='block';
26392         
26393         //this.el.appendTo(on_el);
26394         
26395         var p = this.getPosition();
26396         var box = this.el.getBox();
26397         
26398         if (autoPlace) {
26399             // fixme..
26400         }
26401         
26402         var align = this.alignment[placement];
26403         
26404         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26405         
26406         if(placement == 'top' || placement == 'bottom'){
26407             if(xy[0] < 0){
26408                 placement = 'right';
26409             }
26410             
26411             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26412                 placement = 'left';
26413             }
26414             
26415             var scroll = Roo.select('body', true).first().getScroll();
26416             
26417             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26418                 placement = 'top';
26419             }
26420             
26421             align = this.alignment[placement];
26422         }
26423         
26424         this.el.alignTo(this.bindEl, align[0],align[1]);
26425         //var arrow = this.el.select('.arrow',true).first();
26426         //arrow.set(align[2], 
26427         
26428         this.el.addClass(placement);
26429         
26430         this.el.addClass('in fade');
26431         
26432         this.hoverState = null;
26433         
26434         if (this.el.hasClass('fade')) {
26435             // fade it?
26436         }
26437         
26438     },
26439     hide : function()
26440     {
26441          
26442         if (!this.el) {
26443             return;
26444         }
26445         //this.el.setXY([0,0]);
26446         this.el.removeClass('in');
26447         //this.el.hide();
26448         
26449     }
26450     
26451 });
26452  
26453
26454  /*
26455  * - LGPL
26456  *
26457  * Location Picker
26458  * 
26459  */
26460
26461 /**
26462  * @class Roo.bootstrap.LocationPicker
26463  * @extends Roo.bootstrap.Component
26464  * Bootstrap LocationPicker class
26465  * @cfg {Number} latitude Position when init default 0
26466  * @cfg {Number} longitude Position when init default 0
26467  * @cfg {Number} zoom default 15
26468  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26469  * @cfg {Boolean} mapTypeControl default false
26470  * @cfg {Boolean} disableDoubleClickZoom default false
26471  * @cfg {Boolean} scrollwheel default true
26472  * @cfg {Boolean} streetViewControl default false
26473  * @cfg {Number} radius default 0
26474  * @cfg {String} locationName
26475  * @cfg {Boolean} draggable default true
26476  * @cfg {Boolean} enableAutocomplete default false
26477  * @cfg {Boolean} enableReverseGeocode default true
26478  * @cfg {String} markerTitle
26479  * 
26480  * @constructor
26481  * Create a new LocationPicker
26482  * @param {Object} config The config object
26483  */
26484
26485
26486 Roo.bootstrap.LocationPicker = function(config){
26487     
26488     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26489     
26490     this.addEvents({
26491         /**
26492          * @event initial
26493          * Fires when the picker initialized.
26494          * @param {Roo.bootstrap.LocationPicker} this
26495          * @param {Google Location} location
26496          */
26497         initial : true,
26498         /**
26499          * @event positionchanged
26500          * Fires when the picker position changed.
26501          * @param {Roo.bootstrap.LocationPicker} this
26502          * @param {Google Location} location
26503          */
26504         positionchanged : true,
26505         /**
26506          * @event resize
26507          * Fires when the map resize.
26508          * @param {Roo.bootstrap.LocationPicker} this
26509          */
26510         resize : true,
26511         /**
26512          * @event show
26513          * Fires when the map show.
26514          * @param {Roo.bootstrap.LocationPicker} this
26515          */
26516         show : true,
26517         /**
26518          * @event hide
26519          * Fires when the map hide.
26520          * @param {Roo.bootstrap.LocationPicker} this
26521          */
26522         hide : true,
26523         /**
26524          * @event mapClick
26525          * Fires when click the map.
26526          * @param {Roo.bootstrap.LocationPicker} this
26527          * @param {Map event} e
26528          */
26529         mapClick : true,
26530         /**
26531          * @event mapRightClick
26532          * Fires when right click the map.
26533          * @param {Roo.bootstrap.LocationPicker} this
26534          * @param {Map event} e
26535          */
26536         mapRightClick : true,
26537         /**
26538          * @event markerClick
26539          * Fires when click the marker.
26540          * @param {Roo.bootstrap.LocationPicker} this
26541          * @param {Map event} e
26542          */
26543         markerClick : true,
26544         /**
26545          * @event markerRightClick
26546          * Fires when right click the marker.
26547          * @param {Roo.bootstrap.LocationPicker} this
26548          * @param {Map event} e
26549          */
26550         markerRightClick : true,
26551         /**
26552          * @event OverlayViewDraw
26553          * Fires when OverlayView Draw
26554          * @param {Roo.bootstrap.LocationPicker} this
26555          */
26556         OverlayViewDraw : true,
26557         /**
26558          * @event OverlayViewOnAdd
26559          * Fires when OverlayView Draw
26560          * @param {Roo.bootstrap.LocationPicker} this
26561          */
26562         OverlayViewOnAdd : true,
26563         /**
26564          * @event OverlayViewOnRemove
26565          * Fires when OverlayView Draw
26566          * @param {Roo.bootstrap.LocationPicker} this
26567          */
26568         OverlayViewOnRemove : true,
26569         /**
26570          * @event OverlayViewShow
26571          * Fires when OverlayView Draw
26572          * @param {Roo.bootstrap.LocationPicker} this
26573          * @param {Pixel} cpx
26574          */
26575         OverlayViewShow : true,
26576         /**
26577          * @event OverlayViewHide
26578          * Fires when OverlayView Draw
26579          * @param {Roo.bootstrap.LocationPicker} this
26580          */
26581         OverlayViewHide : true,
26582         /**
26583          * @event loadexception
26584          * Fires when load google lib failed.
26585          * @param {Roo.bootstrap.LocationPicker} this
26586          */
26587         loadexception : true
26588     });
26589         
26590 };
26591
26592 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26593     
26594     gMapContext: false,
26595     
26596     latitude: 0,
26597     longitude: 0,
26598     zoom: 15,
26599     mapTypeId: false,
26600     mapTypeControl: false,
26601     disableDoubleClickZoom: false,
26602     scrollwheel: true,
26603     streetViewControl: false,
26604     radius: 0,
26605     locationName: '',
26606     draggable: true,
26607     enableAutocomplete: false,
26608     enableReverseGeocode: true,
26609     markerTitle: '',
26610     
26611     getAutoCreate: function()
26612     {
26613
26614         var cfg = {
26615             tag: 'div',
26616             cls: 'roo-location-picker'
26617         };
26618         
26619         return cfg
26620     },
26621     
26622     initEvents: function(ct, position)
26623     {       
26624         if(!this.el.getWidth() || this.isApplied()){
26625             return;
26626         }
26627         
26628         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26629         
26630         this.initial();
26631     },
26632     
26633     initial: function()
26634     {
26635         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26636             this.fireEvent('loadexception', this);
26637             return;
26638         }
26639         
26640         if(!this.mapTypeId){
26641             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26642         }
26643         
26644         this.gMapContext = this.GMapContext();
26645         
26646         this.initOverlayView();
26647         
26648         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26649         
26650         var _this = this;
26651                 
26652         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26653             _this.setPosition(_this.gMapContext.marker.position);
26654         });
26655         
26656         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26657             _this.fireEvent('mapClick', this, event);
26658             
26659         });
26660
26661         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26662             _this.fireEvent('mapRightClick', this, event);
26663             
26664         });
26665         
26666         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26667             _this.fireEvent('markerClick', this, event);
26668             
26669         });
26670
26671         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26672             _this.fireEvent('markerRightClick', this, event);
26673             
26674         });
26675         
26676         this.setPosition(this.gMapContext.location);
26677         
26678         this.fireEvent('initial', this, this.gMapContext.location);
26679     },
26680     
26681     initOverlayView: function()
26682     {
26683         var _this = this;
26684         
26685         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26686             
26687             draw: function()
26688             {
26689                 _this.fireEvent('OverlayViewDraw', _this);
26690             },
26691             
26692             onAdd: function()
26693             {
26694                 _this.fireEvent('OverlayViewOnAdd', _this);
26695             },
26696             
26697             onRemove: function()
26698             {
26699                 _this.fireEvent('OverlayViewOnRemove', _this);
26700             },
26701             
26702             show: function(cpx)
26703             {
26704                 _this.fireEvent('OverlayViewShow', _this, cpx);
26705             },
26706             
26707             hide: function()
26708             {
26709                 _this.fireEvent('OverlayViewHide', _this);
26710             }
26711             
26712         });
26713     },
26714     
26715     fromLatLngToContainerPixel: function(event)
26716     {
26717         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26718     },
26719     
26720     isApplied: function() 
26721     {
26722         return this.getGmapContext() == false ? false : true;
26723     },
26724     
26725     getGmapContext: function() 
26726     {
26727         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26728     },
26729     
26730     GMapContext: function() 
26731     {
26732         var position = new google.maps.LatLng(this.latitude, this.longitude);
26733         
26734         var _map = new google.maps.Map(this.el.dom, {
26735             center: position,
26736             zoom: this.zoom,
26737             mapTypeId: this.mapTypeId,
26738             mapTypeControl: this.mapTypeControl,
26739             disableDoubleClickZoom: this.disableDoubleClickZoom,
26740             scrollwheel: this.scrollwheel,
26741             streetViewControl: this.streetViewControl,
26742             locationName: this.locationName,
26743             draggable: this.draggable,
26744             enableAutocomplete: this.enableAutocomplete,
26745             enableReverseGeocode: this.enableReverseGeocode
26746         });
26747         
26748         var _marker = new google.maps.Marker({
26749             position: position,
26750             map: _map,
26751             title: this.markerTitle,
26752             draggable: this.draggable
26753         });
26754         
26755         return {
26756             map: _map,
26757             marker: _marker,
26758             circle: null,
26759             location: position,
26760             radius: this.radius,
26761             locationName: this.locationName,
26762             addressComponents: {
26763                 formatted_address: null,
26764                 addressLine1: null,
26765                 addressLine2: null,
26766                 streetName: null,
26767                 streetNumber: null,
26768                 city: null,
26769                 district: null,
26770                 state: null,
26771                 stateOrProvince: null
26772             },
26773             settings: this,
26774             domContainer: this.el.dom,
26775             geodecoder: new google.maps.Geocoder()
26776         };
26777     },
26778     
26779     drawCircle: function(center, radius, options) 
26780     {
26781         if (this.gMapContext.circle != null) {
26782             this.gMapContext.circle.setMap(null);
26783         }
26784         if (radius > 0) {
26785             radius *= 1;
26786             options = Roo.apply({}, options, {
26787                 strokeColor: "#0000FF",
26788                 strokeOpacity: .35,
26789                 strokeWeight: 2,
26790                 fillColor: "#0000FF",
26791                 fillOpacity: .2
26792             });
26793             
26794             options.map = this.gMapContext.map;
26795             options.radius = radius;
26796             options.center = center;
26797             this.gMapContext.circle = new google.maps.Circle(options);
26798             return this.gMapContext.circle;
26799         }
26800         
26801         return null;
26802     },
26803     
26804     setPosition: function(location) 
26805     {
26806         this.gMapContext.location = location;
26807         this.gMapContext.marker.setPosition(location);
26808         this.gMapContext.map.panTo(location);
26809         this.drawCircle(location, this.gMapContext.radius, {});
26810         
26811         var _this = this;
26812         
26813         if (this.gMapContext.settings.enableReverseGeocode) {
26814             this.gMapContext.geodecoder.geocode({
26815                 latLng: this.gMapContext.location
26816             }, function(results, status) {
26817                 
26818                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26819                     _this.gMapContext.locationName = results[0].formatted_address;
26820                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26821                     
26822                     _this.fireEvent('positionchanged', this, location);
26823                 }
26824             });
26825             
26826             return;
26827         }
26828         
26829         this.fireEvent('positionchanged', this, location);
26830     },
26831     
26832     resize: function()
26833     {
26834         google.maps.event.trigger(this.gMapContext.map, "resize");
26835         
26836         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26837         
26838         this.fireEvent('resize', this);
26839     },
26840     
26841     setPositionByLatLng: function(latitude, longitude)
26842     {
26843         this.setPosition(new google.maps.LatLng(latitude, longitude));
26844     },
26845     
26846     getCurrentPosition: function() 
26847     {
26848         return {
26849             latitude: this.gMapContext.location.lat(),
26850             longitude: this.gMapContext.location.lng()
26851         };
26852     },
26853     
26854     getAddressName: function() 
26855     {
26856         return this.gMapContext.locationName;
26857     },
26858     
26859     getAddressComponents: function() 
26860     {
26861         return this.gMapContext.addressComponents;
26862     },
26863     
26864     address_component_from_google_geocode: function(address_components) 
26865     {
26866         var result = {};
26867         
26868         for (var i = 0; i < address_components.length; i++) {
26869             var component = address_components[i];
26870             if (component.types.indexOf("postal_code") >= 0) {
26871                 result.postalCode = component.short_name;
26872             } else if (component.types.indexOf("street_number") >= 0) {
26873                 result.streetNumber = component.short_name;
26874             } else if (component.types.indexOf("route") >= 0) {
26875                 result.streetName = component.short_name;
26876             } else if (component.types.indexOf("neighborhood") >= 0) {
26877                 result.city = component.short_name;
26878             } else if (component.types.indexOf("locality") >= 0) {
26879                 result.city = component.short_name;
26880             } else if (component.types.indexOf("sublocality") >= 0) {
26881                 result.district = component.short_name;
26882             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26883                 result.stateOrProvince = component.short_name;
26884             } else if (component.types.indexOf("country") >= 0) {
26885                 result.country = component.short_name;
26886             }
26887         }
26888         
26889         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26890         result.addressLine2 = "";
26891         return result;
26892     },
26893     
26894     setZoomLevel: function(zoom)
26895     {
26896         this.gMapContext.map.setZoom(zoom);
26897     },
26898     
26899     show: function()
26900     {
26901         if(!this.el){
26902             return;
26903         }
26904         
26905         this.el.show();
26906         
26907         this.resize();
26908         
26909         this.fireEvent('show', this);
26910     },
26911     
26912     hide: function()
26913     {
26914         if(!this.el){
26915             return;
26916         }
26917         
26918         this.el.hide();
26919         
26920         this.fireEvent('hide', this);
26921     }
26922     
26923 });
26924
26925 Roo.apply(Roo.bootstrap.LocationPicker, {
26926     
26927     OverlayView : function(map, options)
26928     {
26929         options = options || {};
26930         
26931         this.setMap(map);
26932     }
26933     
26934     
26935 });/*
26936  * - LGPL
26937  *
26938  * Alert
26939  * 
26940  */
26941
26942 /**
26943  * @class Roo.bootstrap.Alert
26944  * @extends Roo.bootstrap.Component
26945  * Bootstrap Alert class
26946  * @cfg {String} title The title of alert
26947  * @cfg {String} html The content of alert
26948  * @cfg {String} weight (  success | info | warning | danger )
26949  * @cfg {String} faicon font-awesomeicon
26950  * 
26951  * @constructor
26952  * Create a new alert
26953  * @param {Object} config The config object
26954  */
26955
26956
26957 Roo.bootstrap.Alert = function(config){
26958     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26959     
26960 };
26961
26962 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26963     
26964     title: '',
26965     html: '',
26966     weight: false,
26967     faicon: false,
26968     
26969     getAutoCreate : function()
26970     {
26971         
26972         var cfg = {
26973             tag : 'div',
26974             cls : 'alert',
26975             cn : [
26976                 {
26977                     tag : 'i',
26978                     cls : 'roo-alert-icon'
26979                     
26980                 },
26981                 {
26982                     tag : 'b',
26983                     cls : 'roo-alert-title',
26984                     html : this.title
26985                 },
26986                 {
26987                     tag : 'span',
26988                     cls : 'roo-alert-text',
26989                     html : this.html
26990                 }
26991             ]
26992         };
26993         
26994         if(this.faicon){
26995             cfg.cn[0].cls += ' fa ' + this.faicon;
26996         }
26997         
26998         if(this.weight){
26999             cfg.cls += ' alert-' + this.weight;
27000         }
27001         
27002         return cfg;
27003     },
27004     
27005     initEvents: function() 
27006     {
27007         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27008     },
27009     
27010     setTitle : function(str)
27011     {
27012         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27013     },
27014     
27015     setText : function(str)
27016     {
27017         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27018     },
27019     
27020     setWeight : function(weight)
27021     {
27022         if(this.weight){
27023             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27024         }
27025         
27026         this.weight = weight;
27027         
27028         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27029     },
27030     
27031     setIcon : function(icon)
27032     {
27033         if(this.faicon){
27034             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27035         }
27036         
27037         this.faicon = icon;
27038         
27039         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27040     },
27041     
27042     hide: function() 
27043     {
27044         this.el.hide();   
27045     },
27046     
27047     show: function() 
27048     {  
27049         this.el.show();   
27050     }
27051     
27052 });
27053
27054  
27055 /*
27056 * Licence: LGPL
27057 */
27058
27059 /**
27060  * @class Roo.bootstrap.UploadCropbox
27061  * @extends Roo.bootstrap.Component
27062  * Bootstrap UploadCropbox class
27063  * @cfg {String} emptyText show when image has been loaded
27064  * @cfg {String} rotateNotify show when image too small to rotate
27065  * @cfg {Number} errorTimeout default 3000
27066  * @cfg {Number} minWidth default 300
27067  * @cfg {Number} minHeight default 300
27068  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27069  * @cfg {Boolean} isDocument (true|false) default false
27070  * @cfg {String} url action url
27071  * @cfg {String} paramName default 'imageUpload'
27072  * @cfg {String} method default POST
27073  * @cfg {Boolean} loadMask (true|false) default true
27074  * @cfg {Boolean} loadingText default 'Loading...'
27075  * 
27076  * @constructor
27077  * Create a new UploadCropbox
27078  * @param {Object} config The config object
27079  */
27080
27081 Roo.bootstrap.UploadCropbox = function(config){
27082     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27083     
27084     this.addEvents({
27085         /**
27086          * @event beforeselectfile
27087          * Fire before select file
27088          * @param {Roo.bootstrap.UploadCropbox} this
27089          */
27090         "beforeselectfile" : true,
27091         /**
27092          * @event initial
27093          * Fire after initEvent
27094          * @param {Roo.bootstrap.UploadCropbox} this
27095          */
27096         "initial" : true,
27097         /**
27098          * @event crop
27099          * Fire after initEvent
27100          * @param {Roo.bootstrap.UploadCropbox} this
27101          * @param {String} data
27102          */
27103         "crop" : true,
27104         /**
27105          * @event prepare
27106          * Fire when preparing the file data
27107          * @param {Roo.bootstrap.UploadCropbox} this
27108          * @param {Object} file
27109          */
27110         "prepare" : true,
27111         /**
27112          * @event exception
27113          * Fire when get exception
27114          * @param {Roo.bootstrap.UploadCropbox} this
27115          * @param {XMLHttpRequest} xhr
27116          */
27117         "exception" : true,
27118         /**
27119          * @event beforeloadcanvas
27120          * Fire before load the canvas
27121          * @param {Roo.bootstrap.UploadCropbox} this
27122          * @param {String} src
27123          */
27124         "beforeloadcanvas" : true,
27125         /**
27126          * @event trash
27127          * Fire when trash image
27128          * @param {Roo.bootstrap.UploadCropbox} this
27129          */
27130         "trash" : true,
27131         /**
27132          * @event download
27133          * Fire when download the image
27134          * @param {Roo.bootstrap.UploadCropbox} this
27135          */
27136         "download" : true,
27137         /**
27138          * @event footerbuttonclick
27139          * Fire when footerbuttonclick
27140          * @param {Roo.bootstrap.UploadCropbox} this
27141          * @param {String} type
27142          */
27143         "footerbuttonclick" : true,
27144         /**
27145          * @event resize
27146          * Fire when resize
27147          * @param {Roo.bootstrap.UploadCropbox} this
27148          */
27149         "resize" : true,
27150         /**
27151          * @event rotate
27152          * Fire when rotate the image
27153          * @param {Roo.bootstrap.UploadCropbox} this
27154          * @param {String} pos
27155          */
27156         "rotate" : true,
27157         /**
27158          * @event inspect
27159          * Fire when inspect the file
27160          * @param {Roo.bootstrap.UploadCropbox} this
27161          * @param {Object} file
27162          */
27163         "inspect" : true,
27164         /**
27165          * @event upload
27166          * Fire when xhr upload the file
27167          * @param {Roo.bootstrap.UploadCropbox} this
27168          * @param {Object} data
27169          */
27170         "upload" : true,
27171         /**
27172          * @event arrange
27173          * Fire when arrange the file data
27174          * @param {Roo.bootstrap.UploadCropbox} this
27175          * @param {Object} formData
27176          */
27177         "arrange" : true
27178     });
27179     
27180     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27181 };
27182
27183 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27184     
27185     emptyText : 'Click to upload image',
27186     rotateNotify : 'Image is too small to rotate',
27187     errorTimeout : 3000,
27188     scale : 0,
27189     baseScale : 1,
27190     rotate : 0,
27191     dragable : false,
27192     pinching : false,
27193     mouseX : 0,
27194     mouseY : 0,
27195     cropData : false,
27196     minWidth : 300,
27197     minHeight : 300,
27198     file : false,
27199     exif : {},
27200     baseRotate : 1,
27201     cropType : 'image/jpeg',
27202     buttons : false,
27203     canvasLoaded : false,
27204     isDocument : false,
27205     method : 'POST',
27206     paramName : 'imageUpload',
27207     loadMask : true,
27208     loadingText : 'Loading...',
27209     maskEl : false,
27210     
27211     getAutoCreate : function()
27212     {
27213         var cfg = {
27214             tag : 'div',
27215             cls : 'roo-upload-cropbox',
27216             cn : [
27217                 {
27218                     tag : 'input',
27219                     cls : 'roo-upload-cropbox-selector',
27220                     type : 'file'
27221                 },
27222                 {
27223                     tag : 'div',
27224                     cls : 'roo-upload-cropbox-body',
27225                     style : 'cursor:pointer',
27226                     cn : [
27227                         {
27228                             tag : 'div',
27229                             cls : 'roo-upload-cropbox-preview'
27230                         },
27231                         {
27232                             tag : 'div',
27233                             cls : 'roo-upload-cropbox-thumb'
27234                         },
27235                         {
27236                             tag : 'div',
27237                             cls : 'roo-upload-cropbox-empty-notify',
27238                             html : this.emptyText
27239                         },
27240                         {
27241                             tag : 'div',
27242                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27243                             html : this.rotateNotify
27244                         }
27245                     ]
27246                 },
27247                 {
27248                     tag : 'div',
27249                     cls : 'roo-upload-cropbox-footer',
27250                     cn : {
27251                         tag : 'div',
27252                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27253                         cn : []
27254                     }
27255                 }
27256             ]
27257         };
27258         
27259         return cfg;
27260     },
27261     
27262     onRender : function(ct, position)
27263     {
27264         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27265         
27266         if (this.buttons.length) {
27267             
27268             Roo.each(this.buttons, function(bb) {
27269                 
27270                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27271                 
27272                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27273                 
27274             }, this);
27275         }
27276         
27277         if(this.loadMask){
27278             this.maskEl = this.el;
27279         }
27280     },
27281     
27282     initEvents : function()
27283     {
27284         this.urlAPI = (window.createObjectURL && window) || 
27285                                 (window.URL && URL.revokeObjectURL && URL) || 
27286                                 (window.webkitURL && webkitURL);
27287                         
27288         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27289         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27290         
27291         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27292         this.selectorEl.hide();
27293         
27294         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27295         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27296         
27297         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27298         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27299         this.thumbEl.hide();
27300         
27301         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27302         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27303         
27304         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27305         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27306         this.errorEl.hide();
27307         
27308         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27309         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27310         this.footerEl.hide();
27311         
27312         this.setThumbBoxSize();
27313         
27314         this.bind();
27315         
27316         this.resize();
27317         
27318         this.fireEvent('initial', this);
27319     },
27320
27321     bind : function()
27322     {
27323         var _this = this;
27324         
27325         window.addEventListener("resize", function() { _this.resize(); } );
27326         
27327         this.bodyEl.on('click', this.beforeSelectFile, this);
27328         
27329         if(Roo.isTouch){
27330             this.bodyEl.on('touchstart', this.onTouchStart, this);
27331             this.bodyEl.on('touchmove', this.onTouchMove, this);
27332             this.bodyEl.on('touchend', this.onTouchEnd, this);
27333         }
27334         
27335         if(!Roo.isTouch){
27336             this.bodyEl.on('mousedown', this.onMouseDown, this);
27337             this.bodyEl.on('mousemove', this.onMouseMove, this);
27338             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27339             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27340             Roo.get(document).on('mouseup', this.onMouseUp, this);
27341         }
27342         
27343         this.selectorEl.on('change', this.onFileSelected, this);
27344     },
27345     
27346     reset : function()
27347     {    
27348         this.scale = 0;
27349         this.baseScale = 1;
27350         this.rotate = 0;
27351         this.baseRotate = 1;
27352         this.dragable = false;
27353         this.pinching = false;
27354         this.mouseX = 0;
27355         this.mouseY = 0;
27356         this.cropData = false;
27357         this.notifyEl.dom.innerHTML = this.emptyText;
27358         
27359         this.selectorEl.dom.value = '';
27360         
27361     },
27362     
27363     resize : function()
27364     {
27365         if(this.fireEvent('resize', this) != false){
27366             this.setThumbBoxPosition();
27367             this.setCanvasPosition();
27368         }
27369     },
27370     
27371     onFooterButtonClick : function(e, el, o, type)
27372     {
27373         switch (type) {
27374             case 'rotate-left' :
27375                 this.onRotateLeft(e);
27376                 break;
27377             case 'rotate-right' :
27378                 this.onRotateRight(e);
27379                 break;
27380             case 'picture' :
27381                 this.beforeSelectFile(e);
27382                 break;
27383             case 'trash' :
27384                 this.trash(e);
27385                 break;
27386             case 'crop' :
27387                 this.crop(e);
27388                 break;
27389             case 'download' :
27390                 this.download(e);
27391                 break;
27392             default :
27393                 break;
27394         }
27395         
27396         this.fireEvent('footerbuttonclick', this, type);
27397     },
27398     
27399     beforeSelectFile : function(e)
27400     {
27401         e.preventDefault();
27402         
27403         if(this.fireEvent('beforeselectfile', this) != false){
27404             this.selectorEl.dom.click();
27405         }
27406     },
27407     
27408     onFileSelected : function(e)
27409     {
27410         e.preventDefault();
27411         
27412         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27413             return;
27414         }
27415         
27416         var file = this.selectorEl.dom.files[0];
27417         
27418         if(this.fireEvent('inspect', this, file) != false){
27419             this.prepare(file);
27420         }
27421         
27422     },
27423     
27424     trash : function(e)
27425     {
27426         this.fireEvent('trash', this);
27427     },
27428     
27429     download : function(e)
27430     {
27431         this.fireEvent('download', this);
27432     },
27433     
27434     loadCanvas : function(src)
27435     {   
27436         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27437             
27438             this.reset();
27439             
27440             this.imageEl = document.createElement('img');
27441             
27442             var _this = this;
27443             
27444             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27445             
27446             this.imageEl.src = src;
27447         }
27448     },
27449     
27450     onLoadCanvas : function()
27451     {   
27452         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27453         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27454         
27455         this.bodyEl.un('click', this.beforeSelectFile, this);
27456         
27457         this.notifyEl.hide();
27458         this.thumbEl.show();
27459         this.footerEl.show();
27460         
27461         this.baseRotateLevel();
27462         
27463         if(this.isDocument){
27464             this.setThumbBoxSize();
27465         }
27466         
27467         this.setThumbBoxPosition();
27468         
27469         this.baseScaleLevel();
27470         
27471         this.draw();
27472         
27473         this.resize();
27474         
27475         this.canvasLoaded = true;
27476         
27477         if(this.loadMask){
27478             this.maskEl.unmask();
27479         }
27480         
27481     },
27482     
27483     setCanvasPosition : function()
27484     {   
27485         if(!this.canvasEl){
27486             return;
27487         }
27488         
27489         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27490         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27491         
27492         this.previewEl.setLeft(pw);
27493         this.previewEl.setTop(ph);
27494         
27495     },
27496     
27497     onMouseDown : function(e)
27498     {   
27499         e.stopEvent();
27500         
27501         this.dragable = true;
27502         this.pinching = false;
27503         
27504         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27505             this.dragable = false;
27506             return;
27507         }
27508         
27509         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27510         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27511         
27512     },
27513     
27514     onMouseMove : function(e)
27515     {   
27516         e.stopEvent();
27517         
27518         if(!this.canvasLoaded){
27519             return;
27520         }
27521         
27522         if (!this.dragable){
27523             return;
27524         }
27525         
27526         var minX = Math.ceil(this.thumbEl.getLeft(true));
27527         var minY = Math.ceil(this.thumbEl.getTop(true));
27528         
27529         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27530         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27531         
27532         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27533         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27534         
27535         x = x - this.mouseX;
27536         y = y - this.mouseY;
27537         
27538         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27539         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27540         
27541         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27542         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27543         
27544         this.previewEl.setLeft(bgX);
27545         this.previewEl.setTop(bgY);
27546         
27547         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27548         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27549     },
27550     
27551     onMouseUp : function(e)
27552     {   
27553         e.stopEvent();
27554         
27555         this.dragable = false;
27556     },
27557     
27558     onMouseWheel : function(e)
27559     {   
27560         e.stopEvent();
27561         
27562         this.startScale = this.scale;
27563         
27564         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27565         
27566         if(!this.zoomable()){
27567             this.scale = this.startScale;
27568             return;
27569         }
27570         
27571         this.draw();
27572         
27573         return;
27574     },
27575     
27576     zoomable : function()
27577     {
27578         var minScale = this.thumbEl.getWidth() / this.minWidth;
27579         
27580         if(this.minWidth < this.minHeight){
27581             minScale = this.thumbEl.getHeight() / this.minHeight;
27582         }
27583         
27584         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27585         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27586         
27587         if(
27588                 this.isDocument &&
27589                 (this.rotate == 0 || this.rotate == 180) && 
27590                 (
27591                     width > this.imageEl.OriginWidth || 
27592                     height > this.imageEl.OriginHeight ||
27593                     (width < this.minWidth && height < this.minHeight)
27594                 )
27595         ){
27596             return false;
27597         }
27598         
27599         if(
27600                 this.isDocument &&
27601                 (this.rotate == 90 || this.rotate == 270) && 
27602                 (
27603                     width > this.imageEl.OriginWidth || 
27604                     height > this.imageEl.OriginHeight ||
27605                     (width < this.minHeight && height < this.minWidth)
27606                 )
27607         ){
27608             return false;
27609         }
27610         
27611         if(
27612                 !this.isDocument &&
27613                 (this.rotate == 0 || this.rotate == 180) && 
27614                 (
27615                     width < this.minWidth || 
27616                     width > this.imageEl.OriginWidth || 
27617                     height < this.minHeight || 
27618                     height > this.imageEl.OriginHeight
27619                 )
27620         ){
27621             return false;
27622         }
27623         
27624         if(
27625                 !this.isDocument &&
27626                 (this.rotate == 90 || this.rotate == 270) && 
27627                 (
27628                     width < this.minHeight || 
27629                     width > this.imageEl.OriginWidth || 
27630                     height < this.minWidth || 
27631                     height > this.imageEl.OriginHeight
27632                 )
27633         ){
27634             return false;
27635         }
27636         
27637         return true;
27638         
27639     },
27640     
27641     onRotateLeft : function(e)
27642     {   
27643         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27644             
27645             var minScale = this.thumbEl.getWidth() / this.minWidth;
27646             
27647             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27648             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27649             
27650             this.startScale = this.scale;
27651             
27652             while (this.getScaleLevel() < minScale){
27653             
27654                 this.scale = this.scale + 1;
27655                 
27656                 if(!this.zoomable()){
27657                     break;
27658                 }
27659                 
27660                 if(
27661                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27662                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27663                 ){
27664                     continue;
27665                 }
27666                 
27667                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27668
27669                 this.draw();
27670                 
27671                 return;
27672             }
27673             
27674             this.scale = this.startScale;
27675             
27676             this.onRotateFail();
27677             
27678             return false;
27679         }
27680         
27681         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27682
27683         if(this.isDocument){
27684             this.setThumbBoxSize();
27685             this.setThumbBoxPosition();
27686             this.setCanvasPosition();
27687         }
27688         
27689         this.draw();
27690         
27691         this.fireEvent('rotate', this, 'left');
27692         
27693     },
27694     
27695     onRotateRight : function(e)
27696     {
27697         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27698             
27699             var minScale = this.thumbEl.getWidth() / this.minWidth;
27700         
27701             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27702             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27703             
27704             this.startScale = this.scale;
27705             
27706             while (this.getScaleLevel() < minScale){
27707             
27708                 this.scale = this.scale + 1;
27709                 
27710                 if(!this.zoomable()){
27711                     break;
27712                 }
27713                 
27714                 if(
27715                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27716                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27717                 ){
27718                     continue;
27719                 }
27720                 
27721                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27722
27723                 this.draw();
27724                 
27725                 return;
27726             }
27727             
27728             this.scale = this.startScale;
27729             
27730             this.onRotateFail();
27731             
27732             return false;
27733         }
27734         
27735         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27736
27737         if(this.isDocument){
27738             this.setThumbBoxSize();
27739             this.setThumbBoxPosition();
27740             this.setCanvasPosition();
27741         }
27742         
27743         this.draw();
27744         
27745         this.fireEvent('rotate', this, 'right');
27746     },
27747     
27748     onRotateFail : function()
27749     {
27750         this.errorEl.show(true);
27751         
27752         var _this = this;
27753         
27754         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27755     },
27756     
27757     draw : function()
27758     {
27759         this.previewEl.dom.innerHTML = '';
27760         
27761         var canvasEl = document.createElement("canvas");
27762         
27763         var contextEl = canvasEl.getContext("2d");
27764         
27765         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27766         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27767         var center = this.imageEl.OriginWidth / 2;
27768         
27769         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27770             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27771             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27772             center = this.imageEl.OriginHeight / 2;
27773         }
27774         
27775         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27776         
27777         contextEl.translate(center, center);
27778         contextEl.rotate(this.rotate * Math.PI / 180);
27779
27780         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27781         
27782         this.canvasEl = document.createElement("canvas");
27783         
27784         this.contextEl = this.canvasEl.getContext("2d");
27785         
27786         switch (this.rotate) {
27787             case 0 :
27788                 
27789                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27790                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27791                 
27792                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27793                 
27794                 break;
27795             case 90 : 
27796                 
27797                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27798                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27799                 
27800                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27801                     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);
27802                     break;
27803                 }
27804                 
27805                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27806                 
27807                 break;
27808             case 180 :
27809                 
27810                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27811                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27812                 
27813                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27814                     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);
27815                     break;
27816                 }
27817                 
27818                 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);
27819                 
27820                 break;
27821             case 270 :
27822                 
27823                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27824                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27825         
27826                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27827                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27828                     break;
27829                 }
27830                 
27831                 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);
27832                 
27833                 break;
27834             default : 
27835                 break;
27836         }
27837         
27838         this.previewEl.appendChild(this.canvasEl);
27839         
27840         this.setCanvasPosition();
27841     },
27842     
27843     crop : function()
27844     {
27845         if(!this.canvasLoaded){
27846             return;
27847         }
27848         
27849         var imageCanvas = document.createElement("canvas");
27850         
27851         var imageContext = imageCanvas.getContext("2d");
27852         
27853         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27854         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27855         
27856         var center = imageCanvas.width / 2;
27857         
27858         imageContext.translate(center, center);
27859         
27860         imageContext.rotate(this.rotate * Math.PI / 180);
27861         
27862         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27863         
27864         var canvas = document.createElement("canvas");
27865         
27866         var context = canvas.getContext("2d");
27867                 
27868         canvas.width = this.minWidth;
27869         canvas.height = this.minHeight;
27870
27871         switch (this.rotate) {
27872             case 0 :
27873                 
27874                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27875                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27876                 
27877                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27878                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27879                 
27880                 var targetWidth = this.minWidth - 2 * x;
27881                 var targetHeight = this.minHeight - 2 * y;
27882                 
27883                 var scale = 1;
27884                 
27885                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27886                     scale = targetWidth / width;
27887                 }
27888                 
27889                 if(x > 0 && y == 0){
27890                     scale = targetHeight / height;
27891                 }
27892                 
27893                 if(x > 0 && y > 0){
27894                     scale = targetWidth / width;
27895                     
27896                     if(width < height){
27897                         scale = targetHeight / height;
27898                     }
27899                 }
27900                 
27901                 context.scale(scale, scale);
27902                 
27903                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27904                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27905
27906                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27907                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27908
27909                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27910                 
27911                 break;
27912             case 90 : 
27913                 
27914                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27915                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27916                 
27917                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27918                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27919                 
27920                 var targetWidth = this.minWidth - 2 * x;
27921                 var targetHeight = this.minHeight - 2 * y;
27922                 
27923                 var scale = 1;
27924                 
27925                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27926                     scale = targetWidth / width;
27927                 }
27928                 
27929                 if(x > 0 && y == 0){
27930                     scale = targetHeight / height;
27931                 }
27932                 
27933                 if(x > 0 && y > 0){
27934                     scale = targetWidth / width;
27935                     
27936                     if(width < height){
27937                         scale = targetHeight / height;
27938                     }
27939                 }
27940                 
27941                 context.scale(scale, scale);
27942                 
27943                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27944                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27945
27946                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27947                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27948                 
27949                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27950                 
27951                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27952                 
27953                 break;
27954             case 180 :
27955                 
27956                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27957                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27958                 
27959                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27960                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27961                 
27962                 var targetWidth = this.minWidth - 2 * x;
27963                 var targetHeight = this.minHeight - 2 * y;
27964                 
27965                 var scale = 1;
27966                 
27967                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27968                     scale = targetWidth / width;
27969                 }
27970                 
27971                 if(x > 0 && y == 0){
27972                     scale = targetHeight / height;
27973                 }
27974                 
27975                 if(x > 0 && y > 0){
27976                     scale = targetWidth / width;
27977                     
27978                     if(width < height){
27979                         scale = targetHeight / height;
27980                     }
27981                 }
27982                 
27983                 context.scale(scale, scale);
27984                 
27985                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27986                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27987
27988                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27989                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27990
27991                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27992                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27993                 
27994                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27995                 
27996                 break;
27997             case 270 :
27998                 
27999                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28000                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28001                 
28002                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28003                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28004                 
28005                 var targetWidth = this.minWidth - 2 * x;
28006                 var targetHeight = this.minHeight - 2 * y;
28007                 
28008                 var scale = 1;
28009                 
28010                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28011                     scale = targetWidth / width;
28012                 }
28013                 
28014                 if(x > 0 && y == 0){
28015                     scale = targetHeight / height;
28016                 }
28017                 
28018                 if(x > 0 && y > 0){
28019                     scale = targetWidth / width;
28020                     
28021                     if(width < height){
28022                         scale = targetHeight / height;
28023                     }
28024                 }
28025                 
28026                 context.scale(scale, scale);
28027                 
28028                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28029                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28030
28031                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28032                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28033                 
28034                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28035                 
28036                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28037                 
28038                 break;
28039             default : 
28040                 break;
28041         }
28042         
28043         this.cropData = canvas.toDataURL(this.cropType);
28044         
28045         if(this.fireEvent('crop', this, this.cropData) !== false){
28046             this.process(this.file, this.cropData);
28047         }
28048         
28049         return;
28050         
28051     },
28052     
28053     setThumbBoxSize : function()
28054     {
28055         var width, height;
28056         
28057         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28058             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28059             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28060             
28061             this.minWidth = width;
28062             this.minHeight = height;
28063             
28064             if(this.rotate == 90 || this.rotate == 270){
28065                 this.minWidth = height;
28066                 this.minHeight = width;
28067             }
28068         }
28069         
28070         height = 300;
28071         width = Math.ceil(this.minWidth * height / this.minHeight);
28072         
28073         if(this.minWidth > this.minHeight){
28074             width = 300;
28075             height = Math.ceil(this.minHeight * width / this.minWidth);
28076         }
28077         
28078         this.thumbEl.setStyle({
28079             width : width + 'px',
28080             height : height + 'px'
28081         });
28082
28083         return;
28084             
28085     },
28086     
28087     setThumbBoxPosition : function()
28088     {
28089         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28090         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28091         
28092         this.thumbEl.setLeft(x);
28093         this.thumbEl.setTop(y);
28094         
28095     },
28096     
28097     baseRotateLevel : function()
28098     {
28099         this.baseRotate = 1;
28100         
28101         if(
28102                 typeof(this.exif) != 'undefined' &&
28103                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28104                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28105         ){
28106             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28107         }
28108         
28109         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28110         
28111     },
28112     
28113     baseScaleLevel : function()
28114     {
28115         var width, height;
28116         
28117         if(this.isDocument){
28118             
28119             if(this.baseRotate == 6 || this.baseRotate == 8){
28120             
28121                 height = this.thumbEl.getHeight();
28122                 this.baseScale = height / this.imageEl.OriginWidth;
28123
28124                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28125                     width = this.thumbEl.getWidth();
28126                     this.baseScale = width / this.imageEl.OriginHeight;
28127                 }
28128
28129                 return;
28130             }
28131
28132             height = this.thumbEl.getHeight();
28133             this.baseScale = height / this.imageEl.OriginHeight;
28134
28135             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28136                 width = this.thumbEl.getWidth();
28137                 this.baseScale = width / this.imageEl.OriginWidth;
28138             }
28139
28140             return;
28141         }
28142         
28143         if(this.baseRotate == 6 || this.baseRotate == 8){
28144             
28145             width = this.thumbEl.getHeight();
28146             this.baseScale = width / this.imageEl.OriginHeight;
28147             
28148             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28149                 height = this.thumbEl.getWidth();
28150                 this.baseScale = height / this.imageEl.OriginHeight;
28151             }
28152             
28153             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28154                 height = this.thumbEl.getWidth();
28155                 this.baseScale = height / this.imageEl.OriginHeight;
28156                 
28157                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28158                     width = this.thumbEl.getHeight();
28159                     this.baseScale = width / this.imageEl.OriginWidth;
28160                 }
28161             }
28162             
28163             return;
28164         }
28165         
28166         width = this.thumbEl.getWidth();
28167         this.baseScale = width / this.imageEl.OriginWidth;
28168         
28169         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28170             height = this.thumbEl.getHeight();
28171             this.baseScale = height / this.imageEl.OriginHeight;
28172         }
28173         
28174         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28175             
28176             height = this.thumbEl.getHeight();
28177             this.baseScale = height / this.imageEl.OriginHeight;
28178             
28179             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28180                 width = this.thumbEl.getWidth();
28181                 this.baseScale = width / this.imageEl.OriginWidth;
28182             }
28183             
28184         }
28185         
28186         return;
28187     },
28188     
28189     getScaleLevel : function()
28190     {
28191         return this.baseScale * Math.pow(1.1, this.scale);
28192     },
28193     
28194     onTouchStart : function(e)
28195     {
28196         if(!this.canvasLoaded){
28197             this.beforeSelectFile(e);
28198             return;
28199         }
28200         
28201         var touches = e.browserEvent.touches;
28202         
28203         if(!touches){
28204             return;
28205         }
28206         
28207         if(touches.length == 1){
28208             this.onMouseDown(e);
28209             return;
28210         }
28211         
28212         if(touches.length != 2){
28213             return;
28214         }
28215         
28216         var coords = [];
28217         
28218         for(var i = 0, finger; finger = touches[i]; i++){
28219             coords.push(finger.pageX, finger.pageY);
28220         }
28221         
28222         var x = Math.pow(coords[0] - coords[2], 2);
28223         var y = Math.pow(coords[1] - coords[3], 2);
28224         
28225         this.startDistance = Math.sqrt(x + y);
28226         
28227         this.startScale = this.scale;
28228         
28229         this.pinching = true;
28230         this.dragable = false;
28231         
28232     },
28233     
28234     onTouchMove : function(e)
28235     {
28236         if(!this.pinching && !this.dragable){
28237             return;
28238         }
28239         
28240         var touches = e.browserEvent.touches;
28241         
28242         if(!touches){
28243             return;
28244         }
28245         
28246         if(this.dragable){
28247             this.onMouseMove(e);
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.endDistance = Math.sqrt(x + y);
28261         
28262         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28263         
28264         if(!this.zoomable()){
28265             this.scale = this.startScale;
28266             return;
28267         }
28268         
28269         this.draw();
28270         
28271     },
28272     
28273     onTouchEnd : function(e)
28274     {
28275         this.pinching = false;
28276         this.dragable = false;
28277         
28278     },
28279     
28280     process : function(file, crop)
28281     {
28282         if(this.loadMask){
28283             this.maskEl.mask(this.loadingText);
28284         }
28285         
28286         this.xhr = new XMLHttpRequest();
28287         
28288         file.xhr = this.xhr;
28289
28290         this.xhr.open(this.method, this.url, true);
28291         
28292         var headers = {
28293             "Accept": "application/json",
28294             "Cache-Control": "no-cache",
28295             "X-Requested-With": "XMLHttpRequest"
28296         };
28297         
28298         for (var headerName in headers) {
28299             var headerValue = headers[headerName];
28300             if (headerValue) {
28301                 this.xhr.setRequestHeader(headerName, headerValue);
28302             }
28303         }
28304         
28305         var _this = this;
28306         
28307         this.xhr.onload = function()
28308         {
28309             _this.xhrOnLoad(_this.xhr);
28310         }
28311         
28312         this.xhr.onerror = function()
28313         {
28314             _this.xhrOnError(_this.xhr);
28315         }
28316         
28317         var formData = new FormData();
28318
28319         formData.append('returnHTML', 'NO');
28320         
28321         if(crop){
28322             formData.append('crop', crop);
28323         }
28324         
28325         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28326             formData.append(this.paramName, file, file.name);
28327         }
28328         
28329         if(typeof(file.filename) != 'undefined'){
28330             formData.append('filename', file.filename);
28331         }
28332         
28333         if(typeof(file.mimetype) != 'undefined'){
28334             formData.append('mimetype', file.mimetype);
28335         }
28336         
28337         if(this.fireEvent('arrange', this, formData) != false){
28338             this.xhr.send(formData);
28339         };
28340     },
28341     
28342     xhrOnLoad : function(xhr)
28343     {
28344         if(this.loadMask){
28345             this.maskEl.unmask();
28346         }
28347         
28348         if (xhr.readyState !== 4) {
28349             this.fireEvent('exception', this, xhr);
28350             return;
28351         }
28352
28353         var response = Roo.decode(xhr.responseText);
28354         
28355         if(!response.success){
28356             this.fireEvent('exception', this, xhr);
28357             return;
28358         }
28359         
28360         var response = Roo.decode(xhr.responseText);
28361         
28362         this.fireEvent('upload', this, response);
28363         
28364     },
28365     
28366     xhrOnError : function()
28367     {
28368         if(this.loadMask){
28369             this.maskEl.unmask();
28370         }
28371         
28372         Roo.log('xhr on error');
28373         
28374         var response = Roo.decode(xhr.responseText);
28375           
28376         Roo.log(response);
28377         
28378     },
28379     
28380     prepare : function(file)
28381     {   
28382         if(this.loadMask){
28383             this.maskEl.mask(this.loadingText);
28384         }
28385         
28386         this.file = false;
28387         this.exif = {};
28388         
28389         if(typeof(file) === 'string'){
28390             this.loadCanvas(file);
28391             return;
28392         }
28393         
28394         if(!file || !this.urlAPI){
28395             return;
28396         }
28397         
28398         this.file = file;
28399         this.cropType = file.type;
28400         
28401         var _this = this;
28402         
28403         if(this.fireEvent('prepare', this, this.file) != false){
28404             
28405             var reader = new FileReader();
28406             
28407             reader.onload = function (e) {
28408                 if (e.target.error) {
28409                     Roo.log(e.target.error);
28410                     return;
28411                 }
28412                 
28413                 var buffer = e.target.result,
28414                     dataView = new DataView(buffer),
28415                     offset = 2,
28416                     maxOffset = dataView.byteLength - 4,
28417                     markerBytes,
28418                     markerLength;
28419                 
28420                 if (dataView.getUint16(0) === 0xffd8) {
28421                     while (offset < maxOffset) {
28422                         markerBytes = dataView.getUint16(offset);
28423                         
28424                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28425                             markerLength = dataView.getUint16(offset + 2) + 2;
28426                             if (offset + markerLength > dataView.byteLength) {
28427                                 Roo.log('Invalid meta data: Invalid segment size.');
28428                                 break;
28429                             }
28430                             
28431                             if(markerBytes == 0xffe1){
28432                                 _this.parseExifData(
28433                                     dataView,
28434                                     offset,
28435                                     markerLength
28436                                 );
28437                             }
28438                             
28439                             offset += markerLength;
28440                             
28441                             continue;
28442                         }
28443                         
28444                         break;
28445                     }
28446                     
28447                 }
28448                 
28449                 var url = _this.urlAPI.createObjectURL(_this.file);
28450                 
28451                 _this.loadCanvas(url);
28452                 
28453                 return;
28454             }
28455             
28456             reader.readAsArrayBuffer(this.file);
28457             
28458         }
28459         
28460     },
28461     
28462     parseExifData : function(dataView, offset, length)
28463     {
28464         var tiffOffset = offset + 10,
28465             littleEndian,
28466             dirOffset;
28467     
28468         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28469             // No Exif data, might be XMP data instead
28470             return;
28471         }
28472         
28473         // Check for the ASCII code for "Exif" (0x45786966):
28474         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28475             // No Exif data, might be XMP data instead
28476             return;
28477         }
28478         if (tiffOffset + 8 > dataView.byteLength) {
28479             Roo.log('Invalid Exif data: Invalid segment size.');
28480             return;
28481         }
28482         // Check for the two null bytes:
28483         if (dataView.getUint16(offset + 8) !== 0x0000) {
28484             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28485             return;
28486         }
28487         // Check the byte alignment:
28488         switch (dataView.getUint16(tiffOffset)) {
28489         case 0x4949:
28490             littleEndian = true;
28491             break;
28492         case 0x4D4D:
28493             littleEndian = false;
28494             break;
28495         default:
28496             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28497             return;
28498         }
28499         // Check for the TIFF tag marker (0x002A):
28500         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28501             Roo.log('Invalid Exif data: Missing TIFF marker.');
28502             return;
28503         }
28504         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28505         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28506         
28507         this.parseExifTags(
28508             dataView,
28509             tiffOffset,
28510             tiffOffset + dirOffset,
28511             littleEndian
28512         );
28513     },
28514     
28515     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28516     {
28517         var tagsNumber,
28518             dirEndOffset,
28519             i;
28520         if (dirOffset + 6 > dataView.byteLength) {
28521             Roo.log('Invalid Exif data: Invalid directory offset.');
28522             return;
28523         }
28524         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28525         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28526         if (dirEndOffset + 4 > dataView.byteLength) {
28527             Roo.log('Invalid Exif data: Invalid directory size.');
28528             return;
28529         }
28530         for (i = 0; i < tagsNumber; i += 1) {
28531             this.parseExifTag(
28532                 dataView,
28533                 tiffOffset,
28534                 dirOffset + 2 + 12 * i, // tag offset
28535                 littleEndian
28536             );
28537         }
28538         // Return the offset to the next directory:
28539         return dataView.getUint32(dirEndOffset, littleEndian);
28540     },
28541     
28542     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28543     {
28544         var tag = dataView.getUint16(offset, littleEndian);
28545         
28546         this.exif[tag] = this.getExifValue(
28547             dataView,
28548             tiffOffset,
28549             offset,
28550             dataView.getUint16(offset + 2, littleEndian), // tag type
28551             dataView.getUint32(offset + 4, littleEndian), // tag length
28552             littleEndian
28553         );
28554     },
28555     
28556     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28557     {
28558         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28559             tagSize,
28560             dataOffset,
28561             values,
28562             i,
28563             str,
28564             c;
28565     
28566         if (!tagType) {
28567             Roo.log('Invalid Exif data: Invalid tag type.');
28568             return;
28569         }
28570         
28571         tagSize = tagType.size * length;
28572         // Determine if the value is contained in the dataOffset bytes,
28573         // or if the value at the dataOffset is a pointer to the actual data:
28574         dataOffset = tagSize > 4 ?
28575                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28576         if (dataOffset + tagSize > dataView.byteLength) {
28577             Roo.log('Invalid Exif data: Invalid data offset.');
28578             return;
28579         }
28580         if (length === 1) {
28581             return tagType.getValue(dataView, dataOffset, littleEndian);
28582         }
28583         values = [];
28584         for (i = 0; i < length; i += 1) {
28585             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28586         }
28587         
28588         if (tagType.ascii) {
28589             str = '';
28590             // Concatenate the chars:
28591             for (i = 0; i < values.length; i += 1) {
28592                 c = values[i];
28593                 // Ignore the terminating NULL byte(s):
28594                 if (c === '\u0000') {
28595                     break;
28596                 }
28597                 str += c;
28598             }
28599             return str;
28600         }
28601         return values;
28602     }
28603     
28604 });
28605
28606 Roo.apply(Roo.bootstrap.UploadCropbox, {
28607     tags : {
28608         'Orientation': 0x0112
28609     },
28610     
28611     Orientation: {
28612             1: 0, //'top-left',
28613 //            2: 'top-right',
28614             3: 180, //'bottom-right',
28615 //            4: 'bottom-left',
28616 //            5: 'left-top',
28617             6: 90, //'right-top',
28618 //            7: 'right-bottom',
28619             8: 270 //'left-bottom'
28620     },
28621     
28622     exifTagTypes : {
28623         // byte, 8-bit unsigned int:
28624         1: {
28625             getValue: function (dataView, dataOffset) {
28626                 return dataView.getUint8(dataOffset);
28627             },
28628             size: 1
28629         },
28630         // ascii, 8-bit byte:
28631         2: {
28632             getValue: function (dataView, dataOffset) {
28633                 return String.fromCharCode(dataView.getUint8(dataOffset));
28634             },
28635             size: 1,
28636             ascii: true
28637         },
28638         // short, 16 bit int:
28639         3: {
28640             getValue: function (dataView, dataOffset, littleEndian) {
28641                 return dataView.getUint16(dataOffset, littleEndian);
28642             },
28643             size: 2
28644         },
28645         // long, 32 bit int:
28646         4: {
28647             getValue: function (dataView, dataOffset, littleEndian) {
28648                 return dataView.getUint32(dataOffset, littleEndian);
28649             },
28650             size: 4
28651         },
28652         // rational = two long values, first is numerator, second is denominator:
28653         5: {
28654             getValue: function (dataView, dataOffset, littleEndian) {
28655                 return dataView.getUint32(dataOffset, littleEndian) /
28656                     dataView.getUint32(dataOffset + 4, littleEndian);
28657             },
28658             size: 8
28659         },
28660         // slong, 32 bit signed int:
28661         9: {
28662             getValue: function (dataView, dataOffset, littleEndian) {
28663                 return dataView.getInt32(dataOffset, littleEndian);
28664             },
28665             size: 4
28666         },
28667         // srational, two slongs, first is numerator, second is denominator:
28668         10: {
28669             getValue: function (dataView, dataOffset, littleEndian) {
28670                 return dataView.getInt32(dataOffset, littleEndian) /
28671                     dataView.getInt32(dataOffset + 4, littleEndian);
28672             },
28673             size: 8
28674         }
28675     },
28676     
28677     footer : {
28678         STANDARD : [
28679             {
28680                 tag : 'div',
28681                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28682                 action : 'rotate-left',
28683                 cn : [
28684                     {
28685                         tag : 'button',
28686                         cls : 'btn btn-default',
28687                         html : '<i class="fa fa-undo"></i>'
28688                     }
28689                 ]
28690             },
28691             {
28692                 tag : 'div',
28693                 cls : 'btn-group roo-upload-cropbox-picture',
28694                 action : 'picture',
28695                 cn : [
28696                     {
28697                         tag : 'button',
28698                         cls : 'btn btn-default',
28699                         html : '<i class="fa fa-picture-o"></i>'
28700                     }
28701                 ]
28702             },
28703             {
28704                 tag : 'div',
28705                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28706                 action : 'rotate-right',
28707                 cn : [
28708                     {
28709                         tag : 'button',
28710                         cls : 'btn btn-default',
28711                         html : '<i class="fa fa-repeat"></i>'
28712                     }
28713                 ]
28714             }
28715         ],
28716         DOCUMENT : [
28717             {
28718                 tag : 'div',
28719                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28720                 action : 'rotate-left',
28721                 cn : [
28722                     {
28723                         tag : 'button',
28724                         cls : 'btn btn-default',
28725                         html : '<i class="fa fa-undo"></i>'
28726                     }
28727                 ]
28728             },
28729             {
28730                 tag : 'div',
28731                 cls : 'btn-group roo-upload-cropbox-download',
28732                 action : 'download',
28733                 cn : [
28734                     {
28735                         tag : 'button',
28736                         cls : 'btn btn-default',
28737                         html : '<i class="fa fa-download"></i>'
28738                     }
28739                 ]
28740             },
28741             {
28742                 tag : 'div',
28743                 cls : 'btn-group roo-upload-cropbox-crop',
28744                 action : 'crop',
28745                 cn : [
28746                     {
28747                         tag : 'button',
28748                         cls : 'btn btn-default',
28749                         html : '<i class="fa fa-crop"></i>'
28750                     }
28751                 ]
28752             },
28753             {
28754                 tag : 'div',
28755                 cls : 'btn-group roo-upload-cropbox-trash',
28756                 action : 'trash',
28757                 cn : [
28758                     {
28759                         tag : 'button',
28760                         cls : 'btn btn-default',
28761                         html : '<i class="fa fa-trash"></i>'
28762                     }
28763                 ]
28764             },
28765             {
28766                 tag : 'div',
28767                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28768                 action : 'rotate-right',
28769                 cn : [
28770                     {
28771                         tag : 'button',
28772                         cls : 'btn btn-default',
28773                         html : '<i class="fa fa-repeat"></i>'
28774                     }
28775                 ]
28776             }
28777         ],
28778         ROTATOR : [
28779             {
28780                 tag : 'div',
28781                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28782                 action : 'rotate-left',
28783                 cn : [
28784                     {
28785                         tag : 'button',
28786                         cls : 'btn btn-default',
28787                         html : '<i class="fa fa-undo"></i>'
28788                     }
28789                 ]
28790             },
28791             {
28792                 tag : 'div',
28793                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28794                 action : 'rotate-right',
28795                 cn : [
28796                     {
28797                         tag : 'button',
28798                         cls : 'btn btn-default',
28799                         html : '<i class="fa fa-repeat"></i>'
28800                     }
28801                 ]
28802             }
28803         ]
28804     }
28805 });
28806
28807 /*
28808 * Licence: LGPL
28809 */
28810
28811 /**
28812  * @class Roo.bootstrap.DocumentManager
28813  * @extends Roo.bootstrap.Component
28814  * Bootstrap DocumentManager class
28815  * @cfg {String} paramName default 'imageUpload'
28816  * @cfg {String} toolTipName default 'filename'
28817  * @cfg {String} method default POST
28818  * @cfg {String} url action url
28819  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28820  * @cfg {Boolean} multiple multiple upload default true
28821  * @cfg {Number} thumbSize default 300
28822  * @cfg {String} fieldLabel
28823  * @cfg {Number} labelWidth default 4
28824  * @cfg {String} labelAlign (left|top) default left
28825  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28826 * @cfg {Number} labellg set the width of label (1-12)
28827  * @cfg {Number} labelmd set the width of label (1-12)
28828  * @cfg {Number} labelsm set the width of label (1-12)
28829  * @cfg {Number} labelxs set the width of label (1-12)
28830  * 
28831  * @constructor
28832  * Create a new DocumentManager
28833  * @param {Object} config The config object
28834  */
28835
28836 Roo.bootstrap.DocumentManager = function(config){
28837     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28838     
28839     this.files = [];
28840     this.delegates = [];
28841     
28842     this.addEvents({
28843         /**
28844          * @event initial
28845          * Fire when initial the DocumentManager
28846          * @param {Roo.bootstrap.DocumentManager} this
28847          */
28848         "initial" : true,
28849         /**
28850          * @event inspect
28851          * inspect selected file
28852          * @param {Roo.bootstrap.DocumentManager} this
28853          * @param {File} file
28854          */
28855         "inspect" : true,
28856         /**
28857          * @event exception
28858          * Fire when xhr load exception
28859          * @param {Roo.bootstrap.DocumentManager} this
28860          * @param {XMLHttpRequest} xhr
28861          */
28862         "exception" : true,
28863         /**
28864          * @event afterupload
28865          * Fire when xhr load exception
28866          * @param {Roo.bootstrap.DocumentManager} this
28867          * @param {XMLHttpRequest} xhr
28868          */
28869         "afterupload" : true,
28870         /**
28871          * @event prepare
28872          * prepare the form data
28873          * @param {Roo.bootstrap.DocumentManager} this
28874          * @param {Object} formData
28875          */
28876         "prepare" : true,
28877         /**
28878          * @event remove
28879          * Fire when remove the file
28880          * @param {Roo.bootstrap.DocumentManager} this
28881          * @param {Object} file
28882          */
28883         "remove" : true,
28884         /**
28885          * @event refresh
28886          * Fire after refresh the file
28887          * @param {Roo.bootstrap.DocumentManager} this
28888          */
28889         "refresh" : true,
28890         /**
28891          * @event click
28892          * Fire after click the image
28893          * @param {Roo.bootstrap.DocumentManager} this
28894          * @param {Object} file
28895          */
28896         "click" : true,
28897         /**
28898          * @event edit
28899          * Fire when upload a image and editable set to true
28900          * @param {Roo.bootstrap.DocumentManager} this
28901          * @param {Object} file
28902          */
28903         "edit" : true,
28904         /**
28905          * @event beforeselectfile
28906          * Fire before select file
28907          * @param {Roo.bootstrap.DocumentManager} this
28908          */
28909         "beforeselectfile" : true,
28910         /**
28911          * @event process
28912          * Fire before process file
28913          * @param {Roo.bootstrap.DocumentManager} this
28914          * @param {Object} file
28915          */
28916         "process" : true,
28917         /**
28918          * @event previewrendered
28919          * Fire when preview rendered
28920          * @param {Roo.bootstrap.DocumentManager} this
28921          * @param {Object} file
28922          */
28923         "previewrendered" : true,
28924         /**
28925          */
28926         "previewResize" : true
28927         
28928     });
28929 };
28930
28931 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28932     
28933     boxes : 0,
28934     inputName : '',
28935     thumbSize : 300,
28936     multiple : true,
28937     files : false,
28938     method : 'POST',
28939     url : '',
28940     paramName : 'imageUpload',
28941     toolTipName : 'filename',
28942     fieldLabel : '',
28943     labelWidth : 4,
28944     labelAlign : 'left',
28945     editable : true,
28946     delegates : false,
28947     xhr : false, 
28948     
28949     labellg : 0,
28950     labelmd : 0,
28951     labelsm : 0,
28952     labelxs : 0,
28953     
28954     getAutoCreate : function()
28955     {   
28956         var managerWidget = {
28957             tag : 'div',
28958             cls : 'roo-document-manager',
28959             cn : [
28960                 {
28961                     tag : 'input',
28962                     cls : 'roo-document-manager-selector',
28963                     type : 'file'
28964                 },
28965                 {
28966                     tag : 'div',
28967                     cls : 'roo-document-manager-uploader',
28968                     cn : [
28969                         {
28970                             tag : 'div',
28971                             cls : 'roo-document-manager-upload-btn',
28972                             html : '<i class="fa fa-plus"></i>'
28973                         }
28974                     ]
28975                     
28976                 }
28977             ]
28978         };
28979         
28980         var content = [
28981             {
28982                 tag : 'div',
28983                 cls : 'column col-md-12',
28984                 cn : managerWidget
28985             }
28986         ];
28987         
28988         if(this.fieldLabel.length){
28989             
28990             content = [
28991                 {
28992                     tag : 'div',
28993                     cls : 'column col-md-12',
28994                     html : this.fieldLabel
28995                 },
28996                 {
28997                     tag : 'div',
28998                     cls : 'column col-md-12',
28999                     cn : managerWidget
29000                 }
29001             ];
29002
29003             if(this.labelAlign == 'left'){
29004                 content = [
29005                     {
29006                         tag : 'div',
29007                         cls : 'column',
29008                         html : this.fieldLabel
29009                     },
29010                     {
29011                         tag : 'div',
29012                         cls : 'column',
29013                         cn : managerWidget
29014                     }
29015                 ];
29016                 
29017                 if(this.labelWidth > 12){
29018                     content[0].style = "width: " + this.labelWidth + 'px';
29019                 }
29020
29021                 if(this.labelWidth < 13 && this.labelmd == 0){
29022                     this.labelmd = this.labelWidth;
29023                 }
29024
29025                 if(this.labellg > 0){
29026                     content[0].cls += ' col-lg-' + this.labellg;
29027                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29028                 }
29029
29030                 if(this.labelmd > 0){
29031                     content[0].cls += ' col-md-' + this.labelmd;
29032                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29033                 }
29034
29035                 if(this.labelsm > 0){
29036                     content[0].cls += ' col-sm-' + this.labelsm;
29037                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29038                 }
29039
29040                 if(this.labelxs > 0){
29041                     content[0].cls += ' col-xs-' + this.labelxs;
29042                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29043                 }
29044                 
29045             }
29046         }
29047         
29048         var cfg = {
29049             tag : 'div',
29050             cls : 'row clearfix',
29051             cn : content
29052         };
29053         
29054         return cfg;
29055         
29056     },
29057     
29058     initEvents : function()
29059     {
29060         this.managerEl = this.el.select('.roo-document-manager', true).first();
29061         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29062         
29063         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29064         this.selectorEl.hide();
29065         
29066         if(this.multiple){
29067             this.selectorEl.attr('multiple', 'multiple');
29068         }
29069         
29070         this.selectorEl.on('change', this.onFileSelected, this);
29071         
29072         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29073         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29074         
29075         this.uploader.on('click', this.onUploaderClick, this);
29076         
29077         this.renderProgressDialog();
29078         
29079         var _this = this;
29080         
29081         window.addEventListener("resize", function() { _this.refresh(); } );
29082         
29083         this.fireEvent('initial', this);
29084     },
29085     
29086     renderProgressDialog : function()
29087     {
29088         var _this = this;
29089         
29090         this.progressDialog = new Roo.bootstrap.Modal({
29091             cls : 'roo-document-manager-progress-dialog',
29092             allow_close : false,
29093             title : '',
29094             buttons : [
29095                 {
29096                     name  :'cancel',
29097                     weight : 'danger',
29098                     html : 'Cancel'
29099                 }
29100             ], 
29101             listeners : { 
29102                 btnclick : function() {
29103                     _this.uploadCancel();
29104                     this.hide();
29105                 }
29106             }
29107         });
29108          
29109         this.progressDialog.render(Roo.get(document.body));
29110          
29111         this.progress = new Roo.bootstrap.Progress({
29112             cls : 'roo-document-manager-progress',
29113             active : true,
29114             striped : true
29115         });
29116         
29117         this.progress.render(this.progressDialog.getChildContainer());
29118         
29119         this.progressBar = new Roo.bootstrap.ProgressBar({
29120             cls : 'roo-document-manager-progress-bar',
29121             aria_valuenow : 0,
29122             aria_valuemin : 0,
29123             aria_valuemax : 12,
29124             panel : 'success'
29125         });
29126         
29127         this.progressBar.render(this.progress.getChildContainer());
29128     },
29129     
29130     onUploaderClick : function(e)
29131     {
29132         e.preventDefault();
29133      
29134         if(this.fireEvent('beforeselectfile', this) != false){
29135             this.selectorEl.dom.click();
29136         }
29137         
29138     },
29139     
29140     onFileSelected : function(e)
29141     {
29142         e.preventDefault();
29143         
29144         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29145             return;
29146         }
29147         
29148         Roo.each(this.selectorEl.dom.files, function(file){
29149             if(this.fireEvent('inspect', this, file) != false){
29150                 this.files.push(file);
29151             }
29152         }, this);
29153         
29154         this.queue();
29155         
29156     },
29157     
29158     queue : function()
29159     {
29160         this.selectorEl.dom.value = '';
29161         
29162         if(!this.files || !this.files.length){
29163             return;
29164         }
29165         
29166         if(this.boxes > 0 && this.files.length > this.boxes){
29167             this.files = this.files.slice(0, this.boxes);
29168         }
29169         
29170         this.uploader.show();
29171         
29172         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29173             this.uploader.hide();
29174         }
29175         
29176         var _this = this;
29177         
29178         var files = [];
29179         
29180         var docs = [];
29181         
29182         Roo.each(this.files, function(file){
29183             
29184             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29185                 var f = this.renderPreview(file);
29186                 files.push(f);
29187                 return;
29188             }
29189             
29190             if(file.type.indexOf('image') != -1){
29191                 this.delegates.push(
29192                     (function(){
29193                         _this.process(file);
29194                     }).createDelegate(this)
29195                 );
29196         
29197                 return;
29198             }
29199             
29200             docs.push(
29201                 (function(){
29202                     _this.process(file);
29203                 }).createDelegate(this)
29204             );
29205             
29206         }, this);
29207         
29208         this.files = files;
29209         
29210         this.delegates = this.delegates.concat(docs);
29211         
29212         if(!this.delegates.length){
29213             this.refresh();
29214             return;
29215         }
29216         
29217         this.progressBar.aria_valuemax = this.delegates.length;
29218         
29219         this.arrange();
29220         
29221         return;
29222     },
29223     
29224     arrange : function()
29225     {
29226         if(!this.delegates.length){
29227             this.progressDialog.hide();
29228             this.refresh();
29229             return;
29230         }
29231         
29232         var delegate = this.delegates.shift();
29233         
29234         this.progressDialog.show();
29235         
29236         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29237         
29238         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29239         
29240         delegate();
29241     },
29242     
29243     refresh : function()
29244     {
29245         this.uploader.show();
29246         
29247         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29248             this.uploader.hide();
29249         }
29250         
29251         Roo.isTouch ? this.closable(false) : this.closable(true);
29252         
29253         this.fireEvent('refresh', this);
29254     },
29255     
29256     onRemove : function(e, el, o)
29257     {
29258         e.preventDefault();
29259         
29260         this.fireEvent('remove', this, o);
29261         
29262     },
29263     
29264     remove : function(o)
29265     {
29266         var files = [];
29267         
29268         Roo.each(this.files, function(file){
29269             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29270                 files.push(file);
29271                 return;
29272             }
29273
29274             o.target.remove();
29275
29276         }, this);
29277         
29278         this.files = files;
29279         
29280         this.refresh();
29281     },
29282     
29283     clear : function()
29284     {
29285         Roo.each(this.files, function(file){
29286             if(!file.target){
29287                 return;
29288             }
29289             
29290             file.target.remove();
29291
29292         }, this);
29293         
29294         this.files = [];
29295         
29296         this.refresh();
29297     },
29298     
29299     onClick : function(e, el, o)
29300     {
29301         e.preventDefault();
29302         
29303         this.fireEvent('click', this, o);
29304         
29305     },
29306     
29307     closable : function(closable)
29308     {
29309         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29310             
29311             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29312             
29313             if(closable){
29314                 el.show();
29315                 return;
29316             }
29317             
29318             el.hide();
29319             
29320         }, this);
29321     },
29322     
29323     xhrOnLoad : function(xhr)
29324     {
29325         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29326             el.remove();
29327         }, this);
29328         
29329         if (xhr.readyState !== 4) {
29330             this.arrange();
29331             this.fireEvent('exception', this, xhr);
29332             return;
29333         }
29334
29335         var response = Roo.decode(xhr.responseText);
29336         
29337         if(!response.success){
29338             this.arrange();
29339             this.fireEvent('exception', this, xhr);
29340             return;
29341         }
29342         
29343         var file = this.renderPreview(response.data);
29344         
29345         this.files.push(file);
29346         
29347         this.arrange();
29348         
29349         this.fireEvent('afterupload', this, xhr);
29350         
29351     },
29352     
29353     xhrOnError : function(xhr)
29354     {
29355         Roo.log('xhr on error');
29356         
29357         var response = Roo.decode(xhr.responseText);
29358           
29359         Roo.log(response);
29360         
29361         this.arrange();
29362     },
29363     
29364     process : function(file)
29365     {
29366         if(this.fireEvent('process', this, file) !== false){
29367             if(this.editable && file.type.indexOf('image') != -1){
29368                 this.fireEvent('edit', this, file);
29369                 return;
29370             }
29371
29372             this.uploadStart(file, false);
29373
29374             return;
29375         }
29376         
29377     },
29378     
29379     uploadStart : function(file, crop)
29380     {
29381         this.xhr = new XMLHttpRequest();
29382         
29383         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29384             this.arrange();
29385             return;
29386         }
29387         
29388         file.xhr = this.xhr;
29389             
29390         this.managerEl.createChild({
29391             tag : 'div',
29392             cls : 'roo-document-manager-loading',
29393             cn : [
29394                 {
29395                     tag : 'div',
29396                     tooltip : file.name,
29397                     cls : 'roo-document-manager-thumb',
29398                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29399                 }
29400             ]
29401
29402         });
29403
29404         this.xhr.open(this.method, this.url, true);
29405         
29406         var headers = {
29407             "Accept": "application/json",
29408             "Cache-Control": "no-cache",
29409             "X-Requested-With": "XMLHttpRequest"
29410         };
29411         
29412         for (var headerName in headers) {
29413             var headerValue = headers[headerName];
29414             if (headerValue) {
29415                 this.xhr.setRequestHeader(headerName, headerValue);
29416             }
29417         }
29418         
29419         var _this = this;
29420         
29421         this.xhr.onload = function()
29422         {
29423             _this.xhrOnLoad(_this.xhr);
29424         }
29425         
29426         this.xhr.onerror = function()
29427         {
29428             _this.xhrOnError(_this.xhr);
29429         }
29430         
29431         var formData = new FormData();
29432
29433         formData.append('returnHTML', 'NO');
29434         
29435         if(crop){
29436             formData.append('crop', crop);
29437         }
29438         
29439         formData.append(this.paramName, file, file.name);
29440         
29441         var options = {
29442             file : file, 
29443             manually : false
29444         };
29445         
29446         if(this.fireEvent('prepare', this, formData, options) != false){
29447             
29448             if(options.manually){
29449                 return;
29450             }
29451             
29452             this.xhr.send(formData);
29453             return;
29454         };
29455         
29456         this.uploadCancel();
29457     },
29458     
29459     uploadCancel : function()
29460     {
29461         if (this.xhr) {
29462             this.xhr.abort();
29463         }
29464         
29465         this.delegates = [];
29466         
29467         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29468             el.remove();
29469         }, this);
29470         
29471         this.arrange();
29472     },
29473     
29474     renderPreview : function(file)
29475     {
29476         if(typeof(file.target) != 'undefined' && file.target){
29477             return file;
29478         }
29479         
29480         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29481         
29482         var previewEl = this.managerEl.createChild({
29483             tag : 'div',
29484             cls : 'roo-document-manager-preview',
29485             cn : [
29486                 {
29487                     tag : 'div',
29488                     tooltip : file[this.toolTipName],
29489                     cls : 'roo-document-manager-thumb',
29490                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29491                 },
29492                 {
29493                     tag : 'button',
29494                     cls : 'close',
29495                     html : '<i class="fa fa-times-circle"></i>'
29496                 }
29497             ]
29498         });
29499
29500         var close = previewEl.select('button.close', true).first();
29501
29502         close.on('click', this.onRemove, this, file);
29503
29504         file.target = previewEl;
29505
29506         var image = previewEl.select('img', true).first();
29507         
29508         var _this = this;
29509         
29510         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29511         
29512         image.on('click', this.onClick, this, file);
29513         
29514         this.fireEvent('previewrendered', this, file);
29515         
29516         return file;
29517         
29518     },
29519     
29520     onPreviewLoad : function(file, image)
29521     {
29522         if(typeof(file.target) == 'undefined' || !file.target){
29523             return;
29524         }
29525         
29526         var width = image.dom.naturalWidth || image.dom.width;
29527         var height = image.dom.naturalHeight || image.dom.height;
29528         
29529         if(!this.previewResize) {
29530             return;
29531         }
29532         
29533         if(width > height){
29534             file.target.addClass('wide');
29535             return;
29536         }
29537         
29538         file.target.addClass('tall');
29539         return;
29540         
29541     },
29542     
29543     uploadFromSource : function(file, crop)
29544     {
29545         this.xhr = new XMLHttpRequest();
29546         
29547         this.managerEl.createChild({
29548             tag : 'div',
29549             cls : 'roo-document-manager-loading',
29550             cn : [
29551                 {
29552                     tag : 'div',
29553                     tooltip : file.name,
29554                     cls : 'roo-document-manager-thumb',
29555                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29556                 }
29557             ]
29558
29559         });
29560
29561         this.xhr.open(this.method, this.url, true);
29562         
29563         var headers = {
29564             "Accept": "application/json",
29565             "Cache-Control": "no-cache",
29566             "X-Requested-With": "XMLHttpRequest"
29567         };
29568         
29569         for (var headerName in headers) {
29570             var headerValue = headers[headerName];
29571             if (headerValue) {
29572                 this.xhr.setRequestHeader(headerName, headerValue);
29573             }
29574         }
29575         
29576         var _this = this;
29577         
29578         this.xhr.onload = function()
29579         {
29580             _this.xhrOnLoad(_this.xhr);
29581         }
29582         
29583         this.xhr.onerror = function()
29584         {
29585             _this.xhrOnError(_this.xhr);
29586         }
29587         
29588         var formData = new FormData();
29589
29590         formData.append('returnHTML', 'NO');
29591         
29592         formData.append('crop', crop);
29593         
29594         if(typeof(file.filename) != 'undefined'){
29595             formData.append('filename', file.filename);
29596         }
29597         
29598         if(typeof(file.mimetype) != 'undefined'){
29599             formData.append('mimetype', file.mimetype);
29600         }
29601         
29602         Roo.log(formData);
29603         
29604         if(this.fireEvent('prepare', this, formData) != false){
29605             this.xhr.send(formData);
29606         };
29607     }
29608 });
29609
29610 /*
29611 * Licence: LGPL
29612 */
29613
29614 /**
29615  * @class Roo.bootstrap.DocumentViewer
29616  * @extends Roo.bootstrap.Component
29617  * Bootstrap DocumentViewer class
29618  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29619  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29620  * 
29621  * @constructor
29622  * Create a new DocumentViewer
29623  * @param {Object} config The config object
29624  */
29625
29626 Roo.bootstrap.DocumentViewer = function(config){
29627     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29628     
29629     this.addEvents({
29630         /**
29631          * @event initial
29632          * Fire after initEvent
29633          * @param {Roo.bootstrap.DocumentViewer} this
29634          */
29635         "initial" : true,
29636         /**
29637          * @event click
29638          * Fire after click
29639          * @param {Roo.bootstrap.DocumentViewer} this
29640          */
29641         "click" : true,
29642         /**
29643          * @event download
29644          * Fire after download button
29645          * @param {Roo.bootstrap.DocumentViewer} this
29646          */
29647         "download" : true,
29648         /**
29649          * @event trash
29650          * Fire after trash button
29651          * @param {Roo.bootstrap.DocumentViewer} this
29652          */
29653         "trash" : true
29654         
29655     });
29656 };
29657
29658 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29659     
29660     showDownload : true,
29661     
29662     showTrash : true,
29663     
29664     getAutoCreate : function()
29665     {
29666         var cfg = {
29667             tag : 'div',
29668             cls : 'roo-document-viewer',
29669             cn : [
29670                 {
29671                     tag : 'div',
29672                     cls : 'roo-document-viewer-body',
29673                     cn : [
29674                         {
29675                             tag : 'div',
29676                             cls : 'roo-document-viewer-thumb',
29677                             cn : [
29678                                 {
29679                                     tag : 'img',
29680                                     cls : 'roo-document-viewer-image'
29681                                 }
29682                             ]
29683                         }
29684                     ]
29685                 },
29686                 {
29687                     tag : 'div',
29688                     cls : 'roo-document-viewer-footer',
29689                     cn : {
29690                         tag : 'div',
29691                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29692                         cn : [
29693                             {
29694                                 tag : 'div',
29695                                 cls : 'btn-group roo-document-viewer-download',
29696                                 cn : [
29697                                     {
29698                                         tag : 'button',
29699                                         cls : 'btn btn-default',
29700                                         html : '<i class="fa fa-download"></i>'
29701                                     }
29702                                 ]
29703                             },
29704                             {
29705                                 tag : 'div',
29706                                 cls : 'btn-group roo-document-viewer-trash',
29707                                 cn : [
29708                                     {
29709                                         tag : 'button',
29710                                         cls : 'btn btn-default',
29711                                         html : '<i class="fa fa-trash"></i>'
29712                                     }
29713                                 ]
29714                             }
29715                         ]
29716                     }
29717                 }
29718             ]
29719         };
29720         
29721         return cfg;
29722     },
29723     
29724     initEvents : function()
29725     {
29726         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29727         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29728         
29729         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29730         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29731         
29732         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29733         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29734         
29735         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29736         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29737         
29738         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29739         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29740         
29741         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29742         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29743         
29744         this.bodyEl.on('click', this.onClick, this);
29745         this.downloadBtn.on('click', this.onDownload, this);
29746         this.trashBtn.on('click', this.onTrash, this);
29747         
29748         this.downloadBtn.hide();
29749         this.trashBtn.hide();
29750         
29751         if(this.showDownload){
29752             this.downloadBtn.show();
29753         }
29754         
29755         if(this.showTrash){
29756             this.trashBtn.show();
29757         }
29758         
29759         if(!this.showDownload && !this.showTrash) {
29760             this.footerEl.hide();
29761         }
29762         
29763     },
29764     
29765     initial : function()
29766     {
29767         this.fireEvent('initial', this);
29768         
29769     },
29770     
29771     onClick : function(e)
29772     {
29773         e.preventDefault();
29774         
29775         this.fireEvent('click', this);
29776     },
29777     
29778     onDownload : function(e)
29779     {
29780         e.preventDefault();
29781         
29782         this.fireEvent('download', this);
29783     },
29784     
29785     onTrash : function(e)
29786     {
29787         e.preventDefault();
29788         
29789         this.fireEvent('trash', this);
29790     }
29791     
29792 });
29793 /*
29794  * - LGPL
29795  *
29796  * nav progress bar
29797  * 
29798  */
29799
29800 /**
29801  * @class Roo.bootstrap.NavProgressBar
29802  * @extends Roo.bootstrap.Component
29803  * Bootstrap NavProgressBar class
29804  * 
29805  * @constructor
29806  * Create a new nav progress bar
29807  * @param {Object} config The config object
29808  */
29809
29810 Roo.bootstrap.NavProgressBar = function(config){
29811     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29812
29813     this.bullets = this.bullets || [];
29814    
29815 //    Roo.bootstrap.NavProgressBar.register(this);
29816      this.addEvents({
29817         /**
29818              * @event changed
29819              * Fires when the active item changes
29820              * @param {Roo.bootstrap.NavProgressBar} this
29821              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29822              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29823          */
29824         'changed': true
29825      });
29826     
29827 };
29828
29829 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29830     
29831     bullets : [],
29832     barItems : [],
29833     
29834     getAutoCreate : function()
29835     {
29836         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29837         
29838         cfg = {
29839             tag : 'div',
29840             cls : 'roo-navigation-bar-group',
29841             cn : [
29842                 {
29843                     tag : 'div',
29844                     cls : 'roo-navigation-top-bar'
29845                 },
29846                 {
29847                     tag : 'div',
29848                     cls : 'roo-navigation-bullets-bar',
29849                     cn : [
29850                         {
29851                             tag : 'ul',
29852                             cls : 'roo-navigation-bar'
29853                         }
29854                     ]
29855                 },
29856                 
29857                 {
29858                     tag : 'div',
29859                     cls : 'roo-navigation-bottom-bar'
29860                 }
29861             ]
29862             
29863         };
29864         
29865         return cfg;
29866         
29867     },
29868     
29869     initEvents: function() 
29870     {
29871         
29872     },
29873     
29874     onRender : function(ct, position) 
29875     {
29876         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29877         
29878         if(this.bullets.length){
29879             Roo.each(this.bullets, function(b){
29880                this.addItem(b);
29881             }, this);
29882         }
29883         
29884         this.format();
29885         
29886     },
29887     
29888     addItem : function(cfg)
29889     {
29890         var item = new Roo.bootstrap.NavProgressItem(cfg);
29891         
29892         item.parentId = this.id;
29893         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29894         
29895         if(cfg.html){
29896             var top = new Roo.bootstrap.Element({
29897                 tag : 'div',
29898                 cls : 'roo-navigation-bar-text'
29899             });
29900             
29901             var bottom = new Roo.bootstrap.Element({
29902                 tag : 'div',
29903                 cls : 'roo-navigation-bar-text'
29904             });
29905             
29906             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29907             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29908             
29909             var topText = new Roo.bootstrap.Element({
29910                 tag : 'span',
29911                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29912             });
29913             
29914             var bottomText = new Roo.bootstrap.Element({
29915                 tag : 'span',
29916                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29917             });
29918             
29919             topText.onRender(top.el, null);
29920             bottomText.onRender(bottom.el, null);
29921             
29922             item.topEl = top;
29923             item.bottomEl = bottom;
29924         }
29925         
29926         this.barItems.push(item);
29927         
29928         return item;
29929     },
29930     
29931     getActive : function()
29932     {
29933         var active = false;
29934         
29935         Roo.each(this.barItems, function(v){
29936             
29937             if (!v.isActive()) {
29938                 return;
29939             }
29940             
29941             active = v;
29942             return false;
29943             
29944         });
29945         
29946         return active;
29947     },
29948     
29949     setActiveItem : function(item)
29950     {
29951         var prev = false;
29952         
29953         Roo.each(this.barItems, function(v){
29954             if (v.rid == item.rid) {
29955                 return ;
29956             }
29957             
29958             if (v.isActive()) {
29959                 v.setActive(false);
29960                 prev = v;
29961             }
29962         });
29963
29964         item.setActive(true);
29965         
29966         this.fireEvent('changed', this, item, prev);
29967     },
29968     
29969     getBarItem: function(rid)
29970     {
29971         var ret = false;
29972         
29973         Roo.each(this.barItems, function(e) {
29974             if (e.rid != rid) {
29975                 return;
29976             }
29977             
29978             ret =  e;
29979             return false;
29980         });
29981         
29982         return ret;
29983     },
29984     
29985     indexOfItem : function(item)
29986     {
29987         var index = false;
29988         
29989         Roo.each(this.barItems, function(v, i){
29990             
29991             if (v.rid != item.rid) {
29992                 return;
29993             }
29994             
29995             index = i;
29996             return false
29997         });
29998         
29999         return index;
30000     },
30001     
30002     setActiveNext : function()
30003     {
30004         var i = this.indexOfItem(this.getActive());
30005         
30006         if (i > this.barItems.length) {
30007             return;
30008         }
30009         
30010         this.setActiveItem(this.barItems[i+1]);
30011     },
30012     
30013     setActivePrev : function()
30014     {
30015         var i = this.indexOfItem(this.getActive());
30016         
30017         if (i  < 1) {
30018             return;
30019         }
30020         
30021         this.setActiveItem(this.barItems[i-1]);
30022     },
30023     
30024     format : function()
30025     {
30026         if(!this.barItems.length){
30027             return;
30028         }
30029      
30030         var width = 100 / this.barItems.length;
30031         
30032         Roo.each(this.barItems, function(i){
30033             i.el.setStyle('width', width + '%');
30034             i.topEl.el.setStyle('width', width + '%');
30035             i.bottomEl.el.setStyle('width', width + '%');
30036         }, this);
30037         
30038     }
30039     
30040 });
30041 /*
30042  * - LGPL
30043  *
30044  * Nav Progress Item
30045  * 
30046  */
30047
30048 /**
30049  * @class Roo.bootstrap.NavProgressItem
30050  * @extends Roo.bootstrap.Component
30051  * Bootstrap NavProgressItem class
30052  * @cfg {String} rid the reference id
30053  * @cfg {Boolean} active (true|false) Is item active default false
30054  * @cfg {Boolean} disabled (true|false) Is item active default false
30055  * @cfg {String} html
30056  * @cfg {String} position (top|bottom) text position default bottom
30057  * @cfg {String} icon show icon instead of number
30058  * 
30059  * @constructor
30060  * Create a new NavProgressItem
30061  * @param {Object} config The config object
30062  */
30063 Roo.bootstrap.NavProgressItem = function(config){
30064     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30065     this.addEvents({
30066         // raw events
30067         /**
30068          * @event click
30069          * The raw click event for the entire grid.
30070          * @param {Roo.bootstrap.NavProgressItem} this
30071          * @param {Roo.EventObject} e
30072          */
30073         "click" : true
30074     });
30075    
30076 };
30077
30078 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30079     
30080     rid : '',
30081     active : false,
30082     disabled : false,
30083     html : '',
30084     position : 'bottom',
30085     icon : false,
30086     
30087     getAutoCreate : function()
30088     {
30089         var iconCls = 'roo-navigation-bar-item-icon';
30090         
30091         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30092         
30093         var cfg = {
30094             tag: 'li',
30095             cls: 'roo-navigation-bar-item',
30096             cn : [
30097                 {
30098                     tag : 'i',
30099                     cls : iconCls
30100                 }
30101             ]
30102         };
30103         
30104         if(this.active){
30105             cfg.cls += ' active';
30106         }
30107         if(this.disabled){
30108             cfg.cls += ' disabled';
30109         }
30110         
30111         return cfg;
30112     },
30113     
30114     disable : function()
30115     {
30116         this.setDisabled(true);
30117     },
30118     
30119     enable : function()
30120     {
30121         this.setDisabled(false);
30122     },
30123     
30124     initEvents: function() 
30125     {
30126         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30127         
30128         this.iconEl.on('click', this.onClick, this);
30129     },
30130     
30131     onClick : function(e)
30132     {
30133         e.preventDefault();
30134         
30135         if(this.disabled){
30136             return;
30137         }
30138         
30139         if(this.fireEvent('click', this, e) === false){
30140             return;
30141         };
30142         
30143         this.parent().setActiveItem(this);
30144     },
30145     
30146     isActive: function () 
30147     {
30148         return this.active;
30149     },
30150     
30151     setActive : function(state)
30152     {
30153         if(this.active == state){
30154             return;
30155         }
30156         
30157         this.active = state;
30158         
30159         if (state) {
30160             this.el.addClass('active');
30161             return;
30162         }
30163         
30164         this.el.removeClass('active');
30165         
30166         return;
30167     },
30168     
30169     setDisabled : function(state)
30170     {
30171         if(this.disabled == state){
30172             return;
30173         }
30174         
30175         this.disabled = state;
30176         
30177         if (state) {
30178             this.el.addClass('disabled');
30179             return;
30180         }
30181         
30182         this.el.removeClass('disabled');
30183     },
30184     
30185     tooltipEl : function()
30186     {
30187         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30188     }
30189 });
30190  
30191
30192  /*
30193  * - LGPL
30194  *
30195  * FieldLabel
30196  * 
30197  */
30198
30199 /**
30200  * @class Roo.bootstrap.FieldLabel
30201  * @extends Roo.bootstrap.Component
30202  * Bootstrap FieldLabel class
30203  * @cfg {String} html contents of the element
30204  * @cfg {String} tag tag of the element default label
30205  * @cfg {String} cls class of the element
30206  * @cfg {String} target label target 
30207  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30208  * @cfg {String} invalidClass default "text-warning"
30209  * @cfg {String} validClass default "text-success"
30210  * @cfg {String} iconTooltip default "This field is required"
30211  * @cfg {String} indicatorpos (left|right) default left
30212  * 
30213  * @constructor
30214  * Create a new FieldLabel
30215  * @param {Object} config The config object
30216  */
30217
30218 Roo.bootstrap.FieldLabel = function(config){
30219     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30220     
30221     this.addEvents({
30222             /**
30223              * @event invalid
30224              * Fires after the field has been marked as invalid.
30225              * @param {Roo.form.FieldLabel} this
30226              * @param {String} msg The validation message
30227              */
30228             invalid : true,
30229             /**
30230              * @event valid
30231              * Fires after the field has been validated with no errors.
30232              * @param {Roo.form.FieldLabel} this
30233              */
30234             valid : true
30235         });
30236 };
30237
30238 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30239     
30240     tag: 'label',
30241     cls: '',
30242     html: '',
30243     target: '',
30244     allowBlank : true,
30245     invalidClass : 'has-warning',
30246     validClass : 'has-success',
30247     iconTooltip : 'This field is required',
30248     indicatorpos : 'left',
30249     
30250     getAutoCreate : function(){
30251         
30252         var cls = "";
30253         if (!this.allowBlank) {
30254             cls  = "visible";
30255         }
30256         
30257         var cfg = {
30258             tag : this.tag,
30259             cls : 'roo-bootstrap-field-label ' + this.cls,
30260             for : this.target,
30261             cn : [
30262                 {
30263                     tag : 'i',
30264                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30265                     tooltip : this.iconTooltip
30266                 },
30267                 {
30268                     tag : 'span',
30269                     html : this.html
30270                 }
30271             ] 
30272         };
30273         
30274         if(this.indicatorpos == 'right'){
30275             var cfg = {
30276                 tag : this.tag,
30277                 cls : 'roo-bootstrap-field-label ' + this.cls,
30278                 for : this.target,
30279                 cn : [
30280                     {
30281                         tag : 'span',
30282                         html : this.html
30283                     },
30284                     {
30285                         tag : 'i',
30286                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30287                         tooltip : this.iconTooltip
30288                     }
30289                 ] 
30290             };
30291         }
30292         
30293         return cfg;
30294     },
30295     
30296     initEvents: function() 
30297     {
30298         Roo.bootstrap.Element.superclass.initEvents.call(this);
30299         
30300         this.indicator = this.indicatorEl();
30301         
30302         if(this.indicator){
30303             this.indicator.removeClass('visible');
30304             this.indicator.addClass('invisible');
30305         }
30306         
30307         Roo.bootstrap.FieldLabel.register(this);
30308     },
30309     
30310     indicatorEl : function()
30311     {
30312         var indicator = this.el.select('i.roo-required-indicator',true).first();
30313         
30314         if(!indicator){
30315             return false;
30316         }
30317         
30318         return indicator;
30319         
30320     },
30321     
30322     /**
30323      * Mark this field as valid
30324      */
30325     markValid : function()
30326     {
30327         if(this.indicator){
30328             this.indicator.removeClass('visible');
30329             this.indicator.addClass('invisible');
30330         }
30331         
30332         this.el.removeClass(this.invalidClass);
30333         
30334         this.el.addClass(this.validClass);
30335         
30336         this.fireEvent('valid', this);
30337     },
30338     
30339     /**
30340      * Mark this field as invalid
30341      * @param {String} msg The validation message
30342      */
30343     markInvalid : function(msg)
30344     {
30345         if(this.indicator){
30346             this.indicator.removeClass('invisible');
30347             this.indicator.addClass('visible');
30348         }
30349         
30350         this.el.removeClass(this.validClass);
30351         
30352         this.el.addClass(this.invalidClass);
30353         
30354         this.fireEvent('invalid', this, msg);
30355     }
30356     
30357    
30358 });
30359
30360 Roo.apply(Roo.bootstrap.FieldLabel, {
30361     
30362     groups: {},
30363     
30364      /**
30365     * register a FieldLabel Group
30366     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30367     */
30368     register : function(label)
30369     {
30370         if(this.groups.hasOwnProperty(label.target)){
30371             return;
30372         }
30373      
30374         this.groups[label.target] = label;
30375         
30376     },
30377     /**
30378     * fetch a FieldLabel Group based on the target
30379     * @param {string} target
30380     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30381     */
30382     get: function(target) {
30383         if (typeof(this.groups[target]) == 'undefined') {
30384             return false;
30385         }
30386         
30387         return this.groups[target] ;
30388     }
30389 });
30390
30391  
30392
30393  /*
30394  * - LGPL
30395  *
30396  * page DateSplitField.
30397  * 
30398  */
30399
30400
30401 /**
30402  * @class Roo.bootstrap.DateSplitField
30403  * @extends Roo.bootstrap.Component
30404  * Bootstrap DateSplitField class
30405  * @cfg {string} fieldLabel - the label associated
30406  * @cfg {Number} labelWidth set the width of label (0-12)
30407  * @cfg {String} labelAlign (top|left)
30408  * @cfg {Boolean} dayAllowBlank (true|false) default false
30409  * @cfg {Boolean} monthAllowBlank (true|false) default false
30410  * @cfg {Boolean} yearAllowBlank (true|false) default false
30411  * @cfg {string} dayPlaceholder 
30412  * @cfg {string} monthPlaceholder
30413  * @cfg {string} yearPlaceholder
30414  * @cfg {string} dayFormat default 'd'
30415  * @cfg {string} monthFormat default 'm'
30416  * @cfg {string} yearFormat default 'Y'
30417  * @cfg {Number} labellg set the width of label (1-12)
30418  * @cfg {Number} labelmd set the width of label (1-12)
30419  * @cfg {Number} labelsm set the width of label (1-12)
30420  * @cfg {Number} labelxs set the width of label (1-12)
30421
30422  *     
30423  * @constructor
30424  * Create a new DateSplitField
30425  * @param {Object} config The config object
30426  */
30427
30428 Roo.bootstrap.DateSplitField = function(config){
30429     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30430     
30431     this.addEvents({
30432         // raw events
30433          /**
30434          * @event years
30435          * getting the data of years
30436          * @param {Roo.bootstrap.DateSplitField} this
30437          * @param {Object} years
30438          */
30439         "years" : true,
30440         /**
30441          * @event days
30442          * getting the data of days
30443          * @param {Roo.bootstrap.DateSplitField} this
30444          * @param {Object} days
30445          */
30446         "days" : true,
30447         /**
30448          * @event invalid
30449          * Fires after the field has been marked as invalid.
30450          * @param {Roo.form.Field} this
30451          * @param {String} msg The validation message
30452          */
30453         invalid : true,
30454        /**
30455          * @event valid
30456          * Fires after the field has been validated with no errors.
30457          * @param {Roo.form.Field} this
30458          */
30459         valid : true
30460     });
30461 };
30462
30463 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30464     
30465     fieldLabel : '',
30466     labelAlign : 'top',
30467     labelWidth : 3,
30468     dayAllowBlank : false,
30469     monthAllowBlank : false,
30470     yearAllowBlank : false,
30471     dayPlaceholder : '',
30472     monthPlaceholder : '',
30473     yearPlaceholder : '',
30474     dayFormat : 'd',
30475     monthFormat : 'm',
30476     yearFormat : 'Y',
30477     isFormField : true,
30478     labellg : 0,
30479     labelmd : 0,
30480     labelsm : 0,
30481     labelxs : 0,
30482     
30483     getAutoCreate : function()
30484     {
30485         var cfg = {
30486             tag : 'div',
30487             cls : 'row roo-date-split-field-group',
30488             cn : [
30489                 {
30490                     tag : 'input',
30491                     type : 'hidden',
30492                     cls : 'form-hidden-field roo-date-split-field-group-value',
30493                     name : this.name
30494                 }
30495             ]
30496         };
30497         
30498         var labelCls = 'col-md-12';
30499         var contentCls = 'col-md-4';
30500         
30501         if(this.fieldLabel){
30502             
30503             var label = {
30504                 tag : 'div',
30505                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30506                 cn : [
30507                     {
30508                         tag : 'label',
30509                         html : this.fieldLabel
30510                     }
30511                 ]
30512             };
30513             
30514             if(this.labelAlign == 'left'){
30515             
30516                 if(this.labelWidth > 12){
30517                     label.style = "width: " + this.labelWidth + 'px';
30518                 }
30519
30520                 if(this.labelWidth < 13 && this.labelmd == 0){
30521                     this.labelmd = this.labelWidth;
30522                 }
30523
30524                 if(this.labellg > 0){
30525                     labelCls = ' col-lg-' + this.labellg;
30526                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30527                 }
30528
30529                 if(this.labelmd > 0){
30530                     labelCls = ' col-md-' + this.labelmd;
30531                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30532                 }
30533
30534                 if(this.labelsm > 0){
30535                     labelCls = ' col-sm-' + this.labelsm;
30536                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30537                 }
30538
30539                 if(this.labelxs > 0){
30540                     labelCls = ' col-xs-' + this.labelxs;
30541                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30542                 }
30543             }
30544             
30545             label.cls += ' ' + labelCls;
30546             
30547             cfg.cn.push(label);
30548         }
30549         
30550         Roo.each(['day', 'month', 'year'], function(t){
30551             cfg.cn.push({
30552                 tag : 'div',
30553                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30554             });
30555         }, this);
30556         
30557         return cfg;
30558     },
30559     
30560     inputEl: function ()
30561     {
30562         return this.el.select('.roo-date-split-field-group-value', true).first();
30563     },
30564     
30565     onRender : function(ct, position) 
30566     {
30567         var _this = this;
30568         
30569         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30570         
30571         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30572         
30573         this.dayField = new Roo.bootstrap.ComboBox({
30574             allowBlank : this.dayAllowBlank,
30575             alwaysQuery : true,
30576             displayField : 'value',
30577             editable : false,
30578             fieldLabel : '',
30579             forceSelection : true,
30580             mode : 'local',
30581             placeholder : this.dayPlaceholder,
30582             selectOnFocus : true,
30583             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30584             triggerAction : 'all',
30585             typeAhead : true,
30586             valueField : 'value',
30587             store : new Roo.data.SimpleStore({
30588                 data : (function() {    
30589                     var days = [];
30590                     _this.fireEvent('days', _this, days);
30591                     return days;
30592                 })(),
30593                 fields : [ 'value' ]
30594             }),
30595             listeners : {
30596                 select : function (_self, record, index)
30597                 {
30598                     _this.setValue(_this.getValue());
30599                 }
30600             }
30601         });
30602
30603         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30604         
30605         this.monthField = new Roo.bootstrap.MonthField({
30606             after : '<i class=\"fa fa-calendar\"></i>',
30607             allowBlank : this.monthAllowBlank,
30608             placeholder : this.monthPlaceholder,
30609             readOnly : true,
30610             listeners : {
30611                 render : function (_self)
30612                 {
30613                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30614                         e.preventDefault();
30615                         _self.focus();
30616                     });
30617                 },
30618                 select : function (_self, oldvalue, newvalue)
30619                 {
30620                     _this.setValue(_this.getValue());
30621                 }
30622             }
30623         });
30624         
30625         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30626         
30627         this.yearField = new Roo.bootstrap.ComboBox({
30628             allowBlank : this.yearAllowBlank,
30629             alwaysQuery : true,
30630             displayField : 'value',
30631             editable : false,
30632             fieldLabel : '',
30633             forceSelection : true,
30634             mode : 'local',
30635             placeholder : this.yearPlaceholder,
30636             selectOnFocus : true,
30637             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30638             triggerAction : 'all',
30639             typeAhead : true,
30640             valueField : 'value',
30641             store : new Roo.data.SimpleStore({
30642                 data : (function() {
30643                     var years = [];
30644                     _this.fireEvent('years', _this, years);
30645                     return years;
30646                 })(),
30647                 fields : [ 'value' ]
30648             }),
30649             listeners : {
30650                 select : function (_self, record, index)
30651                 {
30652                     _this.setValue(_this.getValue());
30653                 }
30654             }
30655         });
30656
30657         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30658     },
30659     
30660     setValue : function(v, format)
30661     {
30662         this.inputEl.dom.value = v;
30663         
30664         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30665         
30666         var d = Date.parseDate(v, f);
30667         
30668         if(!d){
30669             this.validate();
30670             return;
30671         }
30672         
30673         this.setDay(d.format(this.dayFormat));
30674         this.setMonth(d.format(this.monthFormat));
30675         this.setYear(d.format(this.yearFormat));
30676         
30677         this.validate();
30678         
30679         return;
30680     },
30681     
30682     setDay : function(v)
30683     {
30684         this.dayField.setValue(v);
30685         this.inputEl.dom.value = this.getValue();
30686         this.validate();
30687         return;
30688     },
30689     
30690     setMonth : function(v)
30691     {
30692         this.monthField.setValue(v, true);
30693         this.inputEl.dom.value = this.getValue();
30694         this.validate();
30695         return;
30696     },
30697     
30698     setYear : function(v)
30699     {
30700         this.yearField.setValue(v);
30701         this.inputEl.dom.value = this.getValue();
30702         this.validate();
30703         return;
30704     },
30705     
30706     getDay : function()
30707     {
30708         return this.dayField.getValue();
30709     },
30710     
30711     getMonth : function()
30712     {
30713         return this.monthField.getValue();
30714     },
30715     
30716     getYear : function()
30717     {
30718         return this.yearField.getValue();
30719     },
30720     
30721     getValue : function()
30722     {
30723         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30724         
30725         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30726         
30727         return date;
30728     },
30729     
30730     reset : function()
30731     {
30732         this.setDay('');
30733         this.setMonth('');
30734         this.setYear('');
30735         this.inputEl.dom.value = '';
30736         this.validate();
30737         return;
30738     },
30739     
30740     validate : function()
30741     {
30742         var d = this.dayField.validate();
30743         var m = this.monthField.validate();
30744         var y = this.yearField.validate();
30745         
30746         var valid = true;
30747         
30748         if(
30749                 (!this.dayAllowBlank && !d) ||
30750                 (!this.monthAllowBlank && !m) ||
30751                 (!this.yearAllowBlank && !y)
30752         ){
30753             valid = false;
30754         }
30755         
30756         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30757             return valid;
30758         }
30759         
30760         if(valid){
30761             this.markValid();
30762             return valid;
30763         }
30764         
30765         this.markInvalid();
30766         
30767         return valid;
30768     },
30769     
30770     markValid : function()
30771     {
30772         
30773         var label = this.el.select('label', true).first();
30774         var icon = this.el.select('i.fa-star', true).first();
30775
30776         if(label && icon){
30777             icon.remove();
30778         }
30779         
30780         this.fireEvent('valid', this);
30781     },
30782     
30783      /**
30784      * Mark this field as invalid
30785      * @param {String} msg The validation message
30786      */
30787     markInvalid : function(msg)
30788     {
30789         
30790         var label = this.el.select('label', true).first();
30791         var icon = this.el.select('i.fa-star', true).first();
30792
30793         if(label && !icon){
30794             this.el.select('.roo-date-split-field-label', true).createChild({
30795                 tag : 'i',
30796                 cls : 'text-danger fa fa-lg fa-star',
30797                 tooltip : 'This field is required',
30798                 style : 'margin-right:5px;'
30799             }, label, true);
30800         }
30801         
30802         this.fireEvent('invalid', this, msg);
30803     },
30804     
30805     clearInvalid : function()
30806     {
30807         var label = this.el.select('label', true).first();
30808         var icon = this.el.select('i.fa-star', true).first();
30809
30810         if(label && icon){
30811             icon.remove();
30812         }
30813         
30814         this.fireEvent('valid', this);
30815     },
30816     
30817     getName: function()
30818     {
30819         return this.name;
30820     }
30821     
30822 });
30823
30824  /**
30825  *
30826  * This is based on 
30827  * http://masonry.desandro.com
30828  *
30829  * The idea is to render all the bricks based on vertical width...
30830  *
30831  * The original code extends 'outlayer' - we might need to use that....
30832  * 
30833  */
30834
30835
30836 /**
30837  * @class Roo.bootstrap.LayoutMasonry
30838  * @extends Roo.bootstrap.Component
30839  * Bootstrap Layout Masonry class
30840  * 
30841  * @constructor
30842  * Create a new Element
30843  * @param {Object} config The config object
30844  */
30845
30846 Roo.bootstrap.LayoutMasonry = function(config){
30847     
30848     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30849     
30850     this.bricks = [];
30851     
30852     Roo.bootstrap.LayoutMasonry.register(this);
30853     
30854     this.addEvents({
30855         // raw events
30856         /**
30857          * @event layout
30858          * Fire after layout the items
30859          * @param {Roo.bootstrap.LayoutMasonry} this
30860          * @param {Roo.EventObject} e
30861          */
30862         "layout" : true
30863     });
30864     
30865 };
30866
30867 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30868     
30869     /**
30870      * @cfg {Boolean} isLayoutInstant = no animation?
30871      */   
30872     isLayoutInstant : false, // needed?
30873    
30874     /**
30875      * @cfg {Number} boxWidth  width of the columns
30876      */   
30877     boxWidth : 450,
30878     
30879       /**
30880      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30881      */   
30882     boxHeight : 0,
30883     
30884     /**
30885      * @cfg {Number} padWidth padding below box..
30886      */   
30887     padWidth : 10, 
30888     
30889     /**
30890      * @cfg {Number} gutter gutter width..
30891      */   
30892     gutter : 10,
30893     
30894      /**
30895      * @cfg {Number} maxCols maximum number of columns
30896      */   
30897     
30898     maxCols: 0,
30899     
30900     /**
30901      * @cfg {Boolean} isAutoInitial defalut true
30902      */   
30903     isAutoInitial : true, 
30904     
30905     containerWidth: 0,
30906     
30907     /**
30908      * @cfg {Boolean} isHorizontal defalut false
30909      */   
30910     isHorizontal : false, 
30911
30912     currentSize : null,
30913     
30914     tag: 'div',
30915     
30916     cls: '',
30917     
30918     bricks: null, //CompositeElement
30919     
30920     cols : 1,
30921     
30922     _isLayoutInited : false,
30923     
30924 //    isAlternative : false, // only use for vertical layout...
30925     
30926     /**
30927      * @cfg {Number} alternativePadWidth padding below box..
30928      */   
30929     alternativePadWidth : 50,
30930     
30931     selectedBrick : [],
30932     
30933     getAutoCreate : function(){
30934         
30935         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30936         
30937         var cfg = {
30938             tag: this.tag,
30939             cls: 'blog-masonary-wrapper ' + this.cls,
30940             cn : {
30941                 cls : 'mas-boxes masonary'
30942             }
30943         };
30944         
30945         return cfg;
30946     },
30947     
30948     getChildContainer: function( )
30949     {
30950         if (this.boxesEl) {
30951             return this.boxesEl;
30952         }
30953         
30954         this.boxesEl = this.el.select('.mas-boxes').first();
30955         
30956         return this.boxesEl;
30957     },
30958     
30959     
30960     initEvents : function()
30961     {
30962         var _this = this;
30963         
30964         if(this.isAutoInitial){
30965             Roo.log('hook children rendered');
30966             this.on('childrenrendered', function() {
30967                 Roo.log('children rendered');
30968                 _this.initial();
30969             } ,this);
30970         }
30971     },
30972     
30973     initial : function()
30974     {
30975         this.selectedBrick = [];
30976         
30977         this.currentSize = this.el.getBox(true);
30978         
30979         Roo.EventManager.onWindowResize(this.resize, this); 
30980
30981         if(!this.isAutoInitial){
30982             this.layout();
30983             return;
30984         }
30985         
30986         this.layout();
30987         
30988         return;
30989         //this.layout.defer(500,this);
30990         
30991     },
30992     
30993     resize : function()
30994     {
30995         var cs = this.el.getBox(true);
30996         
30997         if (
30998                 this.currentSize.width == cs.width && 
30999                 this.currentSize.x == cs.x && 
31000                 this.currentSize.height == cs.height && 
31001                 this.currentSize.y == cs.y 
31002         ) {
31003             Roo.log("no change in with or X or Y");
31004             return;
31005         }
31006         
31007         this.currentSize = cs;
31008         
31009         this.layout();
31010         
31011     },
31012     
31013     layout : function()
31014     {   
31015         this._resetLayout();
31016         
31017         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31018         
31019         this.layoutItems( isInstant );
31020       
31021         this._isLayoutInited = true;
31022         
31023         this.fireEvent('layout', this);
31024         
31025     },
31026     
31027     _resetLayout : function()
31028     {
31029         if(this.isHorizontal){
31030             this.horizontalMeasureColumns();
31031             return;
31032         }
31033         
31034         this.verticalMeasureColumns();
31035         
31036     },
31037     
31038     verticalMeasureColumns : function()
31039     {
31040         this.getContainerWidth();
31041         
31042 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31043 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31044 //            return;
31045 //        }
31046         
31047         var boxWidth = this.boxWidth + this.padWidth;
31048         
31049         if(this.containerWidth < this.boxWidth){
31050             boxWidth = this.containerWidth
31051         }
31052         
31053         var containerWidth = this.containerWidth;
31054         
31055         var cols = Math.floor(containerWidth / boxWidth);
31056         
31057         this.cols = Math.max( cols, 1 );
31058         
31059         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31060         
31061         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31062         
31063         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31064         
31065         this.colWidth = boxWidth + avail - this.padWidth;
31066         
31067         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31068         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31069     },
31070     
31071     horizontalMeasureColumns : function()
31072     {
31073         this.getContainerWidth();
31074         
31075         var boxWidth = this.boxWidth;
31076         
31077         if(this.containerWidth < boxWidth){
31078             boxWidth = this.containerWidth;
31079         }
31080         
31081         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31082         
31083         this.el.setHeight(boxWidth);
31084         
31085     },
31086     
31087     getContainerWidth : function()
31088     {
31089         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31090     },
31091     
31092     layoutItems : function( isInstant )
31093     {
31094         Roo.log(this.bricks);
31095         
31096         var items = Roo.apply([], this.bricks);
31097         
31098         if(this.isHorizontal){
31099             this._horizontalLayoutItems( items , isInstant );
31100             return;
31101         }
31102         
31103 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31104 //            this._verticalAlternativeLayoutItems( items , isInstant );
31105 //            return;
31106 //        }
31107         
31108         this._verticalLayoutItems( items , isInstant );
31109         
31110     },
31111     
31112     _verticalLayoutItems : function ( items , isInstant)
31113     {
31114         if ( !items || !items.length ) {
31115             return;
31116         }
31117         
31118         var standard = [
31119             ['xs', 'xs', 'xs', 'tall'],
31120             ['xs', 'xs', 'tall'],
31121             ['xs', 'xs', 'sm'],
31122             ['xs', 'xs', 'xs'],
31123             ['xs', 'tall'],
31124             ['xs', 'sm'],
31125             ['xs', 'xs'],
31126             ['xs'],
31127             
31128             ['sm', 'xs', 'xs'],
31129             ['sm', 'xs'],
31130             ['sm'],
31131             
31132             ['tall', 'xs', 'xs', 'xs'],
31133             ['tall', 'xs', 'xs'],
31134             ['tall', 'xs'],
31135             ['tall']
31136             
31137         ];
31138         
31139         var queue = [];
31140         
31141         var boxes = [];
31142         
31143         var box = [];
31144         
31145         Roo.each(items, function(item, k){
31146             
31147             switch (item.size) {
31148                 // these layouts take up a full box,
31149                 case 'md' :
31150                 case 'md-left' :
31151                 case 'md-right' :
31152                 case 'wide' :
31153                     
31154                     if(box.length){
31155                         boxes.push(box);
31156                         box = [];
31157                     }
31158                     
31159                     boxes.push([item]);
31160                     
31161                     break;
31162                     
31163                 case 'xs' :
31164                 case 'sm' :
31165                 case 'tall' :
31166                     
31167                     box.push(item);
31168                     
31169                     break;
31170                 default :
31171                     break;
31172                     
31173             }
31174             
31175         }, this);
31176         
31177         if(box.length){
31178             boxes.push(box);
31179             box = [];
31180         }
31181         
31182         var filterPattern = function(box, length)
31183         {
31184             if(!box.length){
31185                 return;
31186             }
31187             
31188             var match = false;
31189             
31190             var pattern = box.slice(0, length);
31191             
31192             var format = [];
31193             
31194             Roo.each(pattern, function(i){
31195                 format.push(i.size);
31196             }, this);
31197             
31198             Roo.each(standard, function(s){
31199                 
31200                 if(String(s) != String(format)){
31201                     return;
31202                 }
31203                 
31204                 match = true;
31205                 return false;
31206                 
31207             }, this);
31208             
31209             if(!match && length == 1){
31210                 return;
31211             }
31212             
31213             if(!match){
31214                 filterPattern(box, length - 1);
31215                 return;
31216             }
31217                 
31218             queue.push(pattern);
31219
31220             box = box.slice(length, box.length);
31221
31222             filterPattern(box, 4);
31223
31224             return;
31225             
31226         }
31227         
31228         Roo.each(boxes, function(box, k){
31229             
31230             if(!box.length){
31231                 return;
31232             }
31233             
31234             if(box.length == 1){
31235                 queue.push(box);
31236                 return;
31237             }
31238             
31239             filterPattern(box, 4);
31240             
31241         }, this);
31242         
31243         this._processVerticalLayoutQueue( queue, isInstant );
31244         
31245     },
31246     
31247 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31248 //    {
31249 //        if ( !items || !items.length ) {
31250 //            return;
31251 //        }
31252 //
31253 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31254 //        
31255 //    },
31256     
31257     _horizontalLayoutItems : function ( items , isInstant)
31258     {
31259         if ( !items || !items.length || items.length < 3) {
31260             return;
31261         }
31262         
31263         items.reverse();
31264         
31265         var eItems = items.slice(0, 3);
31266         
31267         items = items.slice(3, items.length);
31268         
31269         var standard = [
31270             ['xs', 'xs', 'xs', 'wide'],
31271             ['xs', 'xs', 'wide'],
31272             ['xs', 'xs', 'sm'],
31273             ['xs', 'xs', 'xs'],
31274             ['xs', 'wide'],
31275             ['xs', 'sm'],
31276             ['xs', 'xs'],
31277             ['xs'],
31278             
31279             ['sm', 'xs', 'xs'],
31280             ['sm', 'xs'],
31281             ['sm'],
31282             
31283             ['wide', 'xs', 'xs', 'xs'],
31284             ['wide', 'xs', 'xs'],
31285             ['wide', 'xs'],
31286             ['wide'],
31287             
31288             ['wide-thin']
31289         ];
31290         
31291         var queue = [];
31292         
31293         var boxes = [];
31294         
31295         var box = [];
31296         
31297         Roo.each(items, function(item, k){
31298             
31299             switch (item.size) {
31300                 case 'md' :
31301                 case 'md-left' :
31302                 case 'md-right' :
31303                 case 'tall' :
31304                     
31305                     if(box.length){
31306                         boxes.push(box);
31307                         box = [];
31308                     }
31309                     
31310                     boxes.push([item]);
31311                     
31312                     break;
31313                     
31314                 case 'xs' :
31315                 case 'sm' :
31316                 case 'wide' :
31317                 case 'wide-thin' :
31318                     
31319                     box.push(item);
31320                     
31321                     break;
31322                 default :
31323                     break;
31324                     
31325             }
31326             
31327         }, this);
31328         
31329         if(box.length){
31330             boxes.push(box);
31331             box = [];
31332         }
31333         
31334         var filterPattern = function(box, length)
31335         {
31336             if(!box.length){
31337                 return;
31338             }
31339             
31340             var match = false;
31341             
31342             var pattern = box.slice(0, length);
31343             
31344             var format = [];
31345             
31346             Roo.each(pattern, function(i){
31347                 format.push(i.size);
31348             }, this);
31349             
31350             Roo.each(standard, function(s){
31351                 
31352                 if(String(s) != String(format)){
31353                     return;
31354                 }
31355                 
31356                 match = true;
31357                 return false;
31358                 
31359             }, this);
31360             
31361             if(!match && length == 1){
31362                 return;
31363             }
31364             
31365             if(!match){
31366                 filterPattern(box, length - 1);
31367                 return;
31368             }
31369                 
31370             queue.push(pattern);
31371
31372             box = box.slice(length, box.length);
31373
31374             filterPattern(box, 4);
31375
31376             return;
31377             
31378         }
31379         
31380         Roo.each(boxes, function(box, k){
31381             
31382             if(!box.length){
31383                 return;
31384             }
31385             
31386             if(box.length == 1){
31387                 queue.push(box);
31388                 return;
31389             }
31390             
31391             filterPattern(box, 4);
31392             
31393         }, this);
31394         
31395         
31396         var prune = [];
31397         
31398         var pos = this.el.getBox(true);
31399         
31400         var minX = pos.x;
31401         
31402         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31403         
31404         var hit_end = false;
31405         
31406         Roo.each(queue, function(box){
31407             
31408             if(hit_end){
31409                 
31410                 Roo.each(box, function(b){
31411                 
31412                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31413                     b.el.hide();
31414
31415                 }, this);
31416
31417                 return;
31418             }
31419             
31420             var mx = 0;
31421             
31422             Roo.each(box, function(b){
31423                 
31424                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31425                 b.el.show();
31426
31427                 mx = Math.max(mx, b.x);
31428                 
31429             }, this);
31430             
31431             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31432             
31433             if(maxX < minX){
31434                 
31435                 Roo.each(box, function(b){
31436                 
31437                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31438                     b.el.hide();
31439                     
31440                 }, this);
31441                 
31442                 hit_end = true;
31443                 
31444                 return;
31445             }
31446             
31447             prune.push(box);
31448             
31449         }, this);
31450         
31451         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31452     },
31453     
31454     /** Sets position of item in DOM
31455     * @param {Element} item
31456     * @param {Number} x - horizontal position
31457     * @param {Number} y - vertical position
31458     * @param {Boolean} isInstant - disables transitions
31459     */
31460     _processVerticalLayoutQueue : function( queue, isInstant )
31461     {
31462         var pos = this.el.getBox(true);
31463         var x = pos.x;
31464         var y = pos.y;
31465         var maxY = [];
31466         
31467         for (var i = 0; i < this.cols; i++){
31468             maxY[i] = pos.y;
31469         }
31470         
31471         Roo.each(queue, function(box, k){
31472             
31473             var col = k % this.cols;
31474             
31475             Roo.each(box, function(b,kk){
31476                 
31477                 b.el.position('absolute');
31478                 
31479                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31480                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31481                 
31482                 if(b.size == 'md-left' || b.size == 'md-right'){
31483                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31484                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31485                 }
31486                 
31487                 b.el.setWidth(width);
31488                 b.el.setHeight(height);
31489                 // iframe?
31490                 b.el.select('iframe',true).setSize(width,height);
31491                 
31492             }, this);
31493             
31494             for (var i = 0; i < this.cols; i++){
31495                 
31496                 if(maxY[i] < maxY[col]){
31497                     col = i;
31498                     continue;
31499                 }
31500                 
31501                 col = Math.min(col, i);
31502                 
31503             }
31504             
31505             x = pos.x + col * (this.colWidth + this.padWidth);
31506             
31507             y = maxY[col];
31508             
31509             var positions = [];
31510             
31511             switch (box.length){
31512                 case 1 :
31513                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31514                     break;
31515                 case 2 :
31516                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31517                     break;
31518                 case 3 :
31519                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31520                     break;
31521                 case 4 :
31522                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31523                     break;
31524                 default :
31525                     break;
31526             }
31527             
31528             Roo.each(box, function(b,kk){
31529                 
31530                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31531                 
31532                 var sz = b.el.getSize();
31533                 
31534                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31535                 
31536             }, this);
31537             
31538         }, this);
31539         
31540         var mY = 0;
31541         
31542         for (var i = 0; i < this.cols; i++){
31543             mY = Math.max(mY, maxY[i]);
31544         }
31545         
31546         this.el.setHeight(mY - pos.y);
31547         
31548     },
31549     
31550 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31551 //    {
31552 //        var pos = this.el.getBox(true);
31553 //        var x = pos.x;
31554 //        var y = pos.y;
31555 //        var maxX = pos.right;
31556 //        
31557 //        var maxHeight = 0;
31558 //        
31559 //        Roo.each(items, function(item, k){
31560 //            
31561 //            var c = k % 2;
31562 //            
31563 //            item.el.position('absolute');
31564 //                
31565 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31566 //
31567 //            item.el.setWidth(width);
31568 //
31569 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31570 //
31571 //            item.el.setHeight(height);
31572 //            
31573 //            if(c == 0){
31574 //                item.el.setXY([x, y], isInstant ? false : true);
31575 //            } else {
31576 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31577 //            }
31578 //            
31579 //            y = y + height + this.alternativePadWidth;
31580 //            
31581 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31582 //            
31583 //        }, this);
31584 //        
31585 //        this.el.setHeight(maxHeight);
31586 //        
31587 //    },
31588     
31589     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31590     {
31591         var pos = this.el.getBox(true);
31592         
31593         var minX = pos.x;
31594         var minY = pos.y;
31595         
31596         var maxX = pos.right;
31597         
31598         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31599         
31600         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31601         
31602         Roo.each(queue, function(box, k){
31603             
31604             Roo.each(box, function(b, kk){
31605                 
31606                 b.el.position('absolute');
31607                 
31608                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31609                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31610                 
31611                 if(b.size == 'md-left' || b.size == 'md-right'){
31612                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31613                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31614                 }
31615                 
31616                 b.el.setWidth(width);
31617                 b.el.setHeight(height);
31618                 
31619             }, this);
31620             
31621             if(!box.length){
31622                 return;
31623             }
31624             
31625             var positions = [];
31626             
31627             switch (box.length){
31628                 case 1 :
31629                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31630                     break;
31631                 case 2 :
31632                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31633                     break;
31634                 case 3 :
31635                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31636                     break;
31637                 case 4 :
31638                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31639                     break;
31640                 default :
31641                     break;
31642             }
31643             
31644             Roo.each(box, function(b,kk){
31645                 
31646                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31647                 
31648                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31649                 
31650             }, this);
31651             
31652         }, this);
31653         
31654     },
31655     
31656     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31657     {
31658         Roo.each(eItems, function(b,k){
31659             
31660             b.size = (k == 0) ? 'sm' : 'xs';
31661             b.x = (k == 0) ? 2 : 1;
31662             b.y = (k == 0) ? 2 : 1;
31663             
31664             b.el.position('absolute');
31665             
31666             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31667                 
31668             b.el.setWidth(width);
31669             
31670             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31671             
31672             b.el.setHeight(height);
31673             
31674         }, this);
31675
31676         var positions = [];
31677         
31678         positions.push({
31679             x : maxX - this.unitWidth * 2 - this.gutter,
31680             y : minY
31681         });
31682         
31683         positions.push({
31684             x : maxX - this.unitWidth,
31685             y : minY + (this.unitWidth + this.gutter) * 2
31686         });
31687         
31688         positions.push({
31689             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31690             y : minY
31691         });
31692         
31693         Roo.each(eItems, function(b,k){
31694             
31695             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31696
31697         }, this);
31698         
31699     },
31700     
31701     getVerticalOneBoxColPositions : function(x, y, box)
31702     {
31703         var pos = [];
31704         
31705         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31706         
31707         if(box[0].size == 'md-left'){
31708             rand = 0;
31709         }
31710         
31711         if(box[0].size == 'md-right'){
31712             rand = 1;
31713         }
31714         
31715         pos.push({
31716             x : x + (this.unitWidth + this.gutter) * rand,
31717             y : y
31718         });
31719         
31720         return pos;
31721     },
31722     
31723     getVerticalTwoBoxColPositions : function(x, y, box)
31724     {
31725         var pos = [];
31726         
31727         if(box[0].size == 'xs'){
31728             
31729             pos.push({
31730                 x : x,
31731                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31732             });
31733
31734             pos.push({
31735                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31736                 y : y
31737             });
31738             
31739             return pos;
31740             
31741         }
31742         
31743         pos.push({
31744             x : x,
31745             y : y
31746         });
31747
31748         pos.push({
31749             x : x + (this.unitWidth + this.gutter) * 2,
31750             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31751         });
31752         
31753         return pos;
31754         
31755     },
31756     
31757     getVerticalThreeBoxColPositions : function(x, y, box)
31758     {
31759         var pos = [];
31760         
31761         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31762             
31763             pos.push({
31764                 x : x,
31765                 y : y
31766             });
31767
31768             pos.push({
31769                 x : x + (this.unitWidth + this.gutter) * 1,
31770                 y : y
31771             });
31772             
31773             pos.push({
31774                 x : x + (this.unitWidth + this.gutter) * 2,
31775                 y : y
31776             });
31777             
31778             return pos;
31779             
31780         }
31781         
31782         if(box[0].size == 'xs' && box[1].size == 'xs'){
31783             
31784             pos.push({
31785                 x : x,
31786                 y : y
31787             });
31788
31789             pos.push({
31790                 x : x,
31791                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31792             });
31793             
31794             pos.push({
31795                 x : x + (this.unitWidth + this.gutter) * 1,
31796                 y : y
31797             });
31798             
31799             return pos;
31800             
31801         }
31802         
31803         pos.push({
31804             x : x,
31805             y : y
31806         });
31807
31808         pos.push({
31809             x : x + (this.unitWidth + this.gutter) * 2,
31810             y : y
31811         });
31812
31813         pos.push({
31814             x : x + (this.unitWidth + this.gutter) * 2,
31815             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31816         });
31817             
31818         return pos;
31819         
31820     },
31821     
31822     getVerticalFourBoxColPositions : function(x, y, box)
31823     {
31824         var pos = [];
31825         
31826         if(box[0].size == 'xs'){
31827             
31828             pos.push({
31829                 x : x,
31830                 y : y
31831             });
31832
31833             pos.push({
31834                 x : x,
31835                 y : y + (this.unitHeight + this.gutter) * 1
31836             });
31837             
31838             pos.push({
31839                 x : x,
31840                 y : y + (this.unitHeight + this.gutter) * 2
31841             });
31842             
31843             pos.push({
31844                 x : x + (this.unitWidth + this.gutter) * 1,
31845                 y : y
31846             });
31847             
31848             return pos;
31849             
31850         }
31851         
31852         pos.push({
31853             x : x,
31854             y : y
31855         });
31856
31857         pos.push({
31858             x : x + (this.unitWidth + this.gutter) * 2,
31859             y : y
31860         });
31861
31862         pos.push({
31863             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31864             y : y + (this.unitHeight + this.gutter) * 1
31865         });
31866
31867         pos.push({
31868             x : x + (this.unitWidth + this.gutter) * 2,
31869             y : y + (this.unitWidth + this.gutter) * 2
31870         });
31871
31872         return pos;
31873         
31874     },
31875     
31876     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31877     {
31878         var pos = [];
31879         
31880         if(box[0].size == 'md-left'){
31881             pos.push({
31882                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31883                 y : minY
31884             });
31885             
31886             return pos;
31887         }
31888         
31889         if(box[0].size == 'md-right'){
31890             pos.push({
31891                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31892                 y : minY + (this.unitWidth + this.gutter) * 1
31893             });
31894             
31895             return pos;
31896         }
31897         
31898         var rand = Math.floor(Math.random() * (4 - box[0].y));
31899         
31900         pos.push({
31901             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31902             y : minY + (this.unitWidth + this.gutter) * rand
31903         });
31904         
31905         return pos;
31906         
31907     },
31908     
31909     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31910     {
31911         var pos = [];
31912         
31913         if(box[0].size == 'xs'){
31914             
31915             pos.push({
31916                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31917                 y : minY
31918             });
31919
31920             pos.push({
31921                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31922                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31923             });
31924             
31925             return pos;
31926             
31927         }
31928         
31929         pos.push({
31930             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31931             y : minY
31932         });
31933
31934         pos.push({
31935             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31936             y : minY + (this.unitWidth + this.gutter) * 2
31937         });
31938         
31939         return pos;
31940         
31941     },
31942     
31943     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31944     {
31945         var pos = [];
31946         
31947         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31948             
31949             pos.push({
31950                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31951                 y : minY
31952             });
31953
31954             pos.push({
31955                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31956                 y : minY + (this.unitWidth + this.gutter) * 1
31957             });
31958             
31959             pos.push({
31960                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31961                 y : minY + (this.unitWidth + this.gutter) * 2
31962             });
31963             
31964             return pos;
31965             
31966         }
31967         
31968         if(box[0].size == 'xs' && box[1].size == 'xs'){
31969             
31970             pos.push({
31971                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31972                 y : minY
31973             });
31974
31975             pos.push({
31976                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31977                 y : minY
31978             });
31979             
31980             pos.push({
31981                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31982                 y : minY + (this.unitWidth + this.gutter) * 1
31983             });
31984             
31985             return pos;
31986             
31987         }
31988         
31989         pos.push({
31990             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31991             y : minY
31992         });
31993
31994         pos.push({
31995             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31996             y : minY + (this.unitWidth + this.gutter) * 2
31997         });
31998
31999         pos.push({
32000             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32001             y : minY + (this.unitWidth + this.gutter) * 2
32002         });
32003             
32004         return pos;
32005         
32006     },
32007     
32008     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32009     {
32010         var pos = [];
32011         
32012         if(box[0].size == 'xs'){
32013             
32014             pos.push({
32015                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32016                 y : minY
32017             });
32018
32019             pos.push({
32020                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32021                 y : minY
32022             });
32023             
32024             pos.push({
32025                 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),
32026                 y : minY
32027             });
32028             
32029             pos.push({
32030                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32031                 y : minY + (this.unitWidth + this.gutter) * 1
32032             });
32033             
32034             return pos;
32035             
32036         }
32037         
32038         pos.push({
32039             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32040             y : minY
32041         });
32042         
32043         pos.push({
32044             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32045             y : minY + (this.unitWidth + this.gutter) * 2
32046         });
32047         
32048         pos.push({
32049             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32050             y : minY + (this.unitWidth + this.gutter) * 2
32051         });
32052         
32053         pos.push({
32054             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),
32055             y : minY + (this.unitWidth + this.gutter) * 2
32056         });
32057
32058         return pos;
32059         
32060     },
32061     
32062     /**
32063     * remove a Masonry Brick
32064     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32065     */
32066     removeBrick : function(brick_id)
32067     {
32068         if (!brick_id) {
32069             return;
32070         }
32071         
32072         for (var i = 0; i<this.bricks.length; i++) {
32073             if (this.bricks[i].id == brick_id) {
32074                 this.bricks.splice(i,1);
32075                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32076                 this.initial();
32077             }
32078         }
32079     },
32080     
32081     /**
32082     * adds a Masonry Brick
32083     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32084     */
32085     addBrick : function(cfg)
32086     {
32087         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32088         //this.register(cn);
32089         cn.parentId = this.id;
32090         cn.render(this.el);
32091         return cn;
32092     },
32093     
32094     /**
32095     * register a Masonry Brick
32096     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32097     */
32098     
32099     register : function(brick)
32100     {
32101         this.bricks.push(brick);
32102         brick.masonryId = this.id;
32103     },
32104     
32105     /**
32106     * clear all the Masonry Brick
32107     */
32108     clearAll : function()
32109     {
32110         this.bricks = [];
32111         //this.getChildContainer().dom.innerHTML = "";
32112         this.el.dom.innerHTML = '';
32113     },
32114     
32115     getSelected : function()
32116     {
32117         if (!this.selectedBrick) {
32118             return false;
32119         }
32120         
32121         return this.selectedBrick;
32122     }
32123 });
32124
32125 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32126     
32127     groups: {},
32128      /**
32129     * register a Masonry Layout
32130     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32131     */
32132     
32133     register : function(layout)
32134     {
32135         this.groups[layout.id] = layout;
32136     },
32137     /**
32138     * fetch a  Masonry Layout based on the masonry layout ID
32139     * @param {string} the masonry layout to add
32140     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32141     */
32142     
32143     get: function(layout_id) {
32144         if (typeof(this.groups[layout_id]) == 'undefined') {
32145             return false;
32146         }
32147         return this.groups[layout_id] ;
32148     }
32149     
32150     
32151     
32152 });
32153
32154  
32155
32156  /**
32157  *
32158  * This is based on 
32159  * http://masonry.desandro.com
32160  *
32161  * The idea is to render all the bricks based on vertical width...
32162  *
32163  * The original code extends 'outlayer' - we might need to use that....
32164  * 
32165  */
32166
32167
32168 /**
32169  * @class Roo.bootstrap.LayoutMasonryAuto
32170  * @extends Roo.bootstrap.Component
32171  * Bootstrap Layout Masonry class
32172  * 
32173  * @constructor
32174  * Create a new Element
32175  * @param {Object} config The config object
32176  */
32177
32178 Roo.bootstrap.LayoutMasonryAuto = function(config){
32179     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32180 };
32181
32182 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32183     
32184       /**
32185      * @cfg {Boolean} isFitWidth  - resize the width..
32186      */   
32187     isFitWidth : false,  // options..
32188     /**
32189      * @cfg {Boolean} isOriginLeft = left align?
32190      */   
32191     isOriginLeft : true,
32192     /**
32193      * @cfg {Boolean} isOriginTop = top align?
32194      */   
32195     isOriginTop : false,
32196     /**
32197      * @cfg {Boolean} isLayoutInstant = no animation?
32198      */   
32199     isLayoutInstant : false, // needed?
32200     /**
32201      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32202      */   
32203     isResizingContainer : true,
32204     /**
32205      * @cfg {Number} columnWidth  width of the columns 
32206      */   
32207     
32208     columnWidth : 0,
32209     
32210     /**
32211      * @cfg {Number} maxCols maximum number of columns
32212      */   
32213     
32214     maxCols: 0,
32215     /**
32216      * @cfg {Number} padHeight padding below box..
32217      */   
32218     
32219     padHeight : 10, 
32220     
32221     /**
32222      * @cfg {Boolean} isAutoInitial defalut true
32223      */   
32224     
32225     isAutoInitial : true, 
32226     
32227     // private?
32228     gutter : 0,
32229     
32230     containerWidth: 0,
32231     initialColumnWidth : 0,
32232     currentSize : null,
32233     
32234     colYs : null, // array.
32235     maxY : 0,
32236     padWidth: 10,
32237     
32238     
32239     tag: 'div',
32240     cls: '',
32241     bricks: null, //CompositeElement
32242     cols : 0, // array?
32243     // element : null, // wrapped now this.el
32244     _isLayoutInited : null, 
32245     
32246     
32247     getAutoCreate : function(){
32248         
32249         var cfg = {
32250             tag: this.tag,
32251             cls: 'blog-masonary-wrapper ' + this.cls,
32252             cn : {
32253                 cls : 'mas-boxes masonary'
32254             }
32255         };
32256         
32257         return cfg;
32258     },
32259     
32260     getChildContainer: function( )
32261     {
32262         if (this.boxesEl) {
32263             return this.boxesEl;
32264         }
32265         
32266         this.boxesEl = this.el.select('.mas-boxes').first();
32267         
32268         return this.boxesEl;
32269     },
32270     
32271     
32272     initEvents : function()
32273     {
32274         var _this = this;
32275         
32276         if(this.isAutoInitial){
32277             Roo.log('hook children rendered');
32278             this.on('childrenrendered', function() {
32279                 Roo.log('children rendered');
32280                 _this.initial();
32281             } ,this);
32282         }
32283         
32284     },
32285     
32286     initial : function()
32287     {
32288         this.reloadItems();
32289
32290         this.currentSize = this.el.getBox(true);
32291
32292         /// was window resize... - let's see if this works..
32293         Roo.EventManager.onWindowResize(this.resize, this); 
32294
32295         if(!this.isAutoInitial){
32296             this.layout();
32297             return;
32298         }
32299         
32300         this.layout.defer(500,this);
32301     },
32302     
32303     reloadItems: function()
32304     {
32305         this.bricks = this.el.select('.masonry-brick', true);
32306         
32307         this.bricks.each(function(b) {
32308             //Roo.log(b.getSize());
32309             if (!b.attr('originalwidth')) {
32310                 b.attr('originalwidth',  b.getSize().width);
32311             }
32312             
32313         });
32314         
32315         Roo.log(this.bricks.elements.length);
32316     },
32317     
32318     resize : function()
32319     {
32320         Roo.log('resize');
32321         var cs = this.el.getBox(true);
32322         
32323         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32324             Roo.log("no change in with or X");
32325             return;
32326         }
32327         this.currentSize = cs;
32328         this.layout();
32329     },
32330     
32331     layout : function()
32332     {
32333          Roo.log('layout');
32334         this._resetLayout();
32335         //this._manageStamps();
32336       
32337         // don't animate first layout
32338         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32339         this.layoutItems( isInstant );
32340       
32341         // flag for initalized
32342         this._isLayoutInited = true;
32343     },
32344     
32345     layoutItems : function( isInstant )
32346     {
32347         //var items = this._getItemsForLayout( this.items );
32348         // original code supports filtering layout items.. we just ignore it..
32349         
32350         this._layoutItems( this.bricks , isInstant );
32351       
32352         this._postLayout();
32353     },
32354     _layoutItems : function ( items , isInstant)
32355     {
32356        //this.fireEvent( 'layout', this, items );
32357     
32358
32359         if ( !items || !items.elements.length ) {
32360           // no items, emit event with empty array
32361             return;
32362         }
32363
32364         var queue = [];
32365         items.each(function(item) {
32366             Roo.log("layout item");
32367             Roo.log(item);
32368             // get x/y object from method
32369             var position = this._getItemLayoutPosition( item );
32370             // enqueue
32371             position.item = item;
32372             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32373             queue.push( position );
32374         }, this);
32375       
32376         this._processLayoutQueue( queue );
32377     },
32378     /** Sets position of item in DOM
32379     * @param {Element} item
32380     * @param {Number} x - horizontal position
32381     * @param {Number} y - vertical position
32382     * @param {Boolean} isInstant - disables transitions
32383     */
32384     _processLayoutQueue : function( queue )
32385     {
32386         for ( var i=0, len = queue.length; i < len; i++ ) {
32387             var obj = queue[i];
32388             obj.item.position('absolute');
32389             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32390         }
32391     },
32392       
32393     
32394     /**
32395     * Any logic you want to do after each layout,
32396     * i.e. size the container
32397     */
32398     _postLayout : function()
32399     {
32400         this.resizeContainer();
32401     },
32402     
32403     resizeContainer : function()
32404     {
32405         if ( !this.isResizingContainer ) {
32406             return;
32407         }
32408         var size = this._getContainerSize();
32409         if ( size ) {
32410             this.el.setSize(size.width,size.height);
32411             this.boxesEl.setSize(size.width,size.height);
32412         }
32413     },
32414     
32415     
32416     
32417     _resetLayout : function()
32418     {
32419         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32420         this.colWidth = this.el.getWidth();
32421         //this.gutter = this.el.getWidth(); 
32422         
32423         this.measureColumns();
32424
32425         // reset column Y
32426         var i = this.cols;
32427         this.colYs = [];
32428         while (i--) {
32429             this.colYs.push( 0 );
32430         }
32431     
32432         this.maxY = 0;
32433     },
32434
32435     measureColumns : function()
32436     {
32437         this.getContainerWidth();
32438       // if columnWidth is 0, default to outerWidth of first item
32439         if ( !this.columnWidth ) {
32440             var firstItem = this.bricks.first();
32441             Roo.log(firstItem);
32442             this.columnWidth  = this.containerWidth;
32443             if (firstItem && firstItem.attr('originalwidth') ) {
32444                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32445             }
32446             // columnWidth fall back to item of first element
32447             Roo.log("set column width?");
32448                         this.initialColumnWidth = this.columnWidth  ;
32449
32450             // if first elem has no width, default to size of container
32451             
32452         }
32453         
32454         
32455         if (this.initialColumnWidth) {
32456             this.columnWidth = this.initialColumnWidth;
32457         }
32458         
32459         
32460             
32461         // column width is fixed at the top - however if container width get's smaller we should
32462         // reduce it...
32463         
32464         // this bit calcs how man columns..
32465             
32466         var columnWidth = this.columnWidth += this.gutter;
32467       
32468         // calculate columns
32469         var containerWidth = this.containerWidth + this.gutter;
32470         
32471         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32472         // fix rounding errors, typically with gutters
32473         var excess = columnWidth - containerWidth % columnWidth;
32474         
32475         
32476         // if overshoot is less than a pixel, round up, otherwise floor it
32477         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32478         cols = Math[ mathMethod ]( cols );
32479         this.cols = Math.max( cols, 1 );
32480         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32481         
32482          // padding positioning..
32483         var totalColWidth = this.cols * this.columnWidth;
32484         var padavail = this.containerWidth - totalColWidth;
32485         // so for 2 columns - we need 3 'pads'
32486         
32487         var padNeeded = (1+this.cols) * this.padWidth;
32488         
32489         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32490         
32491         this.columnWidth += padExtra
32492         //this.padWidth = Math.floor(padavail /  ( this.cols));
32493         
32494         // adjust colum width so that padding is fixed??
32495         
32496         // we have 3 columns ... total = width * 3
32497         // we have X left over... that should be used by 
32498         
32499         //if (this.expandC) {
32500             
32501         //}
32502         
32503         
32504         
32505     },
32506     
32507     getContainerWidth : function()
32508     {
32509        /* // container is parent if fit width
32510         var container = this.isFitWidth ? this.element.parentNode : this.element;
32511         // check that this.size and size are there
32512         // IE8 triggers resize on body size change, so they might not be
32513         
32514         var size = getSize( container );  //FIXME
32515         this.containerWidth = size && size.innerWidth; //FIXME
32516         */
32517          
32518         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32519         
32520     },
32521     
32522     _getItemLayoutPosition : function( item )  // what is item?
32523     {
32524         // we resize the item to our columnWidth..
32525       
32526         item.setWidth(this.columnWidth);
32527         item.autoBoxAdjust  = false;
32528         
32529         var sz = item.getSize();
32530  
32531         // how many columns does this brick span
32532         var remainder = this.containerWidth % this.columnWidth;
32533         
32534         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32535         // round if off by 1 pixel, otherwise use ceil
32536         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32537         colSpan = Math.min( colSpan, this.cols );
32538         
32539         // normally this should be '1' as we dont' currently allow multi width columns..
32540         
32541         var colGroup = this._getColGroup( colSpan );
32542         // get the minimum Y value from the columns
32543         var minimumY = Math.min.apply( Math, colGroup );
32544         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32545         
32546         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32547          
32548         // position the brick
32549         var position = {
32550             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32551             y: this.currentSize.y + minimumY + this.padHeight
32552         };
32553         
32554         Roo.log(position);
32555         // apply setHeight to necessary columns
32556         var setHeight = minimumY + sz.height + this.padHeight;
32557         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32558         
32559         var setSpan = this.cols + 1 - colGroup.length;
32560         for ( var i = 0; i < setSpan; i++ ) {
32561           this.colYs[ shortColIndex + i ] = setHeight ;
32562         }
32563       
32564         return position;
32565     },
32566     
32567     /**
32568      * @param {Number} colSpan - number of columns the element spans
32569      * @returns {Array} colGroup
32570      */
32571     _getColGroup : function( colSpan )
32572     {
32573         if ( colSpan < 2 ) {
32574           // if brick spans only one column, use all the column Ys
32575           return this.colYs;
32576         }
32577       
32578         var colGroup = [];
32579         // how many different places could this brick fit horizontally
32580         var groupCount = this.cols + 1 - colSpan;
32581         // for each group potential horizontal position
32582         for ( var i = 0; i < groupCount; i++ ) {
32583           // make an array of colY values for that one group
32584           var groupColYs = this.colYs.slice( i, i + colSpan );
32585           // and get the max value of the array
32586           colGroup[i] = Math.max.apply( Math, groupColYs );
32587         }
32588         return colGroup;
32589     },
32590     /*
32591     _manageStamp : function( stamp )
32592     {
32593         var stampSize =  stamp.getSize();
32594         var offset = stamp.getBox();
32595         // get the columns that this stamp affects
32596         var firstX = this.isOriginLeft ? offset.x : offset.right;
32597         var lastX = firstX + stampSize.width;
32598         var firstCol = Math.floor( firstX / this.columnWidth );
32599         firstCol = Math.max( 0, firstCol );
32600         
32601         var lastCol = Math.floor( lastX / this.columnWidth );
32602         // lastCol should not go over if multiple of columnWidth #425
32603         lastCol -= lastX % this.columnWidth ? 0 : 1;
32604         lastCol = Math.min( this.cols - 1, lastCol );
32605         
32606         // set colYs to bottom of the stamp
32607         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32608             stampSize.height;
32609             
32610         for ( var i = firstCol; i <= lastCol; i++ ) {
32611           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32612         }
32613     },
32614     */
32615     
32616     _getContainerSize : function()
32617     {
32618         this.maxY = Math.max.apply( Math, this.colYs );
32619         var size = {
32620             height: this.maxY
32621         };
32622       
32623         if ( this.isFitWidth ) {
32624             size.width = this._getContainerFitWidth();
32625         }
32626       
32627         return size;
32628     },
32629     
32630     _getContainerFitWidth : function()
32631     {
32632         var unusedCols = 0;
32633         // count unused columns
32634         var i = this.cols;
32635         while ( --i ) {
32636           if ( this.colYs[i] !== 0 ) {
32637             break;
32638           }
32639           unusedCols++;
32640         }
32641         // fit container to columns that have been used
32642         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32643     },
32644     
32645     needsResizeLayout : function()
32646     {
32647         var previousWidth = this.containerWidth;
32648         this.getContainerWidth();
32649         return previousWidth !== this.containerWidth;
32650     }
32651  
32652 });
32653
32654  
32655
32656  /*
32657  * - LGPL
32658  *
32659  * element
32660  * 
32661  */
32662
32663 /**
32664  * @class Roo.bootstrap.MasonryBrick
32665  * @extends Roo.bootstrap.Component
32666  * Bootstrap MasonryBrick class
32667  * 
32668  * @constructor
32669  * Create a new MasonryBrick
32670  * @param {Object} config The config object
32671  */
32672
32673 Roo.bootstrap.MasonryBrick = function(config){
32674     
32675     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32676     
32677     Roo.bootstrap.MasonryBrick.register(this);
32678     
32679     this.addEvents({
32680         // raw events
32681         /**
32682          * @event click
32683          * When a MasonryBrick is clcik
32684          * @param {Roo.bootstrap.MasonryBrick} this
32685          * @param {Roo.EventObject} e
32686          */
32687         "click" : true
32688     });
32689 };
32690
32691 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32692     
32693     /**
32694      * @cfg {String} title
32695      */   
32696     title : '',
32697     /**
32698      * @cfg {String} html
32699      */   
32700     html : '',
32701     /**
32702      * @cfg {String} bgimage
32703      */   
32704     bgimage : '',
32705     /**
32706      * @cfg {String} videourl
32707      */   
32708     videourl : '',
32709     /**
32710      * @cfg {String} cls
32711      */   
32712     cls : '',
32713     /**
32714      * @cfg {String} href
32715      */   
32716     href : '',
32717     /**
32718      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32719      */   
32720     size : 'xs',
32721     
32722     /**
32723      * @cfg {String} placetitle (center|bottom)
32724      */   
32725     placetitle : '',
32726     
32727     /**
32728      * @cfg {Boolean} isFitContainer defalut true
32729      */   
32730     isFitContainer : true, 
32731     
32732     /**
32733      * @cfg {Boolean} preventDefault defalut false
32734      */   
32735     preventDefault : false, 
32736     
32737     /**
32738      * @cfg {Boolean} inverse defalut false
32739      */   
32740     maskInverse : false, 
32741     
32742     getAutoCreate : function()
32743     {
32744         if(!this.isFitContainer){
32745             return this.getSplitAutoCreate();
32746         }
32747         
32748         var cls = 'masonry-brick masonry-brick-full';
32749         
32750         if(this.href.length){
32751             cls += ' masonry-brick-link';
32752         }
32753         
32754         if(this.bgimage.length){
32755             cls += ' masonry-brick-image';
32756         }
32757         
32758         if(this.maskInverse){
32759             cls += ' mask-inverse';
32760         }
32761         
32762         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32763             cls += ' enable-mask';
32764         }
32765         
32766         if(this.size){
32767             cls += ' masonry-' + this.size + '-brick';
32768         }
32769         
32770         if(this.placetitle.length){
32771             
32772             switch (this.placetitle) {
32773                 case 'center' :
32774                     cls += ' masonry-center-title';
32775                     break;
32776                 case 'bottom' :
32777                     cls += ' masonry-bottom-title';
32778                     break;
32779                 default:
32780                     break;
32781             }
32782             
32783         } else {
32784             if(!this.html.length && !this.bgimage.length){
32785                 cls += ' masonry-center-title';
32786             }
32787
32788             if(!this.html.length && this.bgimage.length){
32789                 cls += ' masonry-bottom-title';
32790             }
32791         }
32792         
32793         if(this.cls){
32794             cls += ' ' + this.cls;
32795         }
32796         
32797         var cfg = {
32798             tag: (this.href.length) ? 'a' : 'div',
32799             cls: cls,
32800             cn: [
32801                 {
32802                     tag: 'div',
32803                     cls: 'masonry-brick-mask'
32804                 },
32805                 {
32806                     tag: 'div',
32807                     cls: 'masonry-brick-paragraph',
32808                     cn: []
32809                 }
32810             ]
32811         };
32812         
32813         if(this.href.length){
32814             cfg.href = this.href;
32815         }
32816         
32817         var cn = cfg.cn[1].cn;
32818         
32819         if(this.title.length){
32820             cn.push({
32821                 tag: 'h4',
32822                 cls: 'masonry-brick-title',
32823                 html: this.title
32824             });
32825         }
32826         
32827         if(this.html.length){
32828             cn.push({
32829                 tag: 'p',
32830                 cls: 'masonry-brick-text',
32831                 html: this.html
32832             });
32833         }
32834         
32835         if (!this.title.length && !this.html.length) {
32836             cfg.cn[1].cls += ' hide';
32837         }
32838         
32839         if(this.bgimage.length){
32840             cfg.cn.push({
32841                 tag: 'img',
32842                 cls: 'masonry-brick-image-view',
32843                 src: this.bgimage
32844             });
32845         }
32846         
32847         if(this.videourl.length){
32848             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32849             // youtube support only?
32850             cfg.cn.push({
32851                 tag: 'iframe',
32852                 cls: 'masonry-brick-image-view',
32853                 src: vurl,
32854                 frameborder : 0,
32855                 allowfullscreen : true
32856             });
32857         }
32858         
32859         return cfg;
32860         
32861     },
32862     
32863     getSplitAutoCreate : function()
32864     {
32865         var cls = 'masonry-brick masonry-brick-split';
32866         
32867         if(this.href.length){
32868             cls += ' masonry-brick-link';
32869         }
32870         
32871         if(this.bgimage.length){
32872             cls += ' masonry-brick-image';
32873         }
32874         
32875         if(this.size){
32876             cls += ' masonry-' + this.size + '-brick';
32877         }
32878         
32879         switch (this.placetitle) {
32880             case 'center' :
32881                 cls += ' masonry-center-title';
32882                 break;
32883             case 'bottom' :
32884                 cls += ' masonry-bottom-title';
32885                 break;
32886             default:
32887                 if(!this.bgimage.length){
32888                     cls += ' masonry-center-title';
32889                 }
32890
32891                 if(this.bgimage.length){
32892                     cls += ' masonry-bottom-title';
32893                 }
32894                 break;
32895         }
32896         
32897         if(this.cls){
32898             cls += ' ' + this.cls;
32899         }
32900         
32901         var cfg = {
32902             tag: (this.href.length) ? 'a' : 'div',
32903             cls: cls,
32904             cn: [
32905                 {
32906                     tag: 'div',
32907                     cls: 'masonry-brick-split-head',
32908                     cn: [
32909                         {
32910                             tag: 'div',
32911                             cls: 'masonry-brick-paragraph',
32912                             cn: []
32913                         }
32914                     ]
32915                 },
32916                 {
32917                     tag: 'div',
32918                     cls: 'masonry-brick-split-body',
32919                     cn: []
32920                 }
32921             ]
32922         };
32923         
32924         if(this.href.length){
32925             cfg.href = this.href;
32926         }
32927         
32928         if(this.title.length){
32929             cfg.cn[0].cn[0].cn.push({
32930                 tag: 'h4',
32931                 cls: 'masonry-brick-title',
32932                 html: this.title
32933             });
32934         }
32935         
32936         if(this.html.length){
32937             cfg.cn[1].cn.push({
32938                 tag: 'p',
32939                 cls: 'masonry-brick-text',
32940                 html: this.html
32941             });
32942         }
32943
32944         if(this.bgimage.length){
32945             cfg.cn[0].cn.push({
32946                 tag: 'img',
32947                 cls: 'masonry-brick-image-view',
32948                 src: this.bgimage
32949             });
32950         }
32951         
32952         if(this.videourl.length){
32953             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32954             // youtube support only?
32955             cfg.cn[0].cn.cn.push({
32956                 tag: 'iframe',
32957                 cls: 'masonry-brick-image-view',
32958                 src: vurl,
32959                 frameborder : 0,
32960                 allowfullscreen : true
32961             });
32962         }
32963         
32964         return cfg;
32965     },
32966     
32967     initEvents: function() 
32968     {
32969         switch (this.size) {
32970             case 'xs' :
32971                 this.x = 1;
32972                 this.y = 1;
32973                 break;
32974             case 'sm' :
32975                 this.x = 2;
32976                 this.y = 2;
32977                 break;
32978             case 'md' :
32979             case 'md-left' :
32980             case 'md-right' :
32981                 this.x = 3;
32982                 this.y = 3;
32983                 break;
32984             case 'tall' :
32985                 this.x = 2;
32986                 this.y = 3;
32987                 break;
32988             case 'wide' :
32989                 this.x = 3;
32990                 this.y = 2;
32991                 break;
32992             case 'wide-thin' :
32993                 this.x = 3;
32994                 this.y = 1;
32995                 break;
32996                         
32997             default :
32998                 break;
32999         }
33000         
33001         if(Roo.isTouch){
33002             this.el.on('touchstart', this.onTouchStart, this);
33003             this.el.on('touchmove', this.onTouchMove, this);
33004             this.el.on('touchend', this.onTouchEnd, this);
33005             this.el.on('contextmenu', this.onContextMenu, this);
33006         } else {
33007             this.el.on('mouseenter'  ,this.enter, this);
33008             this.el.on('mouseleave', this.leave, this);
33009             this.el.on('click', this.onClick, this);
33010         }
33011         
33012         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33013             this.parent().bricks.push(this);   
33014         }
33015         
33016     },
33017     
33018     onClick: function(e, el)
33019     {
33020         var time = this.endTimer - this.startTimer;
33021         // Roo.log(e.preventDefault());
33022         if(Roo.isTouch){
33023             if(time > 1000){
33024                 e.preventDefault();
33025                 return;
33026             }
33027         }
33028         
33029         if(!this.preventDefault){
33030             return;
33031         }
33032         
33033         e.preventDefault();
33034         
33035         if (this.activeClass != '') {
33036             this.selectBrick();
33037         }
33038         
33039         this.fireEvent('click', this, e);
33040     },
33041     
33042     enter: function(e, el)
33043     {
33044         e.preventDefault();
33045         
33046         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33047             return;
33048         }
33049         
33050         if(this.bgimage.length && this.html.length){
33051             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33052         }
33053     },
33054     
33055     leave: function(e, el)
33056     {
33057         e.preventDefault();
33058         
33059         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33060             return;
33061         }
33062         
33063         if(this.bgimage.length && this.html.length){
33064             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33065         }
33066     },
33067     
33068     onTouchStart: function(e, el)
33069     {
33070 //        e.preventDefault();
33071         
33072         this.touchmoved = false;
33073         
33074         if(!this.isFitContainer){
33075             return;
33076         }
33077         
33078         if(!this.bgimage.length || !this.html.length){
33079             return;
33080         }
33081         
33082         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33083         
33084         this.timer = new Date().getTime();
33085         
33086     },
33087     
33088     onTouchMove: function(e, el)
33089     {
33090         this.touchmoved = true;
33091     },
33092     
33093     onContextMenu : function(e,el)
33094     {
33095         e.preventDefault();
33096         e.stopPropagation();
33097         return false;
33098     },
33099     
33100     onTouchEnd: function(e, el)
33101     {
33102 //        e.preventDefault();
33103         
33104         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33105         
33106             this.leave(e,el);
33107             
33108             return;
33109         }
33110         
33111         if(!this.bgimage.length || !this.html.length){
33112             
33113             if(this.href.length){
33114                 window.location.href = this.href;
33115             }
33116             
33117             return;
33118         }
33119         
33120         if(!this.isFitContainer){
33121             return;
33122         }
33123         
33124         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33125         
33126         window.location.href = this.href;
33127     },
33128     
33129     //selection on single brick only
33130     selectBrick : function() {
33131         
33132         if (!this.parentId) {
33133             return;
33134         }
33135         
33136         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33137         var index = m.selectedBrick.indexOf(this.id);
33138         
33139         if ( index > -1) {
33140             m.selectedBrick.splice(index,1);
33141             this.el.removeClass(this.activeClass);
33142             return;
33143         }
33144         
33145         for(var i = 0; i < m.selectedBrick.length; i++) {
33146             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33147             b.el.removeClass(b.activeClass);
33148         }
33149         
33150         m.selectedBrick = [];
33151         
33152         m.selectedBrick.push(this.id);
33153         this.el.addClass(this.activeClass);
33154         return;
33155     },
33156     
33157     isSelected : function(){
33158         return this.el.hasClass(this.activeClass);
33159         
33160     }
33161 });
33162
33163 Roo.apply(Roo.bootstrap.MasonryBrick, {
33164     
33165     //groups: {},
33166     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33167      /**
33168     * register a Masonry Brick
33169     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33170     */
33171     
33172     register : function(brick)
33173     {
33174         //this.groups[brick.id] = brick;
33175         this.groups.add(brick.id, brick);
33176     },
33177     /**
33178     * fetch a  masonry brick based on the masonry brick ID
33179     * @param {string} the masonry brick to add
33180     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33181     */
33182     
33183     get: function(brick_id) 
33184     {
33185         // if (typeof(this.groups[brick_id]) == 'undefined') {
33186         //     return false;
33187         // }
33188         // return this.groups[brick_id] ;
33189         
33190         if(this.groups.key(brick_id)) {
33191             return this.groups.key(brick_id);
33192         }
33193         
33194         return false;
33195     }
33196     
33197     
33198     
33199 });
33200
33201  /*
33202  * - LGPL
33203  *
33204  * element
33205  * 
33206  */
33207
33208 /**
33209  * @class Roo.bootstrap.Brick
33210  * @extends Roo.bootstrap.Component
33211  * Bootstrap Brick class
33212  * 
33213  * @constructor
33214  * Create a new Brick
33215  * @param {Object} config The config object
33216  */
33217
33218 Roo.bootstrap.Brick = function(config){
33219     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33220     
33221     this.addEvents({
33222         // raw events
33223         /**
33224          * @event click
33225          * When a Brick is click
33226          * @param {Roo.bootstrap.Brick} this
33227          * @param {Roo.EventObject} e
33228          */
33229         "click" : true
33230     });
33231 };
33232
33233 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33234     
33235     /**
33236      * @cfg {String} title
33237      */   
33238     title : '',
33239     /**
33240      * @cfg {String} html
33241      */   
33242     html : '',
33243     /**
33244      * @cfg {String} bgimage
33245      */   
33246     bgimage : '',
33247     /**
33248      * @cfg {String} cls
33249      */   
33250     cls : '',
33251     /**
33252      * @cfg {String} href
33253      */   
33254     href : '',
33255     /**
33256      * @cfg {String} video
33257      */   
33258     video : '',
33259     /**
33260      * @cfg {Boolean} square
33261      */   
33262     square : true,
33263     
33264     getAutoCreate : function()
33265     {
33266         var cls = 'roo-brick';
33267         
33268         if(this.href.length){
33269             cls += ' roo-brick-link';
33270         }
33271         
33272         if(this.bgimage.length){
33273             cls += ' roo-brick-image';
33274         }
33275         
33276         if(!this.html.length && !this.bgimage.length){
33277             cls += ' roo-brick-center-title';
33278         }
33279         
33280         if(!this.html.length && this.bgimage.length){
33281             cls += ' roo-brick-bottom-title';
33282         }
33283         
33284         if(this.cls){
33285             cls += ' ' + this.cls;
33286         }
33287         
33288         var cfg = {
33289             tag: (this.href.length) ? 'a' : 'div',
33290             cls: cls,
33291             cn: [
33292                 {
33293                     tag: 'div',
33294                     cls: 'roo-brick-paragraph',
33295                     cn: []
33296                 }
33297             ]
33298         };
33299         
33300         if(this.href.length){
33301             cfg.href = this.href;
33302         }
33303         
33304         var cn = cfg.cn[0].cn;
33305         
33306         if(this.title.length){
33307             cn.push({
33308                 tag: 'h4',
33309                 cls: 'roo-brick-title',
33310                 html: this.title
33311             });
33312         }
33313         
33314         if(this.html.length){
33315             cn.push({
33316                 tag: 'p',
33317                 cls: 'roo-brick-text',
33318                 html: this.html
33319             });
33320         } else {
33321             cn.cls += ' hide';
33322         }
33323         
33324         if(this.bgimage.length){
33325             cfg.cn.push({
33326                 tag: 'img',
33327                 cls: 'roo-brick-image-view',
33328                 src: this.bgimage
33329             });
33330         }
33331         
33332         return cfg;
33333     },
33334     
33335     initEvents: function() 
33336     {
33337         if(this.title.length || this.html.length){
33338             this.el.on('mouseenter'  ,this.enter, this);
33339             this.el.on('mouseleave', this.leave, this);
33340         }
33341         
33342         Roo.EventManager.onWindowResize(this.resize, this); 
33343         
33344         if(this.bgimage.length){
33345             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33346             this.imageEl.on('load', this.onImageLoad, this);
33347             return;
33348         }
33349         
33350         this.resize();
33351     },
33352     
33353     onImageLoad : function()
33354     {
33355         this.resize();
33356     },
33357     
33358     resize : function()
33359     {
33360         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33361         
33362         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33363         
33364         if(this.bgimage.length){
33365             var image = this.el.select('.roo-brick-image-view', true).first();
33366             
33367             image.setWidth(paragraph.getWidth());
33368             
33369             if(this.square){
33370                 image.setHeight(paragraph.getWidth());
33371             }
33372             
33373             this.el.setHeight(image.getHeight());
33374             paragraph.setHeight(image.getHeight());
33375             
33376         }
33377         
33378     },
33379     
33380     enter: function(e, el)
33381     {
33382         e.preventDefault();
33383         
33384         if(this.bgimage.length){
33385             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33386             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33387         }
33388     },
33389     
33390     leave: function(e, el)
33391     {
33392         e.preventDefault();
33393         
33394         if(this.bgimage.length){
33395             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33396             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33397         }
33398     }
33399     
33400 });
33401
33402  
33403
33404  /*
33405  * - LGPL
33406  *
33407  * Number field 
33408  */
33409
33410 /**
33411  * @class Roo.bootstrap.NumberField
33412  * @extends Roo.bootstrap.Input
33413  * Bootstrap NumberField class
33414  * 
33415  * 
33416  * 
33417  * 
33418  * @constructor
33419  * Create a new NumberField
33420  * @param {Object} config The config object
33421  */
33422
33423 Roo.bootstrap.NumberField = function(config){
33424     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33425 };
33426
33427 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33428     
33429     /**
33430      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33431      */
33432     allowDecimals : true,
33433     /**
33434      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33435      */
33436     decimalSeparator : ".",
33437     /**
33438      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33439      */
33440     decimalPrecision : 2,
33441     /**
33442      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33443      */
33444     allowNegative : true,
33445     
33446     /**
33447      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33448      */
33449     allowZero: true,
33450     /**
33451      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33452      */
33453     minValue : Number.NEGATIVE_INFINITY,
33454     /**
33455      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33456      */
33457     maxValue : Number.MAX_VALUE,
33458     /**
33459      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33460      */
33461     minText : "The minimum value for this field is {0}",
33462     /**
33463      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33464      */
33465     maxText : "The maximum value for this field is {0}",
33466     /**
33467      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33468      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33469      */
33470     nanText : "{0} is not a valid number",
33471     /**
33472      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33473      */
33474     thousandsDelimiter : false,
33475     /**
33476      * @cfg {String} valueAlign alignment of value
33477      */
33478     valueAlign : "left",
33479
33480     getAutoCreate : function()
33481     {
33482         var hiddenInput = {
33483             tag: 'input',
33484             type: 'hidden',
33485             id: Roo.id(),
33486             cls: 'hidden-number-input'
33487         };
33488         
33489         if (this.name) {
33490             hiddenInput.name = this.name;
33491         }
33492         
33493         this.name = '';
33494         
33495         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33496         
33497         this.name = hiddenInput.name;
33498         
33499         if(cfg.cn.length > 0) {
33500             cfg.cn.push(hiddenInput);
33501         }
33502         
33503         return cfg;
33504     },
33505
33506     // private
33507     initEvents : function()
33508     {   
33509         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33510         
33511         var allowed = "0123456789";
33512         
33513         if(this.allowDecimals){
33514             allowed += this.decimalSeparator;
33515         }
33516         
33517         if(this.allowNegative){
33518             allowed += "-";
33519         }
33520         
33521         if(this.thousandsDelimiter) {
33522             allowed += ",";
33523         }
33524         
33525         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33526         
33527         var keyPress = function(e){
33528             
33529             var k = e.getKey();
33530             
33531             var c = e.getCharCode();
33532             
33533             if(
33534                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33535                     allowed.indexOf(String.fromCharCode(c)) === -1
33536             ){
33537                 e.stopEvent();
33538                 return;
33539             }
33540             
33541             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33542                 return;
33543             }
33544             
33545             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33546                 e.stopEvent();
33547             }
33548         };
33549         
33550         this.el.on("keypress", keyPress, this);
33551     },
33552     
33553     validateValue : function(value)
33554     {
33555         
33556         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33557             return false;
33558         }
33559         
33560         var num = this.parseValue(value);
33561         
33562         if(isNaN(num)){
33563             this.markInvalid(String.format(this.nanText, value));
33564             return false;
33565         }
33566         
33567         if(num < this.minValue){
33568             this.markInvalid(String.format(this.minText, this.minValue));
33569             return false;
33570         }
33571         
33572         if(num > this.maxValue){
33573             this.markInvalid(String.format(this.maxText, this.maxValue));
33574             return false;
33575         }
33576         
33577         return true;
33578     },
33579
33580     getValue : function()
33581     {
33582         var v = this.hiddenEl().getValue();
33583         
33584         return this.fixPrecision(this.parseValue(v));
33585     },
33586
33587     parseValue : function(value)
33588     {
33589         if(this.thousandsDelimiter) {
33590             value += "";
33591             r = new RegExp(",", "g");
33592             value = value.replace(r, "");
33593         }
33594         
33595         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33596         return isNaN(value) ? '' : value;
33597     },
33598
33599     fixPrecision : function(value)
33600     {
33601         if(this.thousandsDelimiter) {
33602             value += "";
33603             r = new RegExp(",", "g");
33604             value = value.replace(r, "");
33605         }
33606         
33607         var nan = isNaN(value);
33608         
33609         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33610             return nan ? '' : value;
33611         }
33612         return parseFloat(value).toFixed(this.decimalPrecision);
33613     },
33614
33615     setValue : function(v)
33616     {
33617         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33618         
33619         this.value = v;
33620         
33621         if(this.rendered){
33622             
33623             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33624             
33625             this.inputEl().dom.value = (v == '') ? '' :
33626                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33627             
33628             if(!this.allowZero && v === '0') {
33629                 this.hiddenEl().dom.value = '';
33630                 this.inputEl().dom.value = '';
33631             }
33632             
33633             this.validate();
33634         }
33635     },
33636
33637     decimalPrecisionFcn : function(v)
33638     {
33639         return Math.floor(v);
33640     },
33641
33642     beforeBlur : function()
33643     {
33644         var v = this.parseValue(this.getRawValue());
33645         
33646         if(v || v === 0 || v === ''){
33647             this.setValue(v);
33648         }
33649     },
33650     
33651     hiddenEl : function()
33652     {
33653         return this.el.select('input.hidden-number-input',true).first();
33654     }
33655     
33656 });
33657
33658  
33659
33660 /*
33661 * Licence: LGPL
33662 */
33663
33664 /**
33665  * @class Roo.bootstrap.DocumentSlider
33666  * @extends Roo.bootstrap.Component
33667  * Bootstrap DocumentSlider class
33668  * 
33669  * @constructor
33670  * Create a new DocumentViewer
33671  * @param {Object} config The config object
33672  */
33673
33674 Roo.bootstrap.DocumentSlider = function(config){
33675     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33676     
33677     this.files = [];
33678     
33679     this.addEvents({
33680         /**
33681          * @event initial
33682          * Fire after initEvent
33683          * @param {Roo.bootstrap.DocumentSlider} this
33684          */
33685         "initial" : true,
33686         /**
33687          * @event update
33688          * Fire after update
33689          * @param {Roo.bootstrap.DocumentSlider} this
33690          */
33691         "update" : true,
33692         /**
33693          * @event click
33694          * Fire after click
33695          * @param {Roo.bootstrap.DocumentSlider} this
33696          */
33697         "click" : true
33698     });
33699 };
33700
33701 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33702     
33703     files : false,
33704     
33705     indicator : 0,
33706     
33707     getAutoCreate : function()
33708     {
33709         var cfg = {
33710             tag : 'div',
33711             cls : 'roo-document-slider',
33712             cn : [
33713                 {
33714                     tag : 'div',
33715                     cls : 'roo-document-slider-header',
33716                     cn : [
33717                         {
33718                             tag : 'div',
33719                             cls : 'roo-document-slider-header-title'
33720                         }
33721                     ]
33722                 },
33723                 {
33724                     tag : 'div',
33725                     cls : 'roo-document-slider-body',
33726                     cn : [
33727                         {
33728                             tag : 'div',
33729                             cls : 'roo-document-slider-prev',
33730                             cn : [
33731                                 {
33732                                     tag : 'i',
33733                                     cls : 'fa fa-chevron-left'
33734                                 }
33735                             ]
33736                         },
33737                         {
33738                             tag : 'div',
33739                             cls : 'roo-document-slider-thumb',
33740                             cn : [
33741                                 {
33742                                     tag : 'img',
33743                                     cls : 'roo-document-slider-image'
33744                                 }
33745                             ]
33746                         },
33747                         {
33748                             tag : 'div',
33749                             cls : 'roo-document-slider-next',
33750                             cn : [
33751                                 {
33752                                     tag : 'i',
33753                                     cls : 'fa fa-chevron-right'
33754                                 }
33755                             ]
33756                         }
33757                     ]
33758                 }
33759             ]
33760         };
33761         
33762         return cfg;
33763     },
33764     
33765     initEvents : function()
33766     {
33767         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33768         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33769         
33770         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33771         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33772         
33773         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33774         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33775         
33776         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33777         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33778         
33779         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33780         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33781         
33782         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33783         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33784         
33785         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33786         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33787         
33788         this.thumbEl.on('click', this.onClick, this);
33789         
33790         this.prevIndicator.on('click', this.prev, this);
33791         
33792         this.nextIndicator.on('click', this.next, this);
33793         
33794     },
33795     
33796     initial : function()
33797     {
33798         if(this.files.length){
33799             this.indicator = 1;
33800             this.update()
33801         }
33802         
33803         this.fireEvent('initial', this);
33804     },
33805     
33806     update : function()
33807     {
33808         this.imageEl.attr('src', this.files[this.indicator - 1]);
33809         
33810         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33811         
33812         this.prevIndicator.show();
33813         
33814         if(this.indicator == 1){
33815             this.prevIndicator.hide();
33816         }
33817         
33818         this.nextIndicator.show();
33819         
33820         if(this.indicator == this.files.length){
33821             this.nextIndicator.hide();
33822         }
33823         
33824         this.thumbEl.scrollTo('top');
33825         
33826         this.fireEvent('update', this);
33827     },
33828     
33829     onClick : function(e)
33830     {
33831         e.preventDefault();
33832         
33833         this.fireEvent('click', this);
33834     },
33835     
33836     prev : function(e)
33837     {
33838         e.preventDefault();
33839         
33840         this.indicator = Math.max(1, this.indicator - 1);
33841         
33842         this.update();
33843     },
33844     
33845     next : function(e)
33846     {
33847         e.preventDefault();
33848         
33849         this.indicator = Math.min(this.files.length, this.indicator + 1);
33850         
33851         this.update();
33852     }
33853 });
33854 /*
33855  * - LGPL
33856  *
33857  * RadioSet
33858  *
33859  *
33860  */
33861
33862 /**
33863  * @class Roo.bootstrap.RadioSet
33864  * @extends Roo.bootstrap.Input
33865  * Bootstrap RadioSet class
33866  * @cfg {String} indicatorpos (left|right) default left
33867  * @cfg {Boolean} inline (true|false) inline the element (default true)
33868  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33869  * @constructor
33870  * Create a new RadioSet
33871  * @param {Object} config The config object
33872  */
33873
33874 Roo.bootstrap.RadioSet = function(config){
33875     
33876     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33877     
33878     this.radioes = [];
33879     
33880     Roo.bootstrap.RadioSet.register(this);
33881     
33882     this.addEvents({
33883         /**
33884         * @event check
33885         * Fires when the element is checked or unchecked.
33886         * @param {Roo.bootstrap.RadioSet} this This radio
33887         * @param {Roo.bootstrap.Radio} item The checked item
33888         */
33889        check : true,
33890        /**
33891         * @event click
33892         * Fires when the element is click.
33893         * @param {Roo.bootstrap.RadioSet} this This radio set
33894         * @param {Roo.bootstrap.Radio} item The checked item
33895         * @param {Roo.EventObject} e The event object
33896         */
33897        click : true
33898     });
33899     
33900 };
33901
33902 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33903
33904     radioes : false,
33905     
33906     inline : true,
33907     
33908     weight : '',
33909     
33910     indicatorpos : 'left',
33911     
33912     getAutoCreate : function()
33913     {
33914         var label = {
33915             tag : 'label',
33916             cls : 'roo-radio-set-label',
33917             cn : [
33918                 {
33919                     tag : 'span',
33920                     html : this.fieldLabel
33921                 }
33922             ]
33923         };
33924         
33925         if(this.indicatorpos == 'left'){
33926             label.cn.unshift({
33927                 tag : 'i',
33928                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33929                 tooltip : 'This field is required'
33930             });
33931         } else {
33932             label.cn.push({
33933                 tag : 'i',
33934                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33935                 tooltip : 'This field is required'
33936             });
33937         }
33938         
33939         var items = {
33940             tag : 'div',
33941             cls : 'roo-radio-set-items'
33942         };
33943         
33944         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33945         
33946         if (align === 'left' && this.fieldLabel.length) {
33947             
33948             items = {
33949                 cls : "roo-radio-set-right", 
33950                 cn: [
33951                     items
33952                 ]
33953             };
33954             
33955             if(this.labelWidth > 12){
33956                 label.style = "width: " + this.labelWidth + 'px';
33957             }
33958             
33959             if(this.labelWidth < 13 && this.labelmd == 0){
33960                 this.labelmd = this.labelWidth;
33961             }
33962             
33963             if(this.labellg > 0){
33964                 label.cls += ' col-lg-' + this.labellg;
33965                 items.cls += ' col-lg-' + (12 - this.labellg);
33966             }
33967             
33968             if(this.labelmd > 0){
33969                 label.cls += ' col-md-' + this.labelmd;
33970                 items.cls += ' col-md-' + (12 - this.labelmd);
33971             }
33972             
33973             if(this.labelsm > 0){
33974                 label.cls += ' col-sm-' + this.labelsm;
33975                 items.cls += ' col-sm-' + (12 - this.labelsm);
33976             }
33977             
33978             if(this.labelxs > 0){
33979                 label.cls += ' col-xs-' + this.labelxs;
33980                 items.cls += ' col-xs-' + (12 - this.labelxs);
33981             }
33982         }
33983         
33984         var cfg = {
33985             tag : 'div',
33986             cls : 'roo-radio-set',
33987             cn : [
33988                 {
33989                     tag : 'input',
33990                     cls : 'roo-radio-set-input',
33991                     type : 'hidden',
33992                     name : this.name,
33993                     value : this.value ? this.value :  ''
33994                 },
33995                 label,
33996                 items
33997             ]
33998         };
33999         
34000         if(this.weight.length){
34001             cfg.cls += ' roo-radio-' + this.weight;
34002         }
34003         
34004         if(this.inline) {
34005             cfg.cls += ' roo-radio-set-inline';
34006         }
34007         
34008         var settings=this;
34009         ['xs','sm','md','lg'].map(function(size){
34010             if (settings[size]) {
34011                 cfg.cls += ' col-' + size + '-' + settings[size];
34012             }
34013         });
34014         
34015         return cfg;
34016         
34017     },
34018
34019     initEvents : function()
34020     {
34021         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34022         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34023         
34024         if(!this.fieldLabel.length){
34025             this.labelEl.hide();
34026         }
34027         
34028         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34029         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34030         
34031         this.indicator = this.indicatorEl();
34032         
34033         if(this.indicator){
34034             this.indicator.addClass('invisible');
34035         }
34036         
34037         this.originalValue = this.getValue();
34038         
34039     },
34040     
34041     inputEl: function ()
34042     {
34043         return this.el.select('.roo-radio-set-input', true).first();
34044     },
34045     
34046     getChildContainer : function()
34047     {
34048         return this.itemsEl;
34049     },
34050     
34051     register : function(item)
34052     {
34053         this.radioes.push(item);
34054         
34055     },
34056     
34057     validate : function()
34058     {   
34059         if(this.getVisibilityEl().hasClass('hidden')){
34060             return true;
34061         }
34062         
34063         var valid = false;
34064         
34065         Roo.each(this.radioes, function(i){
34066             if(!i.checked){
34067                 return;
34068             }
34069             
34070             valid = true;
34071             return false;
34072         });
34073         
34074         if(this.allowBlank) {
34075             return true;
34076         }
34077         
34078         if(this.disabled || valid){
34079             this.markValid();
34080             return true;
34081         }
34082         
34083         this.markInvalid();
34084         return false;
34085         
34086     },
34087     
34088     markValid : function()
34089     {
34090         if(this.labelEl.isVisible(true)){
34091             this.indicatorEl().removeClass('visible');
34092             this.indicatorEl().addClass('invisible');
34093         }
34094         
34095         this.el.removeClass([this.invalidClass, this.validClass]);
34096         this.el.addClass(this.validClass);
34097         
34098         this.fireEvent('valid', this);
34099     },
34100     
34101     markInvalid : function(msg)
34102     {
34103         if(this.allowBlank || this.disabled){
34104             return;
34105         }
34106         
34107         if(this.labelEl.isVisible(true)){
34108             this.indicatorEl().removeClass('invisible');
34109             this.indicatorEl().addClass('visible');
34110         }
34111         
34112         this.el.removeClass([this.invalidClass, this.validClass]);
34113         this.el.addClass(this.invalidClass);
34114         
34115         this.fireEvent('invalid', this, msg);
34116         
34117     },
34118     
34119     setValue : function(v, suppressEvent)
34120     {   
34121         if(this.value === v){
34122             return;
34123         }
34124         
34125         this.value = v;
34126         
34127         if(this.rendered){
34128             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34129         }
34130         
34131         Roo.each(this.radioes, function(i){
34132             i.checked = false;
34133             i.el.removeClass('checked');
34134         });
34135         
34136         Roo.each(this.radioes, function(i){
34137             
34138             if(i.value === v || i.value.toString() === v.toString()){
34139                 i.checked = true;
34140                 i.el.addClass('checked');
34141                 
34142                 if(suppressEvent !== true){
34143                     this.fireEvent('check', this, i);
34144                 }
34145                 
34146                 return false;
34147             }
34148             
34149         }, this);
34150         
34151         this.validate();
34152     },
34153     
34154     clearInvalid : function(){
34155         
34156         if(!this.el || this.preventMark){
34157             return;
34158         }
34159         
34160         this.el.removeClass([this.invalidClass]);
34161         
34162         this.fireEvent('valid', this);
34163     }
34164     
34165 });
34166
34167 Roo.apply(Roo.bootstrap.RadioSet, {
34168     
34169     groups: {},
34170     
34171     register : function(set)
34172     {
34173         this.groups[set.name] = set;
34174     },
34175     
34176     get: function(name) 
34177     {
34178         if (typeof(this.groups[name]) == 'undefined') {
34179             return false;
34180         }
34181         
34182         return this.groups[name] ;
34183     }
34184     
34185 });
34186 /*
34187  * Based on:
34188  * Ext JS Library 1.1.1
34189  * Copyright(c) 2006-2007, Ext JS, LLC.
34190  *
34191  * Originally Released Under LGPL - original licence link has changed is not relivant.
34192  *
34193  * Fork - LGPL
34194  * <script type="text/javascript">
34195  */
34196
34197
34198 /**
34199  * @class Roo.bootstrap.SplitBar
34200  * @extends Roo.util.Observable
34201  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34202  * <br><br>
34203  * Usage:
34204  * <pre><code>
34205 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34206                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34207 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34208 split.minSize = 100;
34209 split.maxSize = 600;
34210 split.animate = true;
34211 split.on('moved', splitterMoved);
34212 </code></pre>
34213  * @constructor
34214  * Create a new SplitBar
34215  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34216  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34217  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34218  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34219                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34220                         position of the SplitBar).
34221  */
34222 Roo.bootstrap.SplitBar = function(cfg){
34223     
34224     /** @private */
34225     
34226     //{
34227     //  dragElement : elm
34228     //  resizingElement: el,
34229         // optional..
34230     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34231     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34232         // existingProxy ???
34233     //}
34234     
34235     this.el = Roo.get(cfg.dragElement, true);
34236     this.el.dom.unselectable = "on";
34237     /** @private */
34238     this.resizingEl = Roo.get(cfg.resizingElement, true);
34239
34240     /**
34241      * @private
34242      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34243      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34244      * @type Number
34245      */
34246     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34247     
34248     /**
34249      * The minimum size of the resizing element. (Defaults to 0)
34250      * @type Number
34251      */
34252     this.minSize = 0;
34253     
34254     /**
34255      * The maximum size of the resizing element. (Defaults to 2000)
34256      * @type Number
34257      */
34258     this.maxSize = 2000;
34259     
34260     /**
34261      * Whether to animate the transition to the new size
34262      * @type Boolean
34263      */
34264     this.animate = false;
34265     
34266     /**
34267      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34268      * @type Boolean
34269      */
34270     this.useShim = false;
34271     
34272     /** @private */
34273     this.shim = null;
34274     
34275     if(!cfg.existingProxy){
34276         /** @private */
34277         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34278     }else{
34279         this.proxy = Roo.get(cfg.existingProxy).dom;
34280     }
34281     /** @private */
34282     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34283     
34284     /** @private */
34285     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34286     
34287     /** @private */
34288     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34289     
34290     /** @private */
34291     this.dragSpecs = {};
34292     
34293     /**
34294      * @private The adapter to use to positon and resize elements
34295      */
34296     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34297     this.adapter.init(this);
34298     
34299     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34300         /** @private */
34301         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34302         this.el.addClass("roo-splitbar-h");
34303     }else{
34304         /** @private */
34305         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34306         this.el.addClass("roo-splitbar-v");
34307     }
34308     
34309     this.addEvents({
34310         /**
34311          * @event resize
34312          * Fires when the splitter is moved (alias for {@link #event-moved})
34313          * @param {Roo.bootstrap.SplitBar} this
34314          * @param {Number} newSize the new width or height
34315          */
34316         "resize" : true,
34317         /**
34318          * @event moved
34319          * Fires when the splitter is moved
34320          * @param {Roo.bootstrap.SplitBar} this
34321          * @param {Number} newSize the new width or height
34322          */
34323         "moved" : true,
34324         /**
34325          * @event beforeresize
34326          * Fires before the splitter is dragged
34327          * @param {Roo.bootstrap.SplitBar} this
34328          */
34329         "beforeresize" : true,
34330
34331         "beforeapply" : true
34332     });
34333
34334     Roo.util.Observable.call(this);
34335 };
34336
34337 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34338     onStartProxyDrag : function(x, y){
34339         this.fireEvent("beforeresize", this);
34340         if(!this.overlay){
34341             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34342             o.unselectable();
34343             o.enableDisplayMode("block");
34344             // all splitbars share the same overlay
34345             Roo.bootstrap.SplitBar.prototype.overlay = o;
34346         }
34347         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34348         this.overlay.show();
34349         Roo.get(this.proxy).setDisplayed("block");
34350         var size = this.adapter.getElementSize(this);
34351         this.activeMinSize = this.getMinimumSize();;
34352         this.activeMaxSize = this.getMaximumSize();;
34353         var c1 = size - this.activeMinSize;
34354         var c2 = Math.max(this.activeMaxSize - size, 0);
34355         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34356             this.dd.resetConstraints();
34357             this.dd.setXConstraint(
34358                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34359                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34360             );
34361             this.dd.setYConstraint(0, 0);
34362         }else{
34363             this.dd.resetConstraints();
34364             this.dd.setXConstraint(0, 0);
34365             this.dd.setYConstraint(
34366                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34367                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34368             );
34369          }
34370         this.dragSpecs.startSize = size;
34371         this.dragSpecs.startPoint = [x, y];
34372         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34373     },
34374     
34375     /** 
34376      * @private Called after the drag operation by the DDProxy
34377      */
34378     onEndProxyDrag : function(e){
34379         Roo.get(this.proxy).setDisplayed(false);
34380         var endPoint = Roo.lib.Event.getXY(e);
34381         if(this.overlay){
34382             this.overlay.hide();
34383         }
34384         var newSize;
34385         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34386             newSize = this.dragSpecs.startSize + 
34387                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34388                     endPoint[0] - this.dragSpecs.startPoint[0] :
34389                     this.dragSpecs.startPoint[0] - endPoint[0]
34390                 );
34391         }else{
34392             newSize = this.dragSpecs.startSize + 
34393                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34394                     endPoint[1] - this.dragSpecs.startPoint[1] :
34395                     this.dragSpecs.startPoint[1] - endPoint[1]
34396                 );
34397         }
34398         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34399         if(newSize != this.dragSpecs.startSize){
34400             if(this.fireEvent('beforeapply', this, newSize) !== false){
34401                 this.adapter.setElementSize(this, newSize);
34402                 this.fireEvent("moved", this, newSize);
34403                 this.fireEvent("resize", this, newSize);
34404             }
34405         }
34406     },
34407     
34408     /**
34409      * Get the adapter this SplitBar uses
34410      * @return The adapter object
34411      */
34412     getAdapter : function(){
34413         return this.adapter;
34414     },
34415     
34416     /**
34417      * Set the adapter this SplitBar uses
34418      * @param {Object} adapter A SplitBar adapter object
34419      */
34420     setAdapter : function(adapter){
34421         this.adapter = adapter;
34422         this.adapter.init(this);
34423     },
34424     
34425     /**
34426      * Gets the minimum size for the resizing element
34427      * @return {Number} The minimum size
34428      */
34429     getMinimumSize : function(){
34430         return this.minSize;
34431     },
34432     
34433     /**
34434      * Sets the minimum size for the resizing element
34435      * @param {Number} minSize The minimum size
34436      */
34437     setMinimumSize : function(minSize){
34438         this.minSize = minSize;
34439     },
34440     
34441     /**
34442      * Gets the maximum size for the resizing element
34443      * @return {Number} The maximum size
34444      */
34445     getMaximumSize : function(){
34446         return this.maxSize;
34447     },
34448     
34449     /**
34450      * Sets the maximum size for the resizing element
34451      * @param {Number} maxSize The maximum size
34452      */
34453     setMaximumSize : function(maxSize){
34454         this.maxSize = maxSize;
34455     },
34456     
34457     /**
34458      * Sets the initialize size for the resizing element
34459      * @param {Number} size The initial size
34460      */
34461     setCurrentSize : function(size){
34462         var oldAnimate = this.animate;
34463         this.animate = false;
34464         this.adapter.setElementSize(this, size);
34465         this.animate = oldAnimate;
34466     },
34467     
34468     /**
34469      * Destroy this splitbar. 
34470      * @param {Boolean} removeEl True to remove the element
34471      */
34472     destroy : function(removeEl){
34473         if(this.shim){
34474             this.shim.remove();
34475         }
34476         this.dd.unreg();
34477         this.proxy.parentNode.removeChild(this.proxy);
34478         if(removeEl){
34479             this.el.remove();
34480         }
34481     }
34482 });
34483
34484 /**
34485  * @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.
34486  */
34487 Roo.bootstrap.SplitBar.createProxy = function(dir){
34488     var proxy = new Roo.Element(document.createElement("div"));
34489     proxy.unselectable();
34490     var cls = 'roo-splitbar-proxy';
34491     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34492     document.body.appendChild(proxy.dom);
34493     return proxy.dom;
34494 };
34495
34496 /** 
34497  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34498  * Default Adapter. It assumes the splitter and resizing element are not positioned
34499  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34500  */
34501 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34502 };
34503
34504 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34505     // do nothing for now
34506     init : function(s){
34507     
34508     },
34509     /**
34510      * Called before drag operations to get the current size of the resizing element. 
34511      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34512      */
34513      getElementSize : function(s){
34514         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34515             return s.resizingEl.getWidth();
34516         }else{
34517             return s.resizingEl.getHeight();
34518         }
34519     },
34520     
34521     /**
34522      * Called after drag operations to set the size of the resizing element.
34523      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34524      * @param {Number} newSize The new size to set
34525      * @param {Function} onComplete A function to be invoked when resizing is complete
34526      */
34527     setElementSize : function(s, newSize, onComplete){
34528         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34529             if(!s.animate){
34530                 s.resizingEl.setWidth(newSize);
34531                 if(onComplete){
34532                     onComplete(s, newSize);
34533                 }
34534             }else{
34535                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34536             }
34537         }else{
34538             
34539             if(!s.animate){
34540                 s.resizingEl.setHeight(newSize);
34541                 if(onComplete){
34542                     onComplete(s, newSize);
34543                 }
34544             }else{
34545                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34546             }
34547         }
34548     }
34549 };
34550
34551 /** 
34552  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34553  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34554  * Adapter that  moves the splitter element to align with the resized sizing element. 
34555  * Used with an absolute positioned SplitBar.
34556  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34557  * document.body, make sure you assign an id to the body element.
34558  */
34559 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34560     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34561     this.container = Roo.get(container);
34562 };
34563
34564 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34565     init : function(s){
34566         this.basic.init(s);
34567     },
34568     
34569     getElementSize : function(s){
34570         return this.basic.getElementSize(s);
34571     },
34572     
34573     setElementSize : function(s, newSize, onComplete){
34574         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34575     },
34576     
34577     moveSplitter : function(s){
34578         var yes = Roo.bootstrap.SplitBar;
34579         switch(s.placement){
34580             case yes.LEFT:
34581                 s.el.setX(s.resizingEl.getRight());
34582                 break;
34583             case yes.RIGHT:
34584                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34585                 break;
34586             case yes.TOP:
34587                 s.el.setY(s.resizingEl.getBottom());
34588                 break;
34589             case yes.BOTTOM:
34590                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34591                 break;
34592         }
34593     }
34594 };
34595
34596 /**
34597  * Orientation constant - Create a vertical SplitBar
34598  * @static
34599  * @type Number
34600  */
34601 Roo.bootstrap.SplitBar.VERTICAL = 1;
34602
34603 /**
34604  * Orientation constant - Create a horizontal SplitBar
34605  * @static
34606  * @type Number
34607  */
34608 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34609
34610 /**
34611  * Placement constant - The resizing element is to the left of the splitter element
34612  * @static
34613  * @type Number
34614  */
34615 Roo.bootstrap.SplitBar.LEFT = 1;
34616
34617 /**
34618  * Placement constant - The resizing element is to the right of the splitter element
34619  * @static
34620  * @type Number
34621  */
34622 Roo.bootstrap.SplitBar.RIGHT = 2;
34623
34624 /**
34625  * Placement constant - The resizing element is positioned above the splitter element
34626  * @static
34627  * @type Number
34628  */
34629 Roo.bootstrap.SplitBar.TOP = 3;
34630
34631 /**
34632  * Placement constant - The resizing element is positioned under splitter element
34633  * @static
34634  * @type Number
34635  */
34636 Roo.bootstrap.SplitBar.BOTTOM = 4;
34637 Roo.namespace("Roo.bootstrap.layout");/*
34638  * Based on:
34639  * Ext JS Library 1.1.1
34640  * Copyright(c) 2006-2007, Ext JS, LLC.
34641  *
34642  * Originally Released Under LGPL - original licence link has changed is not relivant.
34643  *
34644  * Fork - LGPL
34645  * <script type="text/javascript">
34646  */
34647
34648 /**
34649  * @class Roo.bootstrap.layout.Manager
34650  * @extends Roo.bootstrap.Component
34651  * Base class for layout managers.
34652  */
34653 Roo.bootstrap.layout.Manager = function(config)
34654 {
34655     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34656
34657
34658
34659
34660
34661     /** false to disable window resize monitoring @type Boolean */
34662     this.monitorWindowResize = true;
34663     this.regions = {};
34664     this.addEvents({
34665         /**
34666          * @event layout
34667          * Fires when a layout is performed.
34668          * @param {Roo.LayoutManager} this
34669          */
34670         "layout" : true,
34671         /**
34672          * @event regionresized
34673          * Fires when the user resizes a region.
34674          * @param {Roo.LayoutRegion} region The resized region
34675          * @param {Number} newSize The new size (width for east/west, height for north/south)
34676          */
34677         "regionresized" : true,
34678         /**
34679          * @event regioncollapsed
34680          * Fires when a region is collapsed.
34681          * @param {Roo.LayoutRegion} region The collapsed region
34682          */
34683         "regioncollapsed" : true,
34684         /**
34685          * @event regionexpanded
34686          * Fires when a region is expanded.
34687          * @param {Roo.LayoutRegion} region The expanded region
34688          */
34689         "regionexpanded" : true
34690     });
34691     this.updating = false;
34692
34693     if (config.el) {
34694         this.el = Roo.get(config.el);
34695         this.initEvents();
34696     }
34697
34698 };
34699
34700 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34701
34702
34703     regions : null,
34704
34705     monitorWindowResize : true,
34706
34707
34708     updating : false,
34709
34710
34711     onRender : function(ct, position)
34712     {
34713         if(!this.el){
34714             this.el = Roo.get(ct);
34715             this.initEvents();
34716         }
34717         //this.fireEvent('render',this);
34718     },
34719
34720
34721     initEvents: function()
34722     {
34723
34724
34725         // ie scrollbar fix
34726         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34727             document.body.scroll = "no";
34728         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34729             this.el.position('relative');
34730         }
34731         this.id = this.el.id;
34732         this.el.addClass("roo-layout-container");
34733         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34734         if(this.el.dom != document.body ) {
34735             this.el.on('resize', this.layout,this);
34736             this.el.on('show', this.layout,this);
34737         }
34738
34739     },
34740
34741     /**
34742      * Returns true if this layout is currently being updated
34743      * @return {Boolean}
34744      */
34745     isUpdating : function(){
34746         return this.updating;
34747     },
34748
34749     /**
34750      * Suspend the LayoutManager from doing auto-layouts while
34751      * making multiple add or remove calls
34752      */
34753     beginUpdate : function(){
34754         this.updating = true;
34755     },
34756
34757     /**
34758      * Restore auto-layouts and optionally disable the manager from performing a layout
34759      * @param {Boolean} noLayout true to disable a layout update
34760      */
34761     endUpdate : function(noLayout){
34762         this.updating = false;
34763         if(!noLayout){
34764             this.layout();
34765         }
34766     },
34767
34768     layout: function(){
34769         // abstract...
34770     },
34771
34772     onRegionResized : function(region, newSize){
34773         this.fireEvent("regionresized", region, newSize);
34774         this.layout();
34775     },
34776
34777     onRegionCollapsed : function(region){
34778         this.fireEvent("regioncollapsed", region);
34779     },
34780
34781     onRegionExpanded : function(region){
34782         this.fireEvent("regionexpanded", region);
34783     },
34784
34785     /**
34786      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34787      * performs box-model adjustments.
34788      * @return {Object} The size as an object {width: (the width), height: (the height)}
34789      */
34790     getViewSize : function()
34791     {
34792         var size;
34793         if(this.el.dom != document.body){
34794             size = this.el.getSize();
34795         }else{
34796             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34797         }
34798         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34799         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34800         return size;
34801     },
34802
34803     /**
34804      * Returns the Element this layout is bound to.
34805      * @return {Roo.Element}
34806      */
34807     getEl : function(){
34808         return this.el;
34809     },
34810
34811     /**
34812      * Returns the specified region.
34813      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34814      * @return {Roo.LayoutRegion}
34815      */
34816     getRegion : function(target){
34817         return this.regions[target.toLowerCase()];
34818     },
34819
34820     onWindowResize : function(){
34821         if(this.monitorWindowResize){
34822             this.layout();
34823         }
34824     }
34825 });
34826 /*
34827  * Based on:
34828  * Ext JS Library 1.1.1
34829  * Copyright(c) 2006-2007, Ext JS, LLC.
34830  *
34831  * Originally Released Under LGPL - original licence link has changed is not relivant.
34832  *
34833  * Fork - LGPL
34834  * <script type="text/javascript">
34835  */
34836 /**
34837  * @class Roo.bootstrap.layout.Border
34838  * @extends Roo.bootstrap.layout.Manager
34839  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34840  * please see: examples/bootstrap/nested.html<br><br>
34841  
34842 <b>The container the layout is rendered into can be either the body element or any other element.
34843 If it is not the body element, the container needs to either be an absolute positioned element,
34844 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34845 the container size if it is not the body element.</b>
34846
34847 * @constructor
34848 * Create a new Border
34849 * @param {Object} config Configuration options
34850  */
34851 Roo.bootstrap.layout.Border = function(config){
34852     config = config || {};
34853     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34854     
34855     
34856     
34857     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34858         if(config[region]){
34859             config[region].region = region;
34860             this.addRegion(config[region]);
34861         }
34862     },this);
34863     
34864 };
34865
34866 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34867
34868 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34869     /**
34870      * Creates and adds a new region if it doesn't already exist.
34871      * @param {String} target The target region key (north, south, east, west or center).
34872      * @param {Object} config The regions config object
34873      * @return {BorderLayoutRegion} The new region
34874      */
34875     addRegion : function(config)
34876     {
34877         if(!this.regions[config.region]){
34878             var r = this.factory(config);
34879             this.bindRegion(r);
34880         }
34881         return this.regions[config.region];
34882     },
34883
34884     // private (kinda)
34885     bindRegion : function(r){
34886         this.regions[r.config.region] = r;
34887         
34888         r.on("visibilitychange",    this.layout, this);
34889         r.on("paneladded",          this.layout, this);
34890         r.on("panelremoved",        this.layout, this);
34891         r.on("invalidated",         this.layout, this);
34892         r.on("resized",             this.onRegionResized, this);
34893         r.on("collapsed",           this.onRegionCollapsed, this);
34894         r.on("expanded",            this.onRegionExpanded, this);
34895     },
34896
34897     /**
34898      * Performs a layout update.
34899      */
34900     layout : function()
34901     {
34902         if(this.updating) {
34903             return;
34904         }
34905         
34906         // render all the rebions if they have not been done alreayd?
34907         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34908             if(this.regions[region] && !this.regions[region].bodyEl){
34909                 this.regions[region].onRender(this.el)
34910             }
34911         },this);
34912         
34913         var size = this.getViewSize();
34914         var w = size.width;
34915         var h = size.height;
34916         var centerW = w;
34917         var centerH = h;
34918         var centerY = 0;
34919         var centerX = 0;
34920         //var x = 0, y = 0;
34921
34922         var rs = this.regions;
34923         var north = rs["north"];
34924         var south = rs["south"]; 
34925         var west = rs["west"];
34926         var east = rs["east"];
34927         var center = rs["center"];
34928         //if(this.hideOnLayout){ // not supported anymore
34929             //c.el.setStyle("display", "none");
34930         //}
34931         if(north && north.isVisible()){
34932             var b = north.getBox();
34933             var m = north.getMargins();
34934             b.width = w - (m.left+m.right);
34935             b.x = m.left;
34936             b.y = m.top;
34937             centerY = b.height + b.y + m.bottom;
34938             centerH -= centerY;
34939             north.updateBox(this.safeBox(b));
34940         }
34941         if(south && south.isVisible()){
34942             var b = south.getBox();
34943             var m = south.getMargins();
34944             b.width = w - (m.left+m.right);
34945             b.x = m.left;
34946             var totalHeight = (b.height + m.top + m.bottom);
34947             b.y = h - totalHeight + m.top;
34948             centerH -= totalHeight;
34949             south.updateBox(this.safeBox(b));
34950         }
34951         if(west && west.isVisible()){
34952             var b = west.getBox();
34953             var m = west.getMargins();
34954             b.height = centerH - (m.top+m.bottom);
34955             b.x = m.left;
34956             b.y = centerY + m.top;
34957             var totalWidth = (b.width + m.left + m.right);
34958             centerX += totalWidth;
34959             centerW -= totalWidth;
34960             west.updateBox(this.safeBox(b));
34961         }
34962         if(east && east.isVisible()){
34963             var b = east.getBox();
34964             var m = east.getMargins();
34965             b.height = centerH - (m.top+m.bottom);
34966             var totalWidth = (b.width + m.left + m.right);
34967             b.x = w - totalWidth + m.left;
34968             b.y = centerY + m.top;
34969             centerW -= totalWidth;
34970             east.updateBox(this.safeBox(b));
34971         }
34972         if(center){
34973             var m = center.getMargins();
34974             var centerBox = {
34975                 x: centerX + m.left,
34976                 y: centerY + m.top,
34977                 width: centerW - (m.left+m.right),
34978                 height: centerH - (m.top+m.bottom)
34979             };
34980             //if(this.hideOnLayout){
34981                 //center.el.setStyle("display", "block");
34982             //}
34983             center.updateBox(this.safeBox(centerBox));
34984         }
34985         this.el.repaint();
34986         this.fireEvent("layout", this);
34987     },
34988
34989     // private
34990     safeBox : function(box){
34991         box.width = Math.max(0, box.width);
34992         box.height = Math.max(0, box.height);
34993         return box;
34994     },
34995
34996     /**
34997      * Adds a ContentPanel (or subclass) to this layout.
34998      * @param {String} target The target region key (north, south, east, west or center).
34999      * @param {Roo.ContentPanel} panel The panel to add
35000      * @return {Roo.ContentPanel} The added panel
35001      */
35002     add : function(target, panel){
35003          
35004         target = target.toLowerCase();
35005         return this.regions[target].add(panel);
35006     },
35007
35008     /**
35009      * Remove a ContentPanel (or subclass) to this layout.
35010      * @param {String} target The target region key (north, south, east, west or center).
35011      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35012      * @return {Roo.ContentPanel} The removed panel
35013      */
35014     remove : function(target, panel){
35015         target = target.toLowerCase();
35016         return this.regions[target].remove(panel);
35017     },
35018
35019     /**
35020      * Searches all regions for a panel with the specified id
35021      * @param {String} panelId
35022      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35023      */
35024     findPanel : function(panelId){
35025         var rs = this.regions;
35026         for(var target in rs){
35027             if(typeof rs[target] != "function"){
35028                 var p = rs[target].getPanel(panelId);
35029                 if(p){
35030                     return p;
35031                 }
35032             }
35033         }
35034         return null;
35035     },
35036
35037     /**
35038      * Searches all regions for a panel with the specified id and activates (shows) it.
35039      * @param {String/ContentPanel} panelId The panels id or the panel itself
35040      * @return {Roo.ContentPanel} The shown panel or null
35041      */
35042     showPanel : function(panelId) {
35043       var rs = this.regions;
35044       for(var target in rs){
35045          var r = rs[target];
35046          if(typeof r != "function"){
35047             if(r.hasPanel(panelId)){
35048                return r.showPanel(panelId);
35049             }
35050          }
35051       }
35052       return null;
35053    },
35054
35055    /**
35056      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35057      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35058      */
35059    /*
35060     restoreState : function(provider){
35061         if(!provider){
35062             provider = Roo.state.Manager;
35063         }
35064         var sm = new Roo.LayoutStateManager();
35065         sm.init(this, provider);
35066     },
35067 */
35068  
35069  
35070     /**
35071      * Adds a xtype elements to the layout.
35072      * <pre><code>
35073
35074 layout.addxtype({
35075        xtype : 'ContentPanel',
35076        region: 'west',
35077        items: [ .... ]
35078    }
35079 );
35080
35081 layout.addxtype({
35082         xtype : 'NestedLayoutPanel',
35083         region: 'west',
35084         layout: {
35085            center: { },
35086            west: { }   
35087         },
35088         items : [ ... list of content panels or nested layout panels.. ]
35089    }
35090 );
35091 </code></pre>
35092      * @param {Object} cfg Xtype definition of item to add.
35093      */
35094     addxtype : function(cfg)
35095     {
35096         // basically accepts a pannel...
35097         // can accept a layout region..!?!?
35098         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35099         
35100         
35101         // theory?  children can only be panels??
35102         
35103         //if (!cfg.xtype.match(/Panel$/)) {
35104         //    return false;
35105         //}
35106         var ret = false;
35107         
35108         if (typeof(cfg.region) == 'undefined') {
35109             Roo.log("Failed to add Panel, region was not set");
35110             Roo.log(cfg);
35111             return false;
35112         }
35113         var region = cfg.region;
35114         delete cfg.region;
35115         
35116           
35117         var xitems = [];
35118         if (cfg.items) {
35119             xitems = cfg.items;
35120             delete cfg.items;
35121         }
35122         var nb = false;
35123         
35124         switch(cfg.xtype) 
35125         {
35126             case 'Content':  // ContentPanel (el, cfg)
35127             case 'Scroll':  // ContentPanel (el, cfg)
35128             case 'View': 
35129                 cfg.autoCreate = true;
35130                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35131                 //} else {
35132                 //    var el = this.el.createChild();
35133                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35134                 //}
35135                 
35136                 this.add(region, ret);
35137                 break;
35138             
35139             /*
35140             case 'TreePanel': // our new panel!
35141                 cfg.el = this.el.createChild();
35142                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35143                 this.add(region, ret);
35144                 break;
35145             */
35146             
35147             case 'Nest': 
35148                 // create a new Layout (which is  a Border Layout...
35149                 
35150                 var clayout = cfg.layout;
35151                 clayout.el  = this.el.createChild();
35152                 clayout.items   = clayout.items  || [];
35153                 
35154                 delete cfg.layout;
35155                 
35156                 // replace this exitems with the clayout ones..
35157                 xitems = clayout.items;
35158                  
35159                 // force background off if it's in center...
35160                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35161                     cfg.background = false;
35162                 }
35163                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35164                 
35165                 
35166                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35167                 //console.log('adding nested layout panel '  + cfg.toSource());
35168                 this.add(region, ret);
35169                 nb = {}; /// find first...
35170                 break;
35171             
35172             case 'Grid':
35173                 
35174                 // needs grid and region
35175                 
35176                 //var el = this.getRegion(region).el.createChild();
35177                 /*
35178                  *var el = this.el.createChild();
35179                 // create the grid first...
35180                 cfg.grid.container = el;
35181                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35182                 */
35183                 
35184                 if (region == 'center' && this.active ) {
35185                     cfg.background = false;
35186                 }
35187                 
35188                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35189                 
35190                 this.add(region, ret);
35191                 /*
35192                 if (cfg.background) {
35193                     // render grid on panel activation (if panel background)
35194                     ret.on('activate', function(gp) {
35195                         if (!gp.grid.rendered) {
35196                     //        gp.grid.render(el);
35197                         }
35198                     });
35199                 } else {
35200                   //  cfg.grid.render(el);
35201                 }
35202                 */
35203                 break;
35204            
35205            
35206             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35207                 // it was the old xcomponent building that caused this before.
35208                 // espeically if border is the top element in the tree.
35209                 ret = this;
35210                 break; 
35211                 
35212                     
35213                 
35214                 
35215                 
35216             default:
35217                 /*
35218                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35219                     
35220                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35221                     this.add(region, ret);
35222                 } else {
35223                 */
35224                     Roo.log(cfg);
35225                     throw "Can not add '" + cfg.xtype + "' to Border";
35226                     return null;
35227              
35228                                 
35229              
35230         }
35231         this.beginUpdate();
35232         // add children..
35233         var region = '';
35234         var abn = {};
35235         Roo.each(xitems, function(i)  {
35236             region = nb && i.region ? i.region : false;
35237             
35238             var add = ret.addxtype(i);
35239            
35240             if (region) {
35241                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35242                 if (!i.background) {
35243                     abn[region] = nb[region] ;
35244                 }
35245             }
35246             
35247         });
35248         this.endUpdate();
35249
35250         // make the last non-background panel active..
35251         //if (nb) { Roo.log(abn); }
35252         if (nb) {
35253             
35254             for(var r in abn) {
35255                 region = this.getRegion(r);
35256                 if (region) {
35257                     // tried using nb[r], but it does not work..
35258                      
35259                     region.showPanel(abn[r]);
35260                    
35261                 }
35262             }
35263         }
35264         return ret;
35265         
35266     },
35267     
35268     
35269 // private
35270     factory : function(cfg)
35271     {
35272         
35273         var validRegions = Roo.bootstrap.layout.Border.regions;
35274
35275         var target = cfg.region;
35276         cfg.mgr = this;
35277         
35278         var r = Roo.bootstrap.layout;
35279         Roo.log(target);
35280         switch(target){
35281             case "north":
35282                 return new r.North(cfg);
35283             case "south":
35284                 return new r.South(cfg);
35285             case "east":
35286                 return new r.East(cfg);
35287             case "west":
35288                 return new r.West(cfg);
35289             case "center":
35290                 return new r.Center(cfg);
35291         }
35292         throw 'Layout region "'+target+'" not supported.';
35293     }
35294     
35295     
35296 });
35297  /*
35298  * Based on:
35299  * Ext JS Library 1.1.1
35300  * Copyright(c) 2006-2007, Ext JS, LLC.
35301  *
35302  * Originally Released Under LGPL - original licence link has changed is not relivant.
35303  *
35304  * Fork - LGPL
35305  * <script type="text/javascript">
35306  */
35307  
35308 /**
35309  * @class Roo.bootstrap.layout.Basic
35310  * @extends Roo.util.Observable
35311  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35312  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35313  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35314  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35315  * @cfg {string}   region  the region that it inhabits..
35316  * @cfg {bool}   skipConfig skip config?
35317  * 
35318
35319  */
35320 Roo.bootstrap.layout.Basic = function(config){
35321     
35322     this.mgr = config.mgr;
35323     
35324     this.position = config.region;
35325     
35326     var skipConfig = config.skipConfig;
35327     
35328     this.events = {
35329         /**
35330          * @scope Roo.BasicLayoutRegion
35331          */
35332         
35333         /**
35334          * @event beforeremove
35335          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35336          * @param {Roo.LayoutRegion} this
35337          * @param {Roo.ContentPanel} panel The panel
35338          * @param {Object} e The cancel event object
35339          */
35340         "beforeremove" : true,
35341         /**
35342          * @event invalidated
35343          * Fires when the layout for this region is changed.
35344          * @param {Roo.LayoutRegion} this
35345          */
35346         "invalidated" : true,
35347         /**
35348          * @event visibilitychange
35349          * Fires when this region is shown or hidden 
35350          * @param {Roo.LayoutRegion} this
35351          * @param {Boolean} visibility true or false
35352          */
35353         "visibilitychange" : true,
35354         /**
35355          * @event paneladded
35356          * Fires when a panel is added. 
35357          * @param {Roo.LayoutRegion} this
35358          * @param {Roo.ContentPanel} panel The panel
35359          */
35360         "paneladded" : true,
35361         /**
35362          * @event panelremoved
35363          * Fires when a panel is removed. 
35364          * @param {Roo.LayoutRegion} this
35365          * @param {Roo.ContentPanel} panel The panel
35366          */
35367         "panelremoved" : true,
35368         /**
35369          * @event beforecollapse
35370          * Fires when this region before collapse.
35371          * @param {Roo.LayoutRegion} this
35372          */
35373         "beforecollapse" : true,
35374         /**
35375          * @event collapsed
35376          * Fires when this region is collapsed.
35377          * @param {Roo.LayoutRegion} this
35378          */
35379         "collapsed" : true,
35380         /**
35381          * @event expanded
35382          * Fires when this region is expanded.
35383          * @param {Roo.LayoutRegion} this
35384          */
35385         "expanded" : true,
35386         /**
35387          * @event slideshow
35388          * Fires when this region is slid into view.
35389          * @param {Roo.LayoutRegion} this
35390          */
35391         "slideshow" : true,
35392         /**
35393          * @event slidehide
35394          * Fires when this region slides out of view. 
35395          * @param {Roo.LayoutRegion} this
35396          */
35397         "slidehide" : true,
35398         /**
35399          * @event panelactivated
35400          * Fires when a panel is activated. 
35401          * @param {Roo.LayoutRegion} this
35402          * @param {Roo.ContentPanel} panel The activated panel
35403          */
35404         "panelactivated" : true,
35405         /**
35406          * @event resized
35407          * Fires when the user resizes this region. 
35408          * @param {Roo.LayoutRegion} this
35409          * @param {Number} newSize The new size (width for east/west, height for north/south)
35410          */
35411         "resized" : true
35412     };
35413     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35414     this.panels = new Roo.util.MixedCollection();
35415     this.panels.getKey = this.getPanelId.createDelegate(this);
35416     this.box = null;
35417     this.activePanel = null;
35418     // ensure listeners are added...
35419     
35420     if (config.listeners || config.events) {
35421         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35422             listeners : config.listeners || {},
35423             events : config.events || {}
35424         });
35425     }
35426     
35427     if(skipConfig !== true){
35428         this.applyConfig(config);
35429     }
35430 };
35431
35432 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35433 {
35434     getPanelId : function(p){
35435         return p.getId();
35436     },
35437     
35438     applyConfig : function(config){
35439         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35440         this.config = config;
35441         
35442     },
35443     
35444     /**
35445      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35446      * the width, for horizontal (north, south) the height.
35447      * @param {Number} newSize The new width or height
35448      */
35449     resizeTo : function(newSize){
35450         var el = this.el ? this.el :
35451                  (this.activePanel ? this.activePanel.getEl() : null);
35452         if(el){
35453             switch(this.position){
35454                 case "east":
35455                 case "west":
35456                     el.setWidth(newSize);
35457                     this.fireEvent("resized", this, newSize);
35458                 break;
35459                 case "north":
35460                 case "south":
35461                     el.setHeight(newSize);
35462                     this.fireEvent("resized", this, newSize);
35463                 break;                
35464             }
35465         }
35466     },
35467     
35468     getBox : function(){
35469         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35470     },
35471     
35472     getMargins : function(){
35473         return this.margins;
35474     },
35475     
35476     updateBox : function(box){
35477         this.box = box;
35478         var el = this.activePanel.getEl();
35479         el.dom.style.left = box.x + "px";
35480         el.dom.style.top = box.y + "px";
35481         this.activePanel.setSize(box.width, box.height);
35482     },
35483     
35484     /**
35485      * Returns the container element for this region.
35486      * @return {Roo.Element}
35487      */
35488     getEl : function(){
35489         return this.activePanel;
35490     },
35491     
35492     /**
35493      * Returns true if this region is currently visible.
35494      * @return {Boolean}
35495      */
35496     isVisible : function(){
35497         return this.activePanel ? true : false;
35498     },
35499     
35500     setActivePanel : function(panel){
35501         panel = this.getPanel(panel);
35502         if(this.activePanel && this.activePanel != panel){
35503             this.activePanel.setActiveState(false);
35504             this.activePanel.getEl().setLeftTop(-10000,-10000);
35505         }
35506         this.activePanel = panel;
35507         panel.setActiveState(true);
35508         if(this.box){
35509             panel.setSize(this.box.width, this.box.height);
35510         }
35511         this.fireEvent("panelactivated", this, panel);
35512         this.fireEvent("invalidated");
35513     },
35514     
35515     /**
35516      * Show the specified panel.
35517      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35518      * @return {Roo.ContentPanel} The shown panel or null
35519      */
35520     showPanel : function(panel){
35521         panel = this.getPanel(panel);
35522         if(panel){
35523             this.setActivePanel(panel);
35524         }
35525         return panel;
35526     },
35527     
35528     /**
35529      * Get the active panel for this region.
35530      * @return {Roo.ContentPanel} The active panel or null
35531      */
35532     getActivePanel : function(){
35533         return this.activePanel;
35534     },
35535     
35536     /**
35537      * Add the passed ContentPanel(s)
35538      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35539      * @return {Roo.ContentPanel} The panel added (if only one was added)
35540      */
35541     add : function(panel){
35542         if(arguments.length > 1){
35543             for(var i = 0, len = arguments.length; i < len; i++) {
35544                 this.add(arguments[i]);
35545             }
35546             return null;
35547         }
35548         if(this.hasPanel(panel)){
35549             this.showPanel(panel);
35550             return panel;
35551         }
35552         var el = panel.getEl();
35553         if(el.dom.parentNode != this.mgr.el.dom){
35554             this.mgr.el.dom.appendChild(el.dom);
35555         }
35556         if(panel.setRegion){
35557             panel.setRegion(this);
35558         }
35559         this.panels.add(panel);
35560         el.setStyle("position", "absolute");
35561         if(!panel.background){
35562             this.setActivePanel(panel);
35563             if(this.config.initialSize && this.panels.getCount()==1){
35564                 this.resizeTo(this.config.initialSize);
35565             }
35566         }
35567         this.fireEvent("paneladded", this, panel);
35568         return panel;
35569     },
35570     
35571     /**
35572      * Returns true if the panel is in this region.
35573      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35574      * @return {Boolean}
35575      */
35576     hasPanel : function(panel){
35577         if(typeof panel == "object"){ // must be panel obj
35578             panel = panel.getId();
35579         }
35580         return this.getPanel(panel) ? true : false;
35581     },
35582     
35583     /**
35584      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35585      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35586      * @param {Boolean} preservePanel Overrides the config preservePanel option
35587      * @return {Roo.ContentPanel} The panel that was removed
35588      */
35589     remove : function(panel, preservePanel){
35590         panel = this.getPanel(panel);
35591         if(!panel){
35592             return null;
35593         }
35594         var e = {};
35595         this.fireEvent("beforeremove", this, panel, e);
35596         if(e.cancel === true){
35597             return null;
35598         }
35599         var panelId = panel.getId();
35600         this.panels.removeKey(panelId);
35601         return panel;
35602     },
35603     
35604     /**
35605      * Returns the panel specified or null if it's not in this region.
35606      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35607      * @return {Roo.ContentPanel}
35608      */
35609     getPanel : function(id){
35610         if(typeof id == "object"){ // must be panel obj
35611             return id;
35612         }
35613         return this.panels.get(id);
35614     },
35615     
35616     /**
35617      * Returns this regions position (north/south/east/west/center).
35618      * @return {String} 
35619      */
35620     getPosition: function(){
35621         return this.position;    
35622     }
35623 });/*
35624  * Based on:
35625  * Ext JS Library 1.1.1
35626  * Copyright(c) 2006-2007, Ext JS, LLC.
35627  *
35628  * Originally Released Under LGPL - original licence link has changed is not relivant.
35629  *
35630  * Fork - LGPL
35631  * <script type="text/javascript">
35632  */
35633  
35634 /**
35635  * @class Roo.bootstrap.layout.Region
35636  * @extends Roo.bootstrap.layout.Basic
35637  * This class represents a region in a layout manager.
35638  
35639  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35640  * @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})
35641  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35642  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35643  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35644  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35645  * @cfg {String}    title           The title for the region (overrides panel titles)
35646  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35647  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35648  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35649  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35650  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35651  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35652  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35653  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35654  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35655  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35656
35657  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35658  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35659  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35660  * @cfg {Number}    width           For East/West panels
35661  * @cfg {Number}    height          For North/South panels
35662  * @cfg {Boolean}   split           To show the splitter
35663  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35664  * 
35665  * @cfg {string}   cls             Extra CSS classes to add to region
35666  * 
35667  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35668  * @cfg {string}   region  the region that it inhabits..
35669  *
35670
35671  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35672  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35673
35674  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35675  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35676  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35677  */
35678 Roo.bootstrap.layout.Region = function(config)
35679 {
35680     this.applyConfig(config);
35681
35682     var mgr = config.mgr;
35683     var pos = config.region;
35684     config.skipConfig = true;
35685     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35686     
35687     if (mgr.el) {
35688         this.onRender(mgr.el);   
35689     }
35690      
35691     this.visible = true;
35692     this.collapsed = false;
35693     this.unrendered_panels = [];
35694 };
35695
35696 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35697
35698     position: '', // set by wrapper (eg. north/south etc..)
35699     unrendered_panels : null,  // unrendered panels.
35700     createBody : function(){
35701         /** This region's body element 
35702         * @type Roo.Element */
35703         this.bodyEl = this.el.createChild({
35704                 tag: "div",
35705                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35706         });
35707     },
35708
35709     onRender: function(ctr, pos)
35710     {
35711         var dh = Roo.DomHelper;
35712         /** This region's container element 
35713         * @type Roo.Element */
35714         this.el = dh.append(ctr.dom, {
35715                 tag: "div",
35716                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35717             }, true);
35718         /** This region's title element 
35719         * @type Roo.Element */
35720     
35721         this.titleEl = dh.append(this.el.dom,
35722             {
35723                     tag: "div",
35724                     unselectable: "on",
35725                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35726                     children:[
35727                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35728                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35729                     ]}, true);
35730         
35731         this.titleEl.enableDisplayMode();
35732         /** This region's title text element 
35733         * @type HTMLElement */
35734         this.titleTextEl = this.titleEl.dom.firstChild;
35735         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35736         /*
35737         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35738         this.closeBtn.enableDisplayMode();
35739         this.closeBtn.on("click", this.closeClicked, this);
35740         this.closeBtn.hide();
35741     */
35742         this.createBody(this.config);
35743         if(this.config.hideWhenEmpty){
35744             this.hide();
35745             this.on("paneladded", this.validateVisibility, this);
35746             this.on("panelremoved", this.validateVisibility, this);
35747         }
35748         if(this.autoScroll){
35749             this.bodyEl.setStyle("overflow", "auto");
35750         }else{
35751             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35752         }
35753         //if(c.titlebar !== false){
35754             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35755                 this.titleEl.hide();
35756             }else{
35757                 this.titleEl.show();
35758                 if(this.config.title){
35759                     this.titleTextEl.innerHTML = this.config.title;
35760                 }
35761             }
35762         //}
35763         if(this.config.collapsed){
35764             this.collapse(true);
35765         }
35766         if(this.config.hidden){
35767             this.hide();
35768         }
35769         
35770         if (this.unrendered_panels && this.unrendered_panels.length) {
35771             for (var i =0;i< this.unrendered_panels.length; i++) {
35772                 this.add(this.unrendered_panels[i]);
35773             }
35774             this.unrendered_panels = null;
35775             
35776         }
35777         
35778     },
35779     
35780     applyConfig : function(c)
35781     {
35782         /*
35783          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35784             var dh = Roo.DomHelper;
35785             if(c.titlebar !== false){
35786                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35787                 this.collapseBtn.on("click", this.collapse, this);
35788                 this.collapseBtn.enableDisplayMode();
35789                 /*
35790                 if(c.showPin === true || this.showPin){
35791                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35792                     this.stickBtn.enableDisplayMode();
35793                     this.stickBtn.on("click", this.expand, this);
35794                     this.stickBtn.hide();
35795                 }
35796                 
35797             }
35798             */
35799             /** This region's collapsed element
35800             * @type Roo.Element */
35801             /*
35802              *
35803             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35804                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35805             ]}, true);
35806             
35807             if(c.floatable !== false){
35808                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35809                this.collapsedEl.on("click", this.collapseClick, this);
35810             }
35811
35812             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35813                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35814                    id: "message", unselectable: "on", style:{"float":"left"}});
35815                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35816              }
35817             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35818             this.expandBtn.on("click", this.expand, this);
35819             
35820         }
35821         
35822         if(this.collapseBtn){
35823             this.collapseBtn.setVisible(c.collapsible == true);
35824         }
35825         
35826         this.cmargins = c.cmargins || this.cmargins ||
35827                          (this.position == "west" || this.position == "east" ?
35828                              {top: 0, left: 2, right:2, bottom: 0} :
35829                              {top: 2, left: 0, right:0, bottom: 2});
35830         */
35831         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35832         
35833         
35834         this.bottomTabs = c.tabPosition != "top";
35835         
35836         this.autoScroll = c.autoScroll || false;
35837         
35838         
35839        
35840         
35841         this.duration = c.duration || .30;
35842         this.slideDuration = c.slideDuration || .45;
35843         this.config = c;
35844        
35845     },
35846     /**
35847      * Returns true if this region is currently visible.
35848      * @return {Boolean}
35849      */
35850     isVisible : function(){
35851         return this.visible;
35852     },
35853
35854     /**
35855      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35856      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35857      */
35858     //setCollapsedTitle : function(title){
35859     //    title = title || "&#160;";
35860      //   if(this.collapsedTitleTextEl){
35861       //      this.collapsedTitleTextEl.innerHTML = title;
35862        // }
35863     //},
35864
35865     getBox : function(){
35866         var b;
35867       //  if(!this.collapsed){
35868             b = this.el.getBox(false, true);
35869        // }else{
35870           //  b = this.collapsedEl.getBox(false, true);
35871         //}
35872         return b;
35873     },
35874
35875     getMargins : function(){
35876         return this.margins;
35877         //return this.collapsed ? this.cmargins : this.margins;
35878     },
35879 /*
35880     highlight : function(){
35881         this.el.addClass("x-layout-panel-dragover");
35882     },
35883
35884     unhighlight : function(){
35885         this.el.removeClass("x-layout-panel-dragover");
35886     },
35887 */
35888     updateBox : function(box)
35889     {
35890         if (!this.bodyEl) {
35891             return; // not rendered yet..
35892         }
35893         
35894         this.box = box;
35895         if(!this.collapsed){
35896             this.el.dom.style.left = box.x + "px";
35897             this.el.dom.style.top = box.y + "px";
35898             this.updateBody(box.width, box.height);
35899         }else{
35900             this.collapsedEl.dom.style.left = box.x + "px";
35901             this.collapsedEl.dom.style.top = box.y + "px";
35902             this.collapsedEl.setSize(box.width, box.height);
35903         }
35904         if(this.tabs){
35905             this.tabs.autoSizeTabs();
35906         }
35907     },
35908
35909     updateBody : function(w, h)
35910     {
35911         if(w !== null){
35912             this.el.setWidth(w);
35913             w -= this.el.getBorderWidth("rl");
35914             if(this.config.adjustments){
35915                 w += this.config.adjustments[0];
35916             }
35917         }
35918         if(h !== null && h > 0){
35919             this.el.setHeight(h);
35920             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35921             h -= this.el.getBorderWidth("tb");
35922             if(this.config.adjustments){
35923                 h += this.config.adjustments[1];
35924             }
35925             this.bodyEl.setHeight(h);
35926             if(this.tabs){
35927                 h = this.tabs.syncHeight(h);
35928             }
35929         }
35930         if(this.panelSize){
35931             w = w !== null ? w : this.panelSize.width;
35932             h = h !== null ? h : this.panelSize.height;
35933         }
35934         if(this.activePanel){
35935             var el = this.activePanel.getEl();
35936             w = w !== null ? w : el.getWidth();
35937             h = h !== null ? h : el.getHeight();
35938             this.panelSize = {width: w, height: h};
35939             this.activePanel.setSize(w, h);
35940         }
35941         if(Roo.isIE && this.tabs){
35942             this.tabs.el.repaint();
35943         }
35944     },
35945
35946     /**
35947      * Returns the container element for this region.
35948      * @return {Roo.Element}
35949      */
35950     getEl : function(){
35951         return this.el;
35952     },
35953
35954     /**
35955      * Hides this region.
35956      */
35957     hide : function(){
35958         //if(!this.collapsed){
35959             this.el.dom.style.left = "-2000px";
35960             this.el.hide();
35961         //}else{
35962          //   this.collapsedEl.dom.style.left = "-2000px";
35963          //   this.collapsedEl.hide();
35964        // }
35965         this.visible = false;
35966         this.fireEvent("visibilitychange", this, false);
35967     },
35968
35969     /**
35970      * Shows this region if it was previously hidden.
35971      */
35972     show : function(){
35973         //if(!this.collapsed){
35974             this.el.show();
35975         //}else{
35976         //    this.collapsedEl.show();
35977        // }
35978         this.visible = true;
35979         this.fireEvent("visibilitychange", this, true);
35980     },
35981 /*
35982     closeClicked : function(){
35983         if(this.activePanel){
35984             this.remove(this.activePanel);
35985         }
35986     },
35987
35988     collapseClick : function(e){
35989         if(this.isSlid){
35990            e.stopPropagation();
35991            this.slideIn();
35992         }else{
35993            e.stopPropagation();
35994            this.slideOut();
35995         }
35996     },
35997 */
35998     /**
35999      * Collapses this region.
36000      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36001      */
36002     /*
36003     collapse : function(skipAnim, skipCheck = false){
36004         if(this.collapsed) {
36005             return;
36006         }
36007         
36008         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36009             
36010             this.collapsed = true;
36011             if(this.split){
36012                 this.split.el.hide();
36013             }
36014             if(this.config.animate && skipAnim !== true){
36015                 this.fireEvent("invalidated", this);
36016                 this.animateCollapse();
36017             }else{
36018                 this.el.setLocation(-20000,-20000);
36019                 this.el.hide();
36020                 this.collapsedEl.show();
36021                 this.fireEvent("collapsed", this);
36022                 this.fireEvent("invalidated", this);
36023             }
36024         }
36025         
36026     },
36027 */
36028     animateCollapse : function(){
36029         // overridden
36030     },
36031
36032     /**
36033      * Expands this region if it was previously collapsed.
36034      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36035      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36036      */
36037     /*
36038     expand : function(e, skipAnim){
36039         if(e) {
36040             e.stopPropagation();
36041         }
36042         if(!this.collapsed || this.el.hasActiveFx()) {
36043             return;
36044         }
36045         if(this.isSlid){
36046             this.afterSlideIn();
36047             skipAnim = true;
36048         }
36049         this.collapsed = false;
36050         if(this.config.animate && skipAnim !== true){
36051             this.animateExpand();
36052         }else{
36053             this.el.show();
36054             if(this.split){
36055                 this.split.el.show();
36056             }
36057             this.collapsedEl.setLocation(-2000,-2000);
36058             this.collapsedEl.hide();
36059             this.fireEvent("invalidated", this);
36060             this.fireEvent("expanded", this);
36061         }
36062     },
36063 */
36064     animateExpand : function(){
36065         // overridden
36066     },
36067
36068     initTabs : function()
36069     {
36070         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36071         
36072         var ts = new Roo.bootstrap.panel.Tabs({
36073                 el: this.bodyEl.dom,
36074                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36075                 disableTooltips: this.config.disableTabTips,
36076                 toolbar : this.config.toolbar
36077             });
36078         
36079         if(this.config.hideTabs){
36080             ts.stripWrap.setDisplayed(false);
36081         }
36082         this.tabs = ts;
36083         ts.resizeTabs = this.config.resizeTabs === true;
36084         ts.minTabWidth = this.config.minTabWidth || 40;
36085         ts.maxTabWidth = this.config.maxTabWidth || 250;
36086         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36087         ts.monitorResize = false;
36088         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36089         ts.bodyEl.addClass('roo-layout-tabs-body');
36090         this.panels.each(this.initPanelAsTab, this);
36091     },
36092
36093     initPanelAsTab : function(panel){
36094         var ti = this.tabs.addTab(
36095             panel.getEl().id,
36096             panel.getTitle(),
36097             null,
36098             this.config.closeOnTab && panel.isClosable(),
36099             panel.tpl
36100         );
36101         if(panel.tabTip !== undefined){
36102             ti.setTooltip(panel.tabTip);
36103         }
36104         ti.on("activate", function(){
36105               this.setActivePanel(panel);
36106         }, this);
36107         
36108         if(this.config.closeOnTab){
36109             ti.on("beforeclose", function(t, e){
36110                 e.cancel = true;
36111                 this.remove(panel);
36112             }, this);
36113         }
36114         
36115         panel.tabItem = ti;
36116         
36117         return ti;
36118     },
36119
36120     updatePanelTitle : function(panel, title)
36121     {
36122         if(this.activePanel == panel){
36123             this.updateTitle(title);
36124         }
36125         if(this.tabs){
36126             var ti = this.tabs.getTab(panel.getEl().id);
36127             ti.setText(title);
36128             if(panel.tabTip !== undefined){
36129                 ti.setTooltip(panel.tabTip);
36130             }
36131         }
36132     },
36133
36134     updateTitle : function(title){
36135         if(this.titleTextEl && !this.config.title){
36136             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36137         }
36138     },
36139
36140     setActivePanel : function(panel)
36141     {
36142         panel = this.getPanel(panel);
36143         if(this.activePanel && this.activePanel != panel){
36144             if(this.activePanel.setActiveState(false) === false){
36145                 return;
36146             }
36147         }
36148         this.activePanel = panel;
36149         panel.setActiveState(true);
36150         if(this.panelSize){
36151             panel.setSize(this.panelSize.width, this.panelSize.height);
36152         }
36153         if(this.closeBtn){
36154             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36155         }
36156         this.updateTitle(panel.getTitle());
36157         if(this.tabs){
36158             this.fireEvent("invalidated", this);
36159         }
36160         this.fireEvent("panelactivated", this, panel);
36161     },
36162
36163     /**
36164      * Shows the specified panel.
36165      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36166      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36167      */
36168     showPanel : function(panel)
36169     {
36170         panel = this.getPanel(panel);
36171         if(panel){
36172             if(this.tabs){
36173                 var tab = this.tabs.getTab(panel.getEl().id);
36174                 if(tab.isHidden()){
36175                     this.tabs.unhideTab(tab.id);
36176                 }
36177                 tab.activate();
36178             }else{
36179                 this.setActivePanel(panel);
36180             }
36181         }
36182         return panel;
36183     },
36184
36185     /**
36186      * Get the active panel for this region.
36187      * @return {Roo.ContentPanel} The active panel or null
36188      */
36189     getActivePanel : function(){
36190         return this.activePanel;
36191     },
36192
36193     validateVisibility : function(){
36194         if(this.panels.getCount() < 1){
36195             this.updateTitle("&#160;");
36196             this.closeBtn.hide();
36197             this.hide();
36198         }else{
36199             if(!this.isVisible()){
36200                 this.show();
36201             }
36202         }
36203     },
36204
36205     /**
36206      * Adds the passed ContentPanel(s) to this region.
36207      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36208      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36209      */
36210     add : function(panel)
36211     {
36212         if(arguments.length > 1){
36213             for(var i = 0, len = arguments.length; i < len; i++) {
36214                 this.add(arguments[i]);
36215             }
36216             return null;
36217         }
36218         
36219         // if we have not been rendered yet, then we can not really do much of this..
36220         if (!this.bodyEl) {
36221             this.unrendered_panels.push(panel);
36222             return panel;
36223         }
36224         
36225         
36226         
36227         
36228         if(this.hasPanel(panel)){
36229             this.showPanel(panel);
36230             return panel;
36231         }
36232         panel.setRegion(this);
36233         this.panels.add(panel);
36234        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36235             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36236             // and hide them... ???
36237             this.bodyEl.dom.appendChild(panel.getEl().dom);
36238             if(panel.background !== true){
36239                 this.setActivePanel(panel);
36240             }
36241             this.fireEvent("paneladded", this, panel);
36242             return panel;
36243         }
36244         */
36245         if(!this.tabs){
36246             this.initTabs();
36247         }else{
36248             this.initPanelAsTab(panel);
36249         }
36250         
36251         
36252         if(panel.background !== true){
36253             this.tabs.activate(panel.getEl().id);
36254         }
36255         this.fireEvent("paneladded", this, panel);
36256         return panel;
36257     },
36258
36259     /**
36260      * Hides the tab for the specified panel.
36261      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36262      */
36263     hidePanel : function(panel){
36264         if(this.tabs && (panel = this.getPanel(panel))){
36265             this.tabs.hideTab(panel.getEl().id);
36266         }
36267     },
36268
36269     /**
36270      * Unhides the tab for a previously hidden panel.
36271      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36272      */
36273     unhidePanel : function(panel){
36274         if(this.tabs && (panel = this.getPanel(panel))){
36275             this.tabs.unhideTab(panel.getEl().id);
36276         }
36277     },
36278
36279     clearPanels : function(){
36280         while(this.panels.getCount() > 0){
36281              this.remove(this.panels.first());
36282         }
36283     },
36284
36285     /**
36286      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36287      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36288      * @param {Boolean} preservePanel Overrides the config preservePanel option
36289      * @return {Roo.ContentPanel} The panel that was removed
36290      */
36291     remove : function(panel, preservePanel)
36292     {
36293         panel = this.getPanel(panel);
36294         if(!panel){
36295             return null;
36296         }
36297         var e = {};
36298         this.fireEvent("beforeremove", this, panel, e);
36299         if(e.cancel === true){
36300             return null;
36301         }
36302         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36303         var panelId = panel.getId();
36304         this.panels.removeKey(panelId);
36305         if(preservePanel){
36306             document.body.appendChild(panel.getEl().dom);
36307         }
36308         if(this.tabs){
36309             this.tabs.removeTab(panel.getEl().id);
36310         }else if (!preservePanel){
36311             this.bodyEl.dom.removeChild(panel.getEl().dom);
36312         }
36313         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36314             var p = this.panels.first();
36315             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36316             tempEl.appendChild(p.getEl().dom);
36317             this.bodyEl.update("");
36318             this.bodyEl.dom.appendChild(p.getEl().dom);
36319             tempEl = null;
36320             this.updateTitle(p.getTitle());
36321             this.tabs = null;
36322             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36323             this.setActivePanel(p);
36324         }
36325         panel.setRegion(null);
36326         if(this.activePanel == panel){
36327             this.activePanel = null;
36328         }
36329         if(this.config.autoDestroy !== false && preservePanel !== true){
36330             try{panel.destroy();}catch(e){}
36331         }
36332         this.fireEvent("panelremoved", this, panel);
36333         return panel;
36334     },
36335
36336     /**
36337      * Returns the TabPanel component used by this region
36338      * @return {Roo.TabPanel}
36339      */
36340     getTabs : function(){
36341         return this.tabs;
36342     },
36343
36344     createTool : function(parentEl, className){
36345         var btn = Roo.DomHelper.append(parentEl, {
36346             tag: "div",
36347             cls: "x-layout-tools-button",
36348             children: [ {
36349                 tag: "div",
36350                 cls: "roo-layout-tools-button-inner " + className,
36351                 html: "&#160;"
36352             }]
36353         }, true);
36354         btn.addClassOnOver("roo-layout-tools-button-over");
36355         return btn;
36356     }
36357 });/*
36358  * Based on:
36359  * Ext JS Library 1.1.1
36360  * Copyright(c) 2006-2007, Ext JS, LLC.
36361  *
36362  * Originally Released Under LGPL - original licence link has changed is not relivant.
36363  *
36364  * Fork - LGPL
36365  * <script type="text/javascript">
36366  */
36367  
36368
36369
36370 /**
36371  * @class Roo.SplitLayoutRegion
36372  * @extends Roo.LayoutRegion
36373  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36374  */
36375 Roo.bootstrap.layout.Split = function(config){
36376     this.cursor = config.cursor;
36377     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36378 };
36379
36380 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36381 {
36382     splitTip : "Drag to resize.",
36383     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36384     useSplitTips : false,
36385
36386     applyConfig : function(config){
36387         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36388     },
36389     
36390     onRender : function(ctr,pos) {
36391         
36392         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36393         if(!this.config.split){
36394             return;
36395         }
36396         if(!this.split){
36397             
36398             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36399                             tag: "div",
36400                             id: this.el.id + "-split",
36401                             cls: "roo-layout-split roo-layout-split-"+this.position,
36402                             html: "&#160;"
36403             });
36404             /** The SplitBar for this region 
36405             * @type Roo.SplitBar */
36406             // does not exist yet...
36407             Roo.log([this.position, this.orientation]);
36408             
36409             this.split = new Roo.bootstrap.SplitBar({
36410                 dragElement : splitEl,
36411                 resizingElement: this.el,
36412                 orientation : this.orientation
36413             });
36414             
36415             this.split.on("moved", this.onSplitMove, this);
36416             this.split.useShim = this.config.useShim === true;
36417             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36418             if(this.useSplitTips){
36419                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36420             }
36421             //if(config.collapsible){
36422             //    this.split.el.on("dblclick", this.collapse,  this);
36423             //}
36424         }
36425         if(typeof this.config.minSize != "undefined"){
36426             this.split.minSize = this.config.minSize;
36427         }
36428         if(typeof this.config.maxSize != "undefined"){
36429             this.split.maxSize = this.config.maxSize;
36430         }
36431         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36432             this.hideSplitter();
36433         }
36434         
36435     },
36436
36437     getHMaxSize : function(){
36438          var cmax = this.config.maxSize || 10000;
36439          var center = this.mgr.getRegion("center");
36440          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36441     },
36442
36443     getVMaxSize : function(){
36444          var cmax = this.config.maxSize || 10000;
36445          var center = this.mgr.getRegion("center");
36446          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36447     },
36448
36449     onSplitMove : function(split, newSize){
36450         this.fireEvent("resized", this, newSize);
36451     },
36452     
36453     /** 
36454      * Returns the {@link Roo.SplitBar} for this region.
36455      * @return {Roo.SplitBar}
36456      */
36457     getSplitBar : function(){
36458         return this.split;
36459     },
36460     
36461     hide : function(){
36462         this.hideSplitter();
36463         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36464     },
36465
36466     hideSplitter : function(){
36467         if(this.split){
36468             this.split.el.setLocation(-2000,-2000);
36469             this.split.el.hide();
36470         }
36471     },
36472
36473     show : function(){
36474         if(this.split){
36475             this.split.el.show();
36476         }
36477         Roo.bootstrap.layout.Split.superclass.show.call(this);
36478     },
36479     
36480     beforeSlide: function(){
36481         if(Roo.isGecko){// firefox overflow auto bug workaround
36482             this.bodyEl.clip();
36483             if(this.tabs) {
36484                 this.tabs.bodyEl.clip();
36485             }
36486             if(this.activePanel){
36487                 this.activePanel.getEl().clip();
36488                 
36489                 if(this.activePanel.beforeSlide){
36490                     this.activePanel.beforeSlide();
36491                 }
36492             }
36493         }
36494     },
36495     
36496     afterSlide : function(){
36497         if(Roo.isGecko){// firefox overflow auto bug workaround
36498             this.bodyEl.unclip();
36499             if(this.tabs) {
36500                 this.tabs.bodyEl.unclip();
36501             }
36502             if(this.activePanel){
36503                 this.activePanel.getEl().unclip();
36504                 if(this.activePanel.afterSlide){
36505                     this.activePanel.afterSlide();
36506                 }
36507             }
36508         }
36509     },
36510
36511     initAutoHide : function(){
36512         if(this.autoHide !== false){
36513             if(!this.autoHideHd){
36514                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36515                 this.autoHideHd = {
36516                     "mouseout": function(e){
36517                         if(!e.within(this.el, true)){
36518                             st.delay(500);
36519                         }
36520                     },
36521                     "mouseover" : function(e){
36522                         st.cancel();
36523                     },
36524                     scope : this
36525                 };
36526             }
36527             this.el.on(this.autoHideHd);
36528         }
36529     },
36530
36531     clearAutoHide : function(){
36532         if(this.autoHide !== false){
36533             this.el.un("mouseout", this.autoHideHd.mouseout);
36534             this.el.un("mouseover", this.autoHideHd.mouseover);
36535         }
36536     },
36537
36538     clearMonitor : function(){
36539         Roo.get(document).un("click", this.slideInIf, this);
36540     },
36541
36542     // these names are backwards but not changed for compat
36543     slideOut : function(){
36544         if(this.isSlid || this.el.hasActiveFx()){
36545             return;
36546         }
36547         this.isSlid = true;
36548         if(this.collapseBtn){
36549             this.collapseBtn.hide();
36550         }
36551         this.closeBtnState = this.closeBtn.getStyle('display');
36552         this.closeBtn.hide();
36553         if(this.stickBtn){
36554             this.stickBtn.show();
36555         }
36556         this.el.show();
36557         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36558         this.beforeSlide();
36559         this.el.setStyle("z-index", 10001);
36560         this.el.slideIn(this.getSlideAnchor(), {
36561             callback: function(){
36562                 this.afterSlide();
36563                 this.initAutoHide();
36564                 Roo.get(document).on("click", this.slideInIf, this);
36565                 this.fireEvent("slideshow", this);
36566             },
36567             scope: this,
36568             block: true
36569         });
36570     },
36571
36572     afterSlideIn : function(){
36573         this.clearAutoHide();
36574         this.isSlid = false;
36575         this.clearMonitor();
36576         this.el.setStyle("z-index", "");
36577         if(this.collapseBtn){
36578             this.collapseBtn.show();
36579         }
36580         this.closeBtn.setStyle('display', this.closeBtnState);
36581         if(this.stickBtn){
36582             this.stickBtn.hide();
36583         }
36584         this.fireEvent("slidehide", this);
36585     },
36586
36587     slideIn : function(cb){
36588         if(!this.isSlid || this.el.hasActiveFx()){
36589             Roo.callback(cb);
36590             return;
36591         }
36592         this.isSlid = false;
36593         this.beforeSlide();
36594         this.el.slideOut(this.getSlideAnchor(), {
36595             callback: function(){
36596                 this.el.setLeftTop(-10000, -10000);
36597                 this.afterSlide();
36598                 this.afterSlideIn();
36599                 Roo.callback(cb);
36600             },
36601             scope: this,
36602             block: true
36603         });
36604     },
36605     
36606     slideInIf : function(e){
36607         if(!e.within(this.el)){
36608             this.slideIn();
36609         }
36610     },
36611
36612     animateCollapse : function(){
36613         this.beforeSlide();
36614         this.el.setStyle("z-index", 20000);
36615         var anchor = this.getSlideAnchor();
36616         this.el.slideOut(anchor, {
36617             callback : function(){
36618                 this.el.setStyle("z-index", "");
36619                 this.collapsedEl.slideIn(anchor, {duration:.3});
36620                 this.afterSlide();
36621                 this.el.setLocation(-10000,-10000);
36622                 this.el.hide();
36623                 this.fireEvent("collapsed", this);
36624             },
36625             scope: this,
36626             block: true
36627         });
36628     },
36629
36630     animateExpand : function(){
36631         this.beforeSlide();
36632         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36633         this.el.setStyle("z-index", 20000);
36634         this.collapsedEl.hide({
36635             duration:.1
36636         });
36637         this.el.slideIn(this.getSlideAnchor(), {
36638             callback : function(){
36639                 this.el.setStyle("z-index", "");
36640                 this.afterSlide();
36641                 if(this.split){
36642                     this.split.el.show();
36643                 }
36644                 this.fireEvent("invalidated", this);
36645                 this.fireEvent("expanded", this);
36646             },
36647             scope: this,
36648             block: true
36649         });
36650     },
36651
36652     anchors : {
36653         "west" : "left",
36654         "east" : "right",
36655         "north" : "top",
36656         "south" : "bottom"
36657     },
36658
36659     sanchors : {
36660         "west" : "l",
36661         "east" : "r",
36662         "north" : "t",
36663         "south" : "b"
36664     },
36665
36666     canchors : {
36667         "west" : "tl-tr",
36668         "east" : "tr-tl",
36669         "north" : "tl-bl",
36670         "south" : "bl-tl"
36671     },
36672
36673     getAnchor : function(){
36674         return this.anchors[this.position];
36675     },
36676
36677     getCollapseAnchor : function(){
36678         return this.canchors[this.position];
36679     },
36680
36681     getSlideAnchor : function(){
36682         return this.sanchors[this.position];
36683     },
36684
36685     getAlignAdj : function(){
36686         var cm = this.cmargins;
36687         switch(this.position){
36688             case "west":
36689                 return [0, 0];
36690             break;
36691             case "east":
36692                 return [0, 0];
36693             break;
36694             case "north":
36695                 return [0, 0];
36696             break;
36697             case "south":
36698                 return [0, 0];
36699             break;
36700         }
36701     },
36702
36703     getExpandAdj : function(){
36704         var c = this.collapsedEl, cm = this.cmargins;
36705         switch(this.position){
36706             case "west":
36707                 return [-(cm.right+c.getWidth()+cm.left), 0];
36708             break;
36709             case "east":
36710                 return [cm.right+c.getWidth()+cm.left, 0];
36711             break;
36712             case "north":
36713                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36714             break;
36715             case "south":
36716                 return [0, cm.top+cm.bottom+c.getHeight()];
36717             break;
36718         }
36719     }
36720 });/*
36721  * Based on:
36722  * Ext JS Library 1.1.1
36723  * Copyright(c) 2006-2007, Ext JS, LLC.
36724  *
36725  * Originally Released Under LGPL - original licence link has changed is not relivant.
36726  *
36727  * Fork - LGPL
36728  * <script type="text/javascript">
36729  */
36730 /*
36731  * These classes are private internal classes
36732  */
36733 Roo.bootstrap.layout.Center = function(config){
36734     config.region = "center";
36735     Roo.bootstrap.layout.Region.call(this, config);
36736     this.visible = true;
36737     this.minWidth = config.minWidth || 20;
36738     this.minHeight = config.minHeight || 20;
36739 };
36740
36741 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36742     hide : function(){
36743         // center panel can't be hidden
36744     },
36745     
36746     show : function(){
36747         // center panel can't be hidden
36748     },
36749     
36750     getMinWidth: function(){
36751         return this.minWidth;
36752     },
36753     
36754     getMinHeight: function(){
36755         return this.minHeight;
36756     }
36757 });
36758
36759
36760
36761
36762  
36763
36764
36765
36766
36767
36768 Roo.bootstrap.layout.North = function(config)
36769 {
36770     config.region = 'north';
36771     config.cursor = 'n-resize';
36772     
36773     Roo.bootstrap.layout.Split.call(this, config);
36774     
36775     
36776     if(this.split){
36777         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36778         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36779         this.split.el.addClass("roo-layout-split-v");
36780     }
36781     var size = config.initialSize || config.height;
36782     if(typeof size != "undefined"){
36783         this.el.setHeight(size);
36784     }
36785 };
36786 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36787 {
36788     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36789     
36790     
36791     
36792     getBox : function(){
36793         if(this.collapsed){
36794             return this.collapsedEl.getBox();
36795         }
36796         var box = this.el.getBox();
36797         if(this.split){
36798             box.height += this.split.el.getHeight();
36799         }
36800         return box;
36801     },
36802     
36803     updateBox : function(box){
36804         if(this.split && !this.collapsed){
36805             box.height -= this.split.el.getHeight();
36806             this.split.el.setLeft(box.x);
36807             this.split.el.setTop(box.y+box.height);
36808             this.split.el.setWidth(box.width);
36809         }
36810         if(this.collapsed){
36811             this.updateBody(box.width, null);
36812         }
36813         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36814     }
36815 });
36816
36817
36818
36819
36820
36821 Roo.bootstrap.layout.South = function(config){
36822     config.region = 'south';
36823     config.cursor = 's-resize';
36824     Roo.bootstrap.layout.Split.call(this, config);
36825     if(this.split){
36826         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36827         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36828         this.split.el.addClass("roo-layout-split-v");
36829     }
36830     var size = config.initialSize || config.height;
36831     if(typeof size != "undefined"){
36832         this.el.setHeight(size);
36833     }
36834 };
36835
36836 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36837     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36838     getBox : function(){
36839         if(this.collapsed){
36840             return this.collapsedEl.getBox();
36841         }
36842         var box = this.el.getBox();
36843         if(this.split){
36844             var sh = this.split.el.getHeight();
36845             box.height += sh;
36846             box.y -= sh;
36847         }
36848         return box;
36849     },
36850     
36851     updateBox : function(box){
36852         if(this.split && !this.collapsed){
36853             var sh = this.split.el.getHeight();
36854             box.height -= sh;
36855             box.y += sh;
36856             this.split.el.setLeft(box.x);
36857             this.split.el.setTop(box.y-sh);
36858             this.split.el.setWidth(box.width);
36859         }
36860         if(this.collapsed){
36861             this.updateBody(box.width, null);
36862         }
36863         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36864     }
36865 });
36866
36867 Roo.bootstrap.layout.East = function(config){
36868     config.region = "east";
36869     config.cursor = "e-resize";
36870     Roo.bootstrap.layout.Split.call(this, config);
36871     if(this.split){
36872         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36873         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36874         this.split.el.addClass("roo-layout-split-h");
36875     }
36876     var size = config.initialSize || config.width;
36877     if(typeof size != "undefined"){
36878         this.el.setWidth(size);
36879     }
36880 };
36881 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36882     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36883     getBox : function(){
36884         if(this.collapsed){
36885             return this.collapsedEl.getBox();
36886         }
36887         var box = this.el.getBox();
36888         if(this.split){
36889             var sw = this.split.el.getWidth();
36890             box.width += sw;
36891             box.x -= sw;
36892         }
36893         return box;
36894     },
36895
36896     updateBox : function(box){
36897         if(this.split && !this.collapsed){
36898             var sw = this.split.el.getWidth();
36899             box.width -= sw;
36900             this.split.el.setLeft(box.x);
36901             this.split.el.setTop(box.y);
36902             this.split.el.setHeight(box.height);
36903             box.x += sw;
36904         }
36905         if(this.collapsed){
36906             this.updateBody(null, box.height);
36907         }
36908         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36909     }
36910 });
36911
36912 Roo.bootstrap.layout.West = function(config){
36913     config.region = "west";
36914     config.cursor = "w-resize";
36915     
36916     Roo.bootstrap.layout.Split.call(this, config);
36917     if(this.split){
36918         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36919         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36920         this.split.el.addClass("roo-layout-split-h");
36921     }
36922     
36923 };
36924 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36925     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36926     
36927     onRender: function(ctr, pos)
36928     {
36929         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36930         var size = this.config.initialSize || this.config.width;
36931         if(typeof size != "undefined"){
36932             this.el.setWidth(size);
36933         }
36934     },
36935     
36936     getBox : function(){
36937         if(this.collapsed){
36938             return this.collapsedEl.getBox();
36939         }
36940         var box = this.el.getBox();
36941         if(this.split){
36942             box.width += this.split.el.getWidth();
36943         }
36944         return box;
36945     },
36946     
36947     updateBox : function(box){
36948         if(this.split && !this.collapsed){
36949             var sw = this.split.el.getWidth();
36950             box.width -= sw;
36951             this.split.el.setLeft(box.x+box.width);
36952             this.split.el.setTop(box.y);
36953             this.split.el.setHeight(box.height);
36954         }
36955         if(this.collapsed){
36956             this.updateBody(null, box.height);
36957         }
36958         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36959     }
36960 });
36961 Roo.namespace("Roo.bootstrap.panel");/*
36962  * Based on:
36963  * Ext JS Library 1.1.1
36964  * Copyright(c) 2006-2007, Ext JS, LLC.
36965  *
36966  * Originally Released Under LGPL - original licence link has changed is not relivant.
36967  *
36968  * Fork - LGPL
36969  * <script type="text/javascript">
36970  */
36971 /**
36972  * @class Roo.ContentPanel
36973  * @extends Roo.util.Observable
36974  * A basic ContentPanel element.
36975  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36976  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36977  * @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
36978  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36979  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36980  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36981  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36982  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36983  * @cfg {String} title          The title for this panel
36984  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36985  * @cfg {String} url            Calls {@link #setUrl} with this value
36986  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36987  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36988  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36989  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36990  * @cfg {Boolean} badges render the badges
36991
36992  * @constructor
36993  * Create a new ContentPanel.
36994  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36995  * @param {String/Object} config A string to set only the title or a config object
36996  * @param {String} content (optional) Set the HTML content for this panel
36997  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36998  */
36999 Roo.bootstrap.panel.Content = function( config){
37000     
37001     this.tpl = config.tpl || false;
37002     
37003     var el = config.el;
37004     var content = config.content;
37005
37006     if(config.autoCreate){ // xtype is available if this is called from factory
37007         el = Roo.id();
37008     }
37009     this.el = Roo.get(el);
37010     if(!this.el && config && config.autoCreate){
37011         if(typeof config.autoCreate == "object"){
37012             if(!config.autoCreate.id){
37013                 config.autoCreate.id = config.id||el;
37014             }
37015             this.el = Roo.DomHelper.append(document.body,
37016                         config.autoCreate, true);
37017         }else{
37018             var elcfg =  {   tag: "div",
37019                             cls: "roo-layout-inactive-content",
37020                             id: config.id||el
37021                             };
37022             if (config.html) {
37023                 elcfg.html = config.html;
37024                 
37025             }
37026                         
37027             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37028         }
37029     } 
37030     this.closable = false;
37031     this.loaded = false;
37032     this.active = false;
37033    
37034       
37035     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37036         
37037         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37038         
37039         this.wrapEl = this.el; //this.el.wrap();
37040         var ti = [];
37041         if (config.toolbar.items) {
37042             ti = config.toolbar.items ;
37043             delete config.toolbar.items ;
37044         }
37045         
37046         var nitems = [];
37047         this.toolbar.render(this.wrapEl, 'before');
37048         for(var i =0;i < ti.length;i++) {
37049           //  Roo.log(['add child', items[i]]);
37050             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37051         }
37052         this.toolbar.items = nitems;
37053         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37054         delete config.toolbar;
37055         
37056     }
37057     /*
37058     // xtype created footer. - not sure if will work as we normally have to render first..
37059     if (this.footer && !this.footer.el && this.footer.xtype) {
37060         if (!this.wrapEl) {
37061             this.wrapEl = this.el.wrap();
37062         }
37063     
37064         this.footer.container = this.wrapEl.createChild();
37065          
37066         this.footer = Roo.factory(this.footer, Roo);
37067         
37068     }
37069     */
37070     
37071      if(typeof config == "string"){
37072         this.title = config;
37073     }else{
37074         Roo.apply(this, config);
37075     }
37076     
37077     if(this.resizeEl){
37078         this.resizeEl = Roo.get(this.resizeEl, true);
37079     }else{
37080         this.resizeEl = this.el;
37081     }
37082     // handle view.xtype
37083     
37084  
37085     
37086     
37087     this.addEvents({
37088         /**
37089          * @event activate
37090          * Fires when this panel is activated. 
37091          * @param {Roo.ContentPanel} this
37092          */
37093         "activate" : true,
37094         /**
37095          * @event deactivate
37096          * Fires when this panel is activated. 
37097          * @param {Roo.ContentPanel} this
37098          */
37099         "deactivate" : true,
37100
37101         /**
37102          * @event resize
37103          * Fires when this panel is resized if fitToFrame is true.
37104          * @param {Roo.ContentPanel} this
37105          * @param {Number} width The width after any component adjustments
37106          * @param {Number} height The height after any component adjustments
37107          */
37108         "resize" : true,
37109         
37110          /**
37111          * @event render
37112          * Fires when this tab is created
37113          * @param {Roo.ContentPanel} this
37114          */
37115         "render" : true
37116         
37117         
37118         
37119     });
37120     
37121
37122     
37123     
37124     if(this.autoScroll){
37125         this.resizeEl.setStyle("overflow", "auto");
37126     } else {
37127         // fix randome scrolling
37128         //this.el.on('scroll', function() {
37129         //    Roo.log('fix random scolling');
37130         //    this.scrollTo('top',0); 
37131         //});
37132     }
37133     content = content || this.content;
37134     if(content){
37135         this.setContent(content);
37136     }
37137     if(config && config.url){
37138         this.setUrl(this.url, this.params, this.loadOnce);
37139     }
37140     
37141     
37142     
37143     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37144     
37145     if (this.view && typeof(this.view.xtype) != 'undefined') {
37146         this.view.el = this.el.appendChild(document.createElement("div"));
37147         this.view = Roo.factory(this.view); 
37148         this.view.render  &&  this.view.render(false, '');  
37149     }
37150     
37151     
37152     this.fireEvent('render', this);
37153 };
37154
37155 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37156     
37157     tabTip : '',
37158     
37159     setRegion : function(region){
37160         this.region = region;
37161         this.setActiveClass(region && !this.background);
37162     },
37163     
37164     
37165     setActiveClass: function(state)
37166     {
37167         if(state){
37168            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37169            this.el.setStyle('position','relative');
37170         }else{
37171            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37172            this.el.setStyle('position', 'absolute');
37173         } 
37174     },
37175     
37176     /**
37177      * Returns the toolbar for this Panel if one was configured. 
37178      * @return {Roo.Toolbar} 
37179      */
37180     getToolbar : function(){
37181         return this.toolbar;
37182     },
37183     
37184     setActiveState : function(active)
37185     {
37186         this.active = active;
37187         this.setActiveClass(active);
37188         if(!active){
37189             if(this.fireEvent("deactivate", this) === false){
37190                 return false;
37191             }
37192             return true;
37193         }
37194         this.fireEvent("activate", this);
37195         return true;
37196     },
37197     /**
37198      * Updates this panel's element
37199      * @param {String} content The new content
37200      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37201     */
37202     setContent : function(content, loadScripts){
37203         this.el.update(content, loadScripts);
37204     },
37205
37206     ignoreResize : function(w, h){
37207         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37208             return true;
37209         }else{
37210             this.lastSize = {width: w, height: h};
37211             return false;
37212         }
37213     },
37214     /**
37215      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37216      * @return {Roo.UpdateManager} The UpdateManager
37217      */
37218     getUpdateManager : function(){
37219         return this.el.getUpdateManager();
37220     },
37221      /**
37222      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37223      * @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:
37224 <pre><code>
37225 panel.load({
37226     url: "your-url.php",
37227     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37228     callback: yourFunction,
37229     scope: yourObject, //(optional scope)
37230     discardUrl: false,
37231     nocache: false,
37232     text: "Loading...",
37233     timeout: 30,
37234     scripts: false
37235 });
37236 </code></pre>
37237      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37238      * 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.
37239      * @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}
37240      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37241      * @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.
37242      * @return {Roo.ContentPanel} this
37243      */
37244     load : function(){
37245         var um = this.el.getUpdateManager();
37246         um.update.apply(um, arguments);
37247         return this;
37248     },
37249
37250
37251     /**
37252      * 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.
37253      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37254      * @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)
37255      * @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)
37256      * @return {Roo.UpdateManager} The UpdateManager
37257      */
37258     setUrl : function(url, params, loadOnce){
37259         if(this.refreshDelegate){
37260             this.removeListener("activate", this.refreshDelegate);
37261         }
37262         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37263         this.on("activate", this.refreshDelegate);
37264         return this.el.getUpdateManager();
37265     },
37266     
37267     _handleRefresh : function(url, params, loadOnce){
37268         if(!loadOnce || !this.loaded){
37269             var updater = this.el.getUpdateManager();
37270             updater.update(url, params, this._setLoaded.createDelegate(this));
37271         }
37272     },
37273     
37274     _setLoaded : function(){
37275         this.loaded = true;
37276     }, 
37277     
37278     /**
37279      * Returns this panel's id
37280      * @return {String} 
37281      */
37282     getId : function(){
37283         return this.el.id;
37284     },
37285     
37286     /** 
37287      * Returns this panel's element - used by regiosn to add.
37288      * @return {Roo.Element} 
37289      */
37290     getEl : function(){
37291         return this.wrapEl || this.el;
37292     },
37293     
37294    
37295     
37296     adjustForComponents : function(width, height)
37297     {
37298         //Roo.log('adjustForComponents ');
37299         if(this.resizeEl != this.el){
37300             width -= this.el.getFrameWidth('lr');
37301             height -= this.el.getFrameWidth('tb');
37302         }
37303         if(this.toolbar){
37304             var te = this.toolbar.getEl();
37305             te.setWidth(width);
37306             height -= te.getHeight();
37307         }
37308         if(this.footer){
37309             var te = this.footer.getEl();
37310             te.setWidth(width);
37311             height -= te.getHeight();
37312         }
37313         
37314         
37315         if(this.adjustments){
37316             width += this.adjustments[0];
37317             height += this.adjustments[1];
37318         }
37319         return {"width": width, "height": height};
37320     },
37321     
37322     setSize : function(width, height){
37323         if(this.fitToFrame && !this.ignoreResize(width, height)){
37324             if(this.fitContainer && this.resizeEl != this.el){
37325                 this.el.setSize(width, height);
37326             }
37327             var size = this.adjustForComponents(width, height);
37328             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37329             this.fireEvent('resize', this, size.width, size.height);
37330         }
37331     },
37332     
37333     /**
37334      * Returns this panel's title
37335      * @return {String} 
37336      */
37337     getTitle : function(){
37338         
37339         if (typeof(this.title) != 'object') {
37340             return this.title;
37341         }
37342         
37343         var t = '';
37344         for (var k in this.title) {
37345             if (!this.title.hasOwnProperty(k)) {
37346                 continue;
37347             }
37348             
37349             if (k.indexOf('-') >= 0) {
37350                 var s = k.split('-');
37351                 for (var i = 0; i<s.length; i++) {
37352                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37353                 }
37354             } else {
37355                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37356             }
37357         }
37358         return t;
37359     },
37360     
37361     /**
37362      * Set this panel's title
37363      * @param {String} title
37364      */
37365     setTitle : function(title){
37366         this.title = title;
37367         if(this.region){
37368             this.region.updatePanelTitle(this, title);
37369         }
37370     },
37371     
37372     /**
37373      * Returns true is this panel was configured to be closable
37374      * @return {Boolean} 
37375      */
37376     isClosable : function(){
37377         return this.closable;
37378     },
37379     
37380     beforeSlide : function(){
37381         this.el.clip();
37382         this.resizeEl.clip();
37383     },
37384     
37385     afterSlide : function(){
37386         this.el.unclip();
37387         this.resizeEl.unclip();
37388     },
37389     
37390     /**
37391      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37392      *   Will fail silently if the {@link #setUrl} method has not been called.
37393      *   This does not activate the panel, just updates its content.
37394      */
37395     refresh : function(){
37396         if(this.refreshDelegate){
37397            this.loaded = false;
37398            this.refreshDelegate();
37399         }
37400     },
37401     
37402     /**
37403      * Destroys this panel
37404      */
37405     destroy : function(){
37406         this.el.removeAllListeners();
37407         var tempEl = document.createElement("span");
37408         tempEl.appendChild(this.el.dom);
37409         tempEl.innerHTML = "";
37410         this.el.remove();
37411         this.el = null;
37412     },
37413     
37414     /**
37415      * form - if the content panel contains a form - this is a reference to it.
37416      * @type {Roo.form.Form}
37417      */
37418     form : false,
37419     /**
37420      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37421      *    This contains a reference to it.
37422      * @type {Roo.View}
37423      */
37424     view : false,
37425     
37426       /**
37427      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37428      * <pre><code>
37429
37430 layout.addxtype({
37431        xtype : 'Form',
37432        items: [ .... ]
37433    }
37434 );
37435
37436 </code></pre>
37437      * @param {Object} cfg Xtype definition of item to add.
37438      */
37439     
37440     
37441     getChildContainer: function () {
37442         return this.getEl();
37443     }
37444     
37445     
37446     /*
37447         var  ret = new Roo.factory(cfg);
37448         return ret;
37449         
37450         
37451         // add form..
37452         if (cfg.xtype.match(/^Form$/)) {
37453             
37454             var el;
37455             //if (this.footer) {
37456             //    el = this.footer.container.insertSibling(false, 'before');
37457             //} else {
37458                 el = this.el.createChild();
37459             //}
37460
37461             this.form = new  Roo.form.Form(cfg);
37462             
37463             
37464             if ( this.form.allItems.length) {
37465                 this.form.render(el.dom);
37466             }
37467             return this.form;
37468         }
37469         // should only have one of theses..
37470         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37471             // views.. should not be just added - used named prop 'view''
37472             
37473             cfg.el = this.el.appendChild(document.createElement("div"));
37474             // factory?
37475             
37476             var ret = new Roo.factory(cfg);
37477              
37478              ret.render && ret.render(false, ''); // render blank..
37479             this.view = ret;
37480             return ret;
37481         }
37482         return false;
37483     }
37484     \*/
37485 });
37486  
37487 /**
37488  * @class Roo.bootstrap.panel.Grid
37489  * @extends Roo.bootstrap.panel.Content
37490  * @constructor
37491  * Create a new GridPanel.
37492  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37493  * @param {Object} config A the config object
37494   
37495  */
37496
37497
37498
37499 Roo.bootstrap.panel.Grid = function(config)
37500 {
37501     
37502       
37503     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37504         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37505
37506     config.el = this.wrapper;
37507     //this.el = this.wrapper;
37508     
37509       if (config.container) {
37510         // ctor'ed from a Border/panel.grid
37511         
37512         
37513         this.wrapper.setStyle("overflow", "hidden");
37514         this.wrapper.addClass('roo-grid-container');
37515
37516     }
37517     
37518     
37519     if(config.toolbar){
37520         var tool_el = this.wrapper.createChild();    
37521         this.toolbar = Roo.factory(config.toolbar);
37522         var ti = [];
37523         if (config.toolbar.items) {
37524             ti = config.toolbar.items ;
37525             delete config.toolbar.items ;
37526         }
37527         
37528         var nitems = [];
37529         this.toolbar.render(tool_el);
37530         for(var i =0;i < ti.length;i++) {
37531           //  Roo.log(['add child', items[i]]);
37532             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37533         }
37534         this.toolbar.items = nitems;
37535         
37536         delete config.toolbar;
37537     }
37538     
37539     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37540     config.grid.scrollBody = true;;
37541     config.grid.monitorWindowResize = false; // turn off autosizing
37542     config.grid.autoHeight = false;
37543     config.grid.autoWidth = false;
37544     
37545     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37546     
37547     if (config.background) {
37548         // render grid on panel activation (if panel background)
37549         this.on('activate', function(gp) {
37550             if (!gp.grid.rendered) {
37551                 gp.grid.render(this.wrapper);
37552                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37553             }
37554         });
37555             
37556     } else {
37557         this.grid.render(this.wrapper);
37558         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37559
37560     }
37561     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37562     // ??? needed ??? config.el = this.wrapper;
37563     
37564     
37565     
37566   
37567     // xtype created footer. - not sure if will work as we normally have to render first..
37568     if (this.footer && !this.footer.el && this.footer.xtype) {
37569         
37570         var ctr = this.grid.getView().getFooterPanel(true);
37571         this.footer.dataSource = this.grid.dataSource;
37572         this.footer = Roo.factory(this.footer, Roo);
37573         this.footer.render(ctr);
37574         
37575     }
37576     
37577     
37578     
37579     
37580      
37581 };
37582
37583 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37584     getId : function(){
37585         return this.grid.id;
37586     },
37587     
37588     /**
37589      * Returns the grid for this panel
37590      * @return {Roo.bootstrap.Table} 
37591      */
37592     getGrid : function(){
37593         return this.grid;    
37594     },
37595     
37596     setSize : function(width, height){
37597         if(!this.ignoreResize(width, height)){
37598             var grid = this.grid;
37599             var size = this.adjustForComponents(width, height);
37600             var gridel = grid.getGridEl();
37601             gridel.setSize(size.width, size.height);
37602             /*
37603             var thd = grid.getGridEl().select('thead',true).first();
37604             var tbd = grid.getGridEl().select('tbody', true).first();
37605             if (tbd) {
37606                 tbd.setSize(width, height - thd.getHeight());
37607             }
37608             */
37609             grid.autoSize();
37610         }
37611     },
37612      
37613     
37614     
37615     beforeSlide : function(){
37616         this.grid.getView().scroller.clip();
37617     },
37618     
37619     afterSlide : function(){
37620         this.grid.getView().scroller.unclip();
37621     },
37622     
37623     destroy : function(){
37624         this.grid.destroy();
37625         delete this.grid;
37626         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37627     }
37628 });
37629
37630 /**
37631  * @class Roo.bootstrap.panel.Nest
37632  * @extends Roo.bootstrap.panel.Content
37633  * @constructor
37634  * Create a new Panel, that can contain a layout.Border.
37635  * 
37636  * 
37637  * @param {Roo.BorderLayout} layout The layout for this panel
37638  * @param {String/Object} config A string to set only the title or a config object
37639  */
37640 Roo.bootstrap.panel.Nest = function(config)
37641 {
37642     // construct with only one argument..
37643     /* FIXME - implement nicer consturctors
37644     if (layout.layout) {
37645         config = layout;
37646         layout = config.layout;
37647         delete config.layout;
37648     }
37649     if (layout.xtype && !layout.getEl) {
37650         // then layout needs constructing..
37651         layout = Roo.factory(layout, Roo);
37652     }
37653     */
37654     
37655     config.el =  config.layout.getEl();
37656     
37657     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37658     
37659     config.layout.monitorWindowResize = false; // turn off autosizing
37660     this.layout = config.layout;
37661     this.layout.getEl().addClass("roo-layout-nested-layout");
37662     
37663     
37664     
37665     
37666 };
37667
37668 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37669
37670     setSize : function(width, height){
37671         if(!this.ignoreResize(width, height)){
37672             var size = this.adjustForComponents(width, height);
37673             var el = this.layout.getEl();
37674             if (size.height < 1) {
37675                 el.setWidth(size.width);   
37676             } else {
37677                 el.setSize(size.width, size.height);
37678             }
37679             var touch = el.dom.offsetWidth;
37680             this.layout.layout();
37681             // ie requires a double layout on the first pass
37682             if(Roo.isIE && !this.initialized){
37683                 this.initialized = true;
37684                 this.layout.layout();
37685             }
37686         }
37687     },
37688     
37689     // activate all subpanels if not currently active..
37690     
37691     setActiveState : function(active){
37692         this.active = active;
37693         this.setActiveClass(active);
37694         
37695         if(!active){
37696             this.fireEvent("deactivate", this);
37697             return;
37698         }
37699         
37700         this.fireEvent("activate", this);
37701         // not sure if this should happen before or after..
37702         if (!this.layout) {
37703             return; // should not happen..
37704         }
37705         var reg = false;
37706         for (var r in this.layout.regions) {
37707             reg = this.layout.getRegion(r);
37708             if (reg.getActivePanel()) {
37709                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37710                 reg.setActivePanel(reg.getActivePanel());
37711                 continue;
37712             }
37713             if (!reg.panels.length) {
37714                 continue;
37715             }
37716             reg.showPanel(reg.getPanel(0));
37717         }
37718         
37719         
37720         
37721         
37722     },
37723     
37724     /**
37725      * Returns the nested BorderLayout for this panel
37726      * @return {Roo.BorderLayout} 
37727      */
37728     getLayout : function(){
37729         return this.layout;
37730     },
37731     
37732      /**
37733      * Adds a xtype elements to the layout of the nested panel
37734      * <pre><code>
37735
37736 panel.addxtype({
37737        xtype : 'ContentPanel',
37738        region: 'west',
37739        items: [ .... ]
37740    }
37741 );
37742
37743 panel.addxtype({
37744         xtype : 'NestedLayoutPanel',
37745         region: 'west',
37746         layout: {
37747            center: { },
37748            west: { }   
37749         },
37750         items : [ ... list of content panels or nested layout panels.. ]
37751    }
37752 );
37753 </code></pre>
37754      * @param {Object} cfg Xtype definition of item to add.
37755      */
37756     addxtype : function(cfg) {
37757         return this.layout.addxtype(cfg);
37758     
37759     }
37760 });        /*
37761  * Based on:
37762  * Ext JS Library 1.1.1
37763  * Copyright(c) 2006-2007, Ext JS, LLC.
37764  *
37765  * Originally Released Under LGPL - original licence link has changed is not relivant.
37766  *
37767  * Fork - LGPL
37768  * <script type="text/javascript">
37769  */
37770 /**
37771  * @class Roo.TabPanel
37772  * @extends Roo.util.Observable
37773  * A lightweight tab container.
37774  * <br><br>
37775  * Usage:
37776  * <pre><code>
37777 // basic tabs 1, built from existing content
37778 var tabs = new Roo.TabPanel("tabs1");
37779 tabs.addTab("script", "View Script");
37780 tabs.addTab("markup", "View Markup");
37781 tabs.activate("script");
37782
37783 // more advanced tabs, built from javascript
37784 var jtabs = new Roo.TabPanel("jtabs");
37785 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37786
37787 // set up the UpdateManager
37788 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37789 var updater = tab2.getUpdateManager();
37790 updater.setDefaultUrl("ajax1.htm");
37791 tab2.on('activate', updater.refresh, updater, true);
37792
37793 // Use setUrl for Ajax loading
37794 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37795 tab3.setUrl("ajax2.htm", null, true);
37796
37797 // Disabled tab
37798 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37799 tab4.disable();
37800
37801 jtabs.activate("jtabs-1");
37802  * </code></pre>
37803  * @constructor
37804  * Create a new TabPanel.
37805  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37806  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37807  */
37808 Roo.bootstrap.panel.Tabs = function(config){
37809     /**
37810     * The container element for this TabPanel.
37811     * @type Roo.Element
37812     */
37813     this.el = Roo.get(config.el);
37814     delete config.el;
37815     if(config){
37816         if(typeof config == "boolean"){
37817             this.tabPosition = config ? "bottom" : "top";
37818         }else{
37819             Roo.apply(this, config);
37820         }
37821     }
37822     
37823     if(this.tabPosition == "bottom"){
37824         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37825         this.el.addClass("roo-tabs-bottom");
37826     }
37827     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37828     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37829     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37830     if(Roo.isIE){
37831         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37832     }
37833     if(this.tabPosition != "bottom"){
37834         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37835          * @type Roo.Element
37836          */
37837         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37838         this.el.addClass("roo-tabs-top");
37839     }
37840     this.items = [];
37841
37842     this.bodyEl.setStyle("position", "relative");
37843
37844     this.active = null;
37845     this.activateDelegate = this.activate.createDelegate(this);
37846
37847     this.addEvents({
37848         /**
37849          * @event tabchange
37850          * Fires when the active tab changes
37851          * @param {Roo.TabPanel} this
37852          * @param {Roo.TabPanelItem} activePanel The new active tab
37853          */
37854         "tabchange": true,
37855         /**
37856          * @event beforetabchange
37857          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37858          * @param {Roo.TabPanel} this
37859          * @param {Object} e Set cancel to true on this object to cancel the tab change
37860          * @param {Roo.TabPanelItem} tab The tab being changed to
37861          */
37862         "beforetabchange" : true
37863     });
37864
37865     Roo.EventManager.onWindowResize(this.onResize, this);
37866     this.cpad = this.el.getPadding("lr");
37867     this.hiddenCount = 0;
37868
37869
37870     // toolbar on the tabbar support...
37871     if (this.toolbar) {
37872         alert("no toolbar support yet");
37873         this.toolbar  = false;
37874         /*
37875         var tcfg = this.toolbar;
37876         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37877         this.toolbar = new Roo.Toolbar(tcfg);
37878         if (Roo.isSafari) {
37879             var tbl = tcfg.container.child('table', true);
37880             tbl.setAttribute('width', '100%');
37881         }
37882         */
37883         
37884     }
37885    
37886
37887
37888     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37889 };
37890
37891 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37892     /*
37893      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37894      */
37895     tabPosition : "top",
37896     /*
37897      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37898      */
37899     currentTabWidth : 0,
37900     /*
37901      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37902      */
37903     minTabWidth : 40,
37904     /*
37905      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37906      */
37907     maxTabWidth : 250,
37908     /*
37909      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37910      */
37911     preferredTabWidth : 175,
37912     /*
37913      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37914      */
37915     resizeTabs : false,
37916     /*
37917      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37918      */
37919     monitorResize : true,
37920     /*
37921      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37922      */
37923     toolbar : false,
37924
37925     /**
37926      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37927      * @param {String} id The id of the div to use <b>or create</b>
37928      * @param {String} text The text for the tab
37929      * @param {String} content (optional) Content to put in the TabPanelItem body
37930      * @param {Boolean} closable (optional) True to create a close icon on the tab
37931      * @return {Roo.TabPanelItem} The created TabPanelItem
37932      */
37933     addTab : function(id, text, content, closable, tpl)
37934     {
37935         var item = new Roo.bootstrap.panel.TabItem({
37936             panel: this,
37937             id : id,
37938             text : text,
37939             closable : closable,
37940             tpl : tpl
37941         });
37942         this.addTabItem(item);
37943         if(content){
37944             item.setContent(content);
37945         }
37946         return item;
37947     },
37948
37949     /**
37950      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37951      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37952      * @return {Roo.TabPanelItem}
37953      */
37954     getTab : function(id){
37955         return this.items[id];
37956     },
37957
37958     /**
37959      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37960      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37961      */
37962     hideTab : function(id){
37963         var t = this.items[id];
37964         if(!t.isHidden()){
37965            t.setHidden(true);
37966            this.hiddenCount++;
37967            this.autoSizeTabs();
37968         }
37969     },
37970
37971     /**
37972      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37973      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37974      */
37975     unhideTab : function(id){
37976         var t = this.items[id];
37977         if(t.isHidden()){
37978            t.setHidden(false);
37979            this.hiddenCount--;
37980            this.autoSizeTabs();
37981         }
37982     },
37983
37984     /**
37985      * Adds an existing {@link Roo.TabPanelItem}.
37986      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37987      */
37988     addTabItem : function(item){
37989         this.items[item.id] = item;
37990         this.items.push(item);
37991       //  if(this.resizeTabs){
37992     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37993   //         this.autoSizeTabs();
37994 //        }else{
37995 //            item.autoSize();
37996        // }
37997     },
37998
37999     /**
38000      * Removes a {@link Roo.TabPanelItem}.
38001      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38002      */
38003     removeTab : function(id){
38004         var items = this.items;
38005         var tab = items[id];
38006         if(!tab) { return; }
38007         var index = items.indexOf(tab);
38008         if(this.active == tab && items.length > 1){
38009             var newTab = this.getNextAvailable(index);
38010             if(newTab) {
38011                 newTab.activate();
38012             }
38013         }
38014         this.stripEl.dom.removeChild(tab.pnode.dom);
38015         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38016             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38017         }
38018         items.splice(index, 1);
38019         delete this.items[tab.id];
38020         tab.fireEvent("close", tab);
38021         tab.purgeListeners();
38022         this.autoSizeTabs();
38023     },
38024
38025     getNextAvailable : function(start){
38026         var items = this.items;
38027         var index = start;
38028         // look for a next tab that will slide over to
38029         // replace the one being removed
38030         while(index < items.length){
38031             var item = items[++index];
38032             if(item && !item.isHidden()){
38033                 return item;
38034             }
38035         }
38036         // if one isn't found select the previous tab (on the left)
38037         index = start;
38038         while(index >= 0){
38039             var item = items[--index];
38040             if(item && !item.isHidden()){
38041                 return item;
38042             }
38043         }
38044         return null;
38045     },
38046
38047     /**
38048      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38049      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38050      */
38051     disableTab : function(id){
38052         var tab = this.items[id];
38053         if(tab && this.active != tab){
38054             tab.disable();
38055         }
38056     },
38057
38058     /**
38059      * Enables a {@link Roo.TabPanelItem} that is disabled.
38060      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38061      */
38062     enableTab : function(id){
38063         var tab = this.items[id];
38064         tab.enable();
38065     },
38066
38067     /**
38068      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38069      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38070      * @return {Roo.TabPanelItem} The TabPanelItem.
38071      */
38072     activate : function(id){
38073         var tab = this.items[id];
38074         if(!tab){
38075             return null;
38076         }
38077         if(tab == this.active || tab.disabled){
38078             return tab;
38079         }
38080         var e = {};
38081         this.fireEvent("beforetabchange", this, e, tab);
38082         if(e.cancel !== true && !tab.disabled){
38083             if(this.active){
38084                 this.active.hide();
38085             }
38086             this.active = this.items[id];
38087             this.active.show();
38088             this.fireEvent("tabchange", this, this.active);
38089         }
38090         return tab;
38091     },
38092
38093     /**
38094      * Gets the active {@link Roo.TabPanelItem}.
38095      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38096      */
38097     getActiveTab : function(){
38098         return this.active;
38099     },
38100
38101     /**
38102      * Updates the tab body element to fit the height of the container element
38103      * for overflow scrolling
38104      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38105      */
38106     syncHeight : function(targetHeight){
38107         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38108         var bm = this.bodyEl.getMargins();
38109         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38110         this.bodyEl.setHeight(newHeight);
38111         return newHeight;
38112     },
38113
38114     onResize : function(){
38115         if(this.monitorResize){
38116             this.autoSizeTabs();
38117         }
38118     },
38119
38120     /**
38121      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38122      */
38123     beginUpdate : function(){
38124         this.updating = true;
38125     },
38126
38127     /**
38128      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38129      */
38130     endUpdate : function(){
38131         this.updating = false;
38132         this.autoSizeTabs();
38133     },
38134
38135     /**
38136      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38137      */
38138     autoSizeTabs : function(){
38139         var count = this.items.length;
38140         var vcount = count - this.hiddenCount;
38141         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38142             return;
38143         }
38144         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38145         var availWidth = Math.floor(w / vcount);
38146         var b = this.stripBody;
38147         if(b.getWidth() > w){
38148             var tabs = this.items;
38149             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38150             if(availWidth < this.minTabWidth){
38151                 /*if(!this.sleft){    // incomplete scrolling code
38152                     this.createScrollButtons();
38153                 }
38154                 this.showScroll();
38155                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38156             }
38157         }else{
38158             if(this.currentTabWidth < this.preferredTabWidth){
38159                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38160             }
38161         }
38162     },
38163
38164     /**
38165      * Returns the number of tabs in this TabPanel.
38166      * @return {Number}
38167      */
38168      getCount : function(){
38169          return this.items.length;
38170      },
38171
38172     /**
38173      * Resizes all the tabs to the passed width
38174      * @param {Number} The new width
38175      */
38176     setTabWidth : function(width){
38177         this.currentTabWidth = width;
38178         for(var i = 0, len = this.items.length; i < len; i++) {
38179                 if(!this.items[i].isHidden()) {
38180                 this.items[i].setWidth(width);
38181             }
38182         }
38183     },
38184
38185     /**
38186      * Destroys this TabPanel
38187      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38188      */
38189     destroy : function(removeEl){
38190         Roo.EventManager.removeResizeListener(this.onResize, this);
38191         for(var i = 0, len = this.items.length; i < len; i++){
38192             this.items[i].purgeListeners();
38193         }
38194         if(removeEl === true){
38195             this.el.update("");
38196             this.el.remove();
38197         }
38198     },
38199     
38200     createStrip : function(container)
38201     {
38202         var strip = document.createElement("nav");
38203         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38204         container.appendChild(strip);
38205         return strip;
38206     },
38207     
38208     createStripList : function(strip)
38209     {
38210         // div wrapper for retard IE
38211         // returns the "tr" element.
38212         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38213         //'<div class="x-tabs-strip-wrap">'+
38214           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38215           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38216         return strip.firstChild; //.firstChild.firstChild.firstChild;
38217     },
38218     createBody : function(container)
38219     {
38220         var body = document.createElement("div");
38221         Roo.id(body, "tab-body");
38222         //Roo.fly(body).addClass("x-tabs-body");
38223         Roo.fly(body).addClass("tab-content");
38224         container.appendChild(body);
38225         return body;
38226     },
38227     createItemBody :function(bodyEl, id){
38228         var body = Roo.getDom(id);
38229         if(!body){
38230             body = document.createElement("div");
38231             body.id = id;
38232         }
38233         //Roo.fly(body).addClass("x-tabs-item-body");
38234         Roo.fly(body).addClass("tab-pane");
38235          bodyEl.insertBefore(body, bodyEl.firstChild);
38236         return body;
38237     },
38238     /** @private */
38239     createStripElements :  function(stripEl, text, closable, tpl)
38240     {
38241         var td = document.createElement("li"); // was td..
38242         
38243         
38244         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38245         
38246         
38247         stripEl.appendChild(td);
38248         /*if(closable){
38249             td.className = "x-tabs-closable";
38250             if(!this.closeTpl){
38251                 this.closeTpl = new Roo.Template(
38252                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38253                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38254                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38255                 );
38256             }
38257             var el = this.closeTpl.overwrite(td, {"text": text});
38258             var close = el.getElementsByTagName("div")[0];
38259             var inner = el.getElementsByTagName("em")[0];
38260             return {"el": el, "close": close, "inner": inner};
38261         } else {
38262         */
38263         // not sure what this is..
38264 //            if(!this.tabTpl){
38265                 //this.tabTpl = new Roo.Template(
38266                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38267                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38268                 //);
38269 //                this.tabTpl = new Roo.Template(
38270 //                   '<a href="#">' +
38271 //                   '<span unselectable="on"' +
38272 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38273 //                            ' >{text}</span></a>'
38274 //                );
38275 //                
38276 //            }
38277
38278
38279             var template = tpl || this.tabTpl || false;
38280             
38281             if(!template){
38282                 
38283                 template = new Roo.Template(
38284                    '<a href="#">' +
38285                    '<span unselectable="on"' +
38286                             (this.disableTooltips ? '' : ' title="{text}"') +
38287                             ' >{text}</span></a>'
38288                 );
38289             }
38290             
38291             switch (typeof(template)) {
38292                 case 'object' :
38293                     break;
38294                 case 'string' :
38295                     template = new Roo.Template(template);
38296                     break;
38297                 default :
38298                     break;
38299             }
38300             
38301             var el = template.overwrite(td, {"text": text});
38302             
38303             var inner = el.getElementsByTagName("span")[0];
38304             
38305             return {"el": el, "inner": inner};
38306             
38307     }
38308         
38309     
38310 });
38311
38312 /**
38313  * @class Roo.TabPanelItem
38314  * @extends Roo.util.Observable
38315  * Represents an individual item (tab plus body) in a TabPanel.
38316  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38317  * @param {String} id The id of this TabPanelItem
38318  * @param {String} text The text for the tab of this TabPanelItem
38319  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38320  */
38321 Roo.bootstrap.panel.TabItem = function(config){
38322     /**
38323      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38324      * @type Roo.TabPanel
38325      */
38326     this.tabPanel = config.panel;
38327     /**
38328      * The id for this TabPanelItem
38329      * @type String
38330      */
38331     this.id = config.id;
38332     /** @private */
38333     this.disabled = false;
38334     /** @private */
38335     this.text = config.text;
38336     /** @private */
38337     this.loaded = false;
38338     this.closable = config.closable;
38339
38340     /**
38341      * The body element for this TabPanelItem.
38342      * @type Roo.Element
38343      */
38344     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38345     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38346     this.bodyEl.setStyle("display", "block");
38347     this.bodyEl.setStyle("zoom", "1");
38348     //this.hideAction();
38349
38350     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38351     /** @private */
38352     this.el = Roo.get(els.el);
38353     this.inner = Roo.get(els.inner, true);
38354     this.textEl = Roo.get(this.el.dom.firstChild, true);
38355     this.pnode = Roo.get(els.el.parentNode, true);
38356 //    this.el.on("mousedown", this.onTabMouseDown, this);
38357     this.el.on("click", this.onTabClick, this);
38358     /** @private */
38359     if(config.closable){
38360         var c = Roo.get(els.close, true);
38361         c.dom.title = this.closeText;
38362         c.addClassOnOver("close-over");
38363         c.on("click", this.closeClick, this);
38364      }
38365
38366     this.addEvents({
38367          /**
38368          * @event activate
38369          * Fires when this tab becomes the active tab.
38370          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38371          * @param {Roo.TabPanelItem} this
38372          */
38373         "activate": true,
38374         /**
38375          * @event beforeclose
38376          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38377          * @param {Roo.TabPanelItem} this
38378          * @param {Object} e Set cancel to true on this object to cancel the close.
38379          */
38380         "beforeclose": true,
38381         /**
38382          * @event close
38383          * Fires when this tab is closed.
38384          * @param {Roo.TabPanelItem} this
38385          */
38386          "close": true,
38387         /**
38388          * @event deactivate
38389          * Fires when this tab is no longer the active tab.
38390          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38391          * @param {Roo.TabPanelItem} this
38392          */
38393          "deactivate" : true
38394     });
38395     this.hidden = false;
38396
38397     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38398 };
38399
38400 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38401            {
38402     purgeListeners : function(){
38403        Roo.util.Observable.prototype.purgeListeners.call(this);
38404        this.el.removeAllListeners();
38405     },
38406     /**
38407      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38408      */
38409     show : function(){
38410         this.pnode.addClass("active");
38411         this.showAction();
38412         if(Roo.isOpera){
38413             this.tabPanel.stripWrap.repaint();
38414         }
38415         this.fireEvent("activate", this.tabPanel, this);
38416     },
38417
38418     /**
38419      * Returns true if this tab is the active tab.
38420      * @return {Boolean}
38421      */
38422     isActive : function(){
38423         return this.tabPanel.getActiveTab() == this;
38424     },
38425
38426     /**
38427      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38428      */
38429     hide : function(){
38430         this.pnode.removeClass("active");
38431         this.hideAction();
38432         this.fireEvent("deactivate", this.tabPanel, this);
38433     },
38434
38435     hideAction : function(){
38436         this.bodyEl.hide();
38437         this.bodyEl.setStyle("position", "absolute");
38438         this.bodyEl.setLeft("-20000px");
38439         this.bodyEl.setTop("-20000px");
38440     },
38441
38442     showAction : function(){
38443         this.bodyEl.setStyle("position", "relative");
38444         this.bodyEl.setTop("");
38445         this.bodyEl.setLeft("");
38446         this.bodyEl.show();
38447     },
38448
38449     /**
38450      * Set the tooltip for the tab.
38451      * @param {String} tooltip The tab's tooltip
38452      */
38453     setTooltip : function(text){
38454         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38455             this.textEl.dom.qtip = text;
38456             this.textEl.dom.removeAttribute('title');
38457         }else{
38458             this.textEl.dom.title = text;
38459         }
38460     },
38461
38462     onTabClick : function(e){
38463         e.preventDefault();
38464         this.tabPanel.activate(this.id);
38465     },
38466
38467     onTabMouseDown : function(e){
38468         e.preventDefault();
38469         this.tabPanel.activate(this.id);
38470     },
38471 /*
38472     getWidth : function(){
38473         return this.inner.getWidth();
38474     },
38475
38476     setWidth : function(width){
38477         var iwidth = width - this.pnode.getPadding("lr");
38478         this.inner.setWidth(iwidth);
38479         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38480         this.pnode.setWidth(width);
38481     },
38482 */
38483     /**
38484      * Show or hide the tab
38485      * @param {Boolean} hidden True to hide or false to show.
38486      */
38487     setHidden : function(hidden){
38488         this.hidden = hidden;
38489         this.pnode.setStyle("display", hidden ? "none" : "");
38490     },
38491
38492     /**
38493      * Returns true if this tab is "hidden"
38494      * @return {Boolean}
38495      */
38496     isHidden : function(){
38497         return this.hidden;
38498     },
38499
38500     /**
38501      * Returns the text for this tab
38502      * @return {String}
38503      */
38504     getText : function(){
38505         return this.text;
38506     },
38507     /*
38508     autoSize : function(){
38509         //this.el.beginMeasure();
38510         this.textEl.setWidth(1);
38511         /*
38512          *  #2804 [new] Tabs in Roojs
38513          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38514          */
38515         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38516         //this.el.endMeasure();
38517     //},
38518
38519     /**
38520      * Sets the text for the tab (Note: this also sets the tooltip text)
38521      * @param {String} text The tab's text and tooltip
38522      */
38523     setText : function(text){
38524         this.text = text;
38525         this.textEl.update(text);
38526         this.setTooltip(text);
38527         //if(!this.tabPanel.resizeTabs){
38528         //    this.autoSize();
38529         //}
38530     },
38531     /**
38532      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38533      */
38534     activate : function(){
38535         this.tabPanel.activate(this.id);
38536     },
38537
38538     /**
38539      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38540      */
38541     disable : function(){
38542         if(this.tabPanel.active != this){
38543             this.disabled = true;
38544             this.pnode.addClass("disabled");
38545         }
38546     },
38547
38548     /**
38549      * Enables this TabPanelItem if it was previously disabled.
38550      */
38551     enable : function(){
38552         this.disabled = false;
38553         this.pnode.removeClass("disabled");
38554     },
38555
38556     /**
38557      * Sets the content for this TabPanelItem.
38558      * @param {String} content The content
38559      * @param {Boolean} loadScripts true to look for and load scripts
38560      */
38561     setContent : function(content, loadScripts){
38562         this.bodyEl.update(content, loadScripts);
38563     },
38564
38565     /**
38566      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38567      * @return {Roo.UpdateManager} The UpdateManager
38568      */
38569     getUpdateManager : function(){
38570         return this.bodyEl.getUpdateManager();
38571     },
38572
38573     /**
38574      * Set a URL to be used to load the content for this TabPanelItem.
38575      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38576      * @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)
38577      * @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)
38578      * @return {Roo.UpdateManager} The UpdateManager
38579      */
38580     setUrl : function(url, params, loadOnce){
38581         if(this.refreshDelegate){
38582             this.un('activate', this.refreshDelegate);
38583         }
38584         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38585         this.on("activate", this.refreshDelegate);
38586         return this.bodyEl.getUpdateManager();
38587     },
38588
38589     /** @private */
38590     _handleRefresh : function(url, params, loadOnce){
38591         if(!loadOnce || !this.loaded){
38592             var updater = this.bodyEl.getUpdateManager();
38593             updater.update(url, params, this._setLoaded.createDelegate(this));
38594         }
38595     },
38596
38597     /**
38598      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38599      *   Will fail silently if the setUrl method has not been called.
38600      *   This does not activate the panel, just updates its content.
38601      */
38602     refresh : function(){
38603         if(this.refreshDelegate){
38604            this.loaded = false;
38605            this.refreshDelegate();
38606         }
38607     },
38608
38609     /** @private */
38610     _setLoaded : function(){
38611         this.loaded = true;
38612     },
38613
38614     /** @private */
38615     closeClick : function(e){
38616         var o = {};
38617         e.stopEvent();
38618         this.fireEvent("beforeclose", this, o);
38619         if(o.cancel !== true){
38620             this.tabPanel.removeTab(this.id);
38621         }
38622     },
38623     /**
38624      * The text displayed in the tooltip for the close icon.
38625      * @type String
38626      */
38627     closeText : "Close this tab"
38628 });
38629 /**
38630 *    This script refer to:
38631 *    Title: International Telephone Input
38632 *    Author: Jack O'Connor
38633 *    Code version:  v12.1.12
38634 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38635 **/
38636
38637 Roo.bootstrap.PhoneInputData = function() {
38638     var d = [
38639       [
38640         "Afghanistan (‫افغانستان‬‎)",
38641         "af",
38642         "93"
38643       ],
38644       [
38645         "Albania (Shqipëri)",
38646         "al",
38647         "355"
38648       ],
38649       [
38650         "Algeria (‫الجزائر‬‎)",
38651         "dz",
38652         "213"
38653       ],
38654       [
38655         "American Samoa",
38656         "as",
38657         "1684"
38658       ],
38659       [
38660         "Andorra",
38661         "ad",
38662         "376"
38663       ],
38664       [
38665         "Angola",
38666         "ao",
38667         "244"
38668       ],
38669       [
38670         "Anguilla",
38671         "ai",
38672         "1264"
38673       ],
38674       [
38675         "Antigua and Barbuda",
38676         "ag",
38677         "1268"
38678       ],
38679       [
38680         "Argentina",
38681         "ar",
38682         "54"
38683       ],
38684       [
38685         "Armenia (Հայաստան)",
38686         "am",
38687         "374"
38688       ],
38689       [
38690         "Aruba",
38691         "aw",
38692         "297"
38693       ],
38694       [
38695         "Australia",
38696         "au",
38697         "61",
38698         0
38699       ],
38700       [
38701         "Austria (Österreich)",
38702         "at",
38703         "43"
38704       ],
38705       [
38706         "Azerbaijan (Azərbaycan)",
38707         "az",
38708         "994"
38709       ],
38710       [
38711         "Bahamas",
38712         "bs",
38713         "1242"
38714       ],
38715       [
38716         "Bahrain (‫البحرين‬‎)",
38717         "bh",
38718         "973"
38719       ],
38720       [
38721         "Bangladesh (বাংলাদেশ)",
38722         "bd",
38723         "880"
38724       ],
38725       [
38726         "Barbados",
38727         "bb",
38728         "1246"
38729       ],
38730       [
38731         "Belarus (Беларусь)",
38732         "by",
38733         "375"
38734       ],
38735       [
38736         "Belgium (België)",
38737         "be",
38738         "32"
38739       ],
38740       [
38741         "Belize",
38742         "bz",
38743         "501"
38744       ],
38745       [
38746         "Benin (Bénin)",
38747         "bj",
38748         "229"
38749       ],
38750       [
38751         "Bermuda",
38752         "bm",
38753         "1441"
38754       ],
38755       [
38756         "Bhutan (འབྲུག)",
38757         "bt",
38758         "975"
38759       ],
38760       [
38761         "Bolivia",
38762         "bo",
38763         "591"
38764       ],
38765       [
38766         "Bosnia and Herzegovina (Босна и Херцеговина)",
38767         "ba",
38768         "387"
38769       ],
38770       [
38771         "Botswana",
38772         "bw",
38773         "267"
38774       ],
38775       [
38776         "Brazil (Brasil)",
38777         "br",
38778         "55"
38779       ],
38780       [
38781         "British Indian Ocean Territory",
38782         "io",
38783         "246"
38784       ],
38785       [
38786         "British Virgin Islands",
38787         "vg",
38788         "1284"
38789       ],
38790       [
38791         "Brunei",
38792         "bn",
38793         "673"
38794       ],
38795       [
38796         "Bulgaria (България)",
38797         "bg",
38798         "359"
38799       ],
38800       [
38801         "Burkina Faso",
38802         "bf",
38803         "226"
38804       ],
38805       [
38806         "Burundi (Uburundi)",
38807         "bi",
38808         "257"
38809       ],
38810       [
38811         "Cambodia (កម្ពុជា)",
38812         "kh",
38813         "855"
38814       ],
38815       [
38816         "Cameroon (Cameroun)",
38817         "cm",
38818         "237"
38819       ],
38820       [
38821         "Canada",
38822         "ca",
38823         "1",
38824         1,
38825         ["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"]
38826       ],
38827       [
38828         "Cape Verde (Kabu Verdi)",
38829         "cv",
38830         "238"
38831       ],
38832       [
38833         "Caribbean Netherlands",
38834         "bq",
38835         "599",
38836         1
38837       ],
38838       [
38839         "Cayman Islands",
38840         "ky",
38841         "1345"
38842       ],
38843       [
38844         "Central African Republic (République centrafricaine)",
38845         "cf",
38846         "236"
38847       ],
38848       [
38849         "Chad (Tchad)",
38850         "td",
38851         "235"
38852       ],
38853       [
38854         "Chile",
38855         "cl",
38856         "56"
38857       ],
38858       [
38859         "China (中国)",
38860         "cn",
38861         "86"
38862       ],
38863       [
38864         "Christmas Island",
38865         "cx",
38866         "61",
38867         2
38868       ],
38869       [
38870         "Cocos (Keeling) Islands",
38871         "cc",
38872         "61",
38873         1
38874       ],
38875       [
38876         "Colombia",
38877         "co",
38878         "57"
38879       ],
38880       [
38881         "Comoros (‫جزر القمر‬‎)",
38882         "km",
38883         "269"
38884       ],
38885       [
38886         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38887         "cd",
38888         "243"
38889       ],
38890       [
38891         "Congo (Republic) (Congo-Brazzaville)",
38892         "cg",
38893         "242"
38894       ],
38895       [
38896         "Cook Islands",
38897         "ck",
38898         "682"
38899       ],
38900       [
38901         "Costa Rica",
38902         "cr",
38903         "506"
38904       ],
38905       [
38906         "Côte d’Ivoire",
38907         "ci",
38908         "225"
38909       ],
38910       [
38911         "Croatia (Hrvatska)",
38912         "hr",
38913         "385"
38914       ],
38915       [
38916         "Cuba",
38917         "cu",
38918         "53"
38919       ],
38920       [
38921         "Curaçao",
38922         "cw",
38923         "599",
38924         0
38925       ],
38926       [
38927         "Cyprus (Κύπρος)",
38928         "cy",
38929         "357"
38930       ],
38931       [
38932         "Czech Republic (Česká republika)",
38933         "cz",
38934         "420"
38935       ],
38936       [
38937         "Denmark (Danmark)",
38938         "dk",
38939         "45"
38940       ],
38941       [
38942         "Djibouti",
38943         "dj",
38944         "253"
38945       ],
38946       [
38947         "Dominica",
38948         "dm",
38949         "1767"
38950       ],
38951       [
38952         "Dominican Republic (República Dominicana)",
38953         "do",
38954         "1",
38955         2,
38956         ["809", "829", "849"]
38957       ],
38958       [
38959         "Ecuador",
38960         "ec",
38961         "593"
38962       ],
38963       [
38964         "Egypt (‫مصر‬‎)",
38965         "eg",
38966         "20"
38967       ],
38968       [
38969         "El Salvador",
38970         "sv",
38971         "503"
38972       ],
38973       [
38974         "Equatorial Guinea (Guinea Ecuatorial)",
38975         "gq",
38976         "240"
38977       ],
38978       [
38979         "Eritrea",
38980         "er",
38981         "291"
38982       ],
38983       [
38984         "Estonia (Eesti)",
38985         "ee",
38986         "372"
38987       ],
38988       [
38989         "Ethiopia",
38990         "et",
38991         "251"
38992       ],
38993       [
38994         "Falkland Islands (Islas Malvinas)",
38995         "fk",
38996         "500"
38997       ],
38998       [
38999         "Faroe Islands (Føroyar)",
39000         "fo",
39001         "298"
39002       ],
39003       [
39004         "Fiji",
39005         "fj",
39006         "679"
39007       ],
39008       [
39009         "Finland (Suomi)",
39010         "fi",
39011         "358",
39012         0
39013       ],
39014       [
39015         "France",
39016         "fr",
39017         "33"
39018       ],
39019       [
39020         "French Guiana (Guyane française)",
39021         "gf",
39022         "594"
39023       ],
39024       [
39025         "French Polynesia (Polynésie française)",
39026         "pf",
39027         "689"
39028       ],
39029       [
39030         "Gabon",
39031         "ga",
39032         "241"
39033       ],
39034       [
39035         "Gambia",
39036         "gm",
39037         "220"
39038       ],
39039       [
39040         "Georgia (საქართველო)",
39041         "ge",
39042         "995"
39043       ],
39044       [
39045         "Germany (Deutschland)",
39046         "de",
39047         "49"
39048       ],
39049       [
39050         "Ghana (Gaana)",
39051         "gh",
39052         "233"
39053       ],
39054       [
39055         "Gibraltar",
39056         "gi",
39057         "350"
39058       ],
39059       [
39060         "Greece (Ελλάδα)",
39061         "gr",
39062         "30"
39063       ],
39064       [
39065         "Greenland (Kalaallit Nunaat)",
39066         "gl",
39067         "299"
39068       ],
39069       [
39070         "Grenada",
39071         "gd",
39072         "1473"
39073       ],
39074       [
39075         "Guadeloupe",
39076         "gp",
39077         "590",
39078         0
39079       ],
39080       [
39081         "Guam",
39082         "gu",
39083         "1671"
39084       ],
39085       [
39086         "Guatemala",
39087         "gt",
39088         "502"
39089       ],
39090       [
39091         "Guernsey",
39092         "gg",
39093         "44",
39094         1
39095       ],
39096       [
39097         "Guinea (Guinée)",
39098         "gn",
39099         "224"
39100       ],
39101       [
39102         "Guinea-Bissau (Guiné Bissau)",
39103         "gw",
39104         "245"
39105       ],
39106       [
39107         "Guyana",
39108         "gy",
39109         "592"
39110       ],
39111       [
39112         "Haiti",
39113         "ht",
39114         "509"
39115       ],
39116       [
39117         "Honduras",
39118         "hn",
39119         "504"
39120       ],
39121       [
39122         "Hong Kong (香港)",
39123         "hk",
39124         "852"
39125       ],
39126       [
39127         "Hungary (Magyarország)",
39128         "hu",
39129         "36"
39130       ],
39131       [
39132         "Iceland (Ísland)",
39133         "is",
39134         "354"
39135       ],
39136       [
39137         "India (भारत)",
39138         "in",
39139         "91"
39140       ],
39141       [
39142         "Indonesia",
39143         "id",
39144         "62"
39145       ],
39146       [
39147         "Iran (‫ایران‬‎)",
39148         "ir",
39149         "98"
39150       ],
39151       [
39152         "Iraq (‫العراق‬‎)",
39153         "iq",
39154         "964"
39155       ],
39156       [
39157         "Ireland",
39158         "ie",
39159         "353"
39160       ],
39161       [
39162         "Isle of Man",
39163         "im",
39164         "44",
39165         2
39166       ],
39167       [
39168         "Israel (‫ישראל‬‎)",
39169         "il",
39170         "972"
39171       ],
39172       [
39173         "Italy (Italia)",
39174         "it",
39175         "39",
39176         0
39177       ],
39178       [
39179         "Jamaica",
39180         "jm",
39181         "1876"
39182       ],
39183       [
39184         "Japan (日本)",
39185         "jp",
39186         "81"
39187       ],
39188       [
39189         "Jersey",
39190         "je",
39191         "44",
39192         3
39193       ],
39194       [
39195         "Jordan (‫الأردن‬‎)",
39196         "jo",
39197         "962"
39198       ],
39199       [
39200         "Kazakhstan (Казахстан)",
39201         "kz",
39202         "7",
39203         1
39204       ],
39205       [
39206         "Kenya",
39207         "ke",
39208         "254"
39209       ],
39210       [
39211         "Kiribati",
39212         "ki",
39213         "686"
39214       ],
39215       [
39216         "Kosovo",
39217         "xk",
39218         "383"
39219       ],
39220       [
39221         "Kuwait (‫الكويت‬‎)",
39222         "kw",
39223         "965"
39224       ],
39225       [
39226         "Kyrgyzstan (Кыргызстан)",
39227         "kg",
39228         "996"
39229       ],
39230       [
39231         "Laos (ລາວ)",
39232         "la",
39233         "856"
39234       ],
39235       [
39236         "Latvia (Latvija)",
39237         "lv",
39238         "371"
39239       ],
39240       [
39241         "Lebanon (‫لبنان‬‎)",
39242         "lb",
39243         "961"
39244       ],
39245       [
39246         "Lesotho",
39247         "ls",
39248         "266"
39249       ],
39250       [
39251         "Liberia",
39252         "lr",
39253         "231"
39254       ],
39255       [
39256         "Libya (‫ليبيا‬‎)",
39257         "ly",
39258         "218"
39259       ],
39260       [
39261         "Liechtenstein",
39262         "li",
39263         "423"
39264       ],
39265       [
39266         "Lithuania (Lietuva)",
39267         "lt",
39268         "370"
39269       ],
39270       [
39271         "Luxembourg",
39272         "lu",
39273         "352"
39274       ],
39275       [
39276         "Macau (澳門)",
39277         "mo",
39278         "853"
39279       ],
39280       [
39281         "Macedonia (FYROM) (Македонија)",
39282         "mk",
39283         "389"
39284       ],
39285       [
39286         "Madagascar (Madagasikara)",
39287         "mg",
39288         "261"
39289       ],
39290       [
39291         "Malawi",
39292         "mw",
39293         "265"
39294       ],
39295       [
39296         "Malaysia",
39297         "my",
39298         "60"
39299       ],
39300       [
39301         "Maldives",
39302         "mv",
39303         "960"
39304       ],
39305       [
39306         "Mali",
39307         "ml",
39308         "223"
39309       ],
39310       [
39311         "Malta",
39312         "mt",
39313         "356"
39314       ],
39315       [
39316         "Marshall Islands",
39317         "mh",
39318         "692"
39319       ],
39320       [
39321         "Martinique",
39322         "mq",
39323         "596"
39324       ],
39325       [
39326         "Mauritania (‫موريتانيا‬‎)",
39327         "mr",
39328         "222"
39329       ],
39330       [
39331         "Mauritius (Moris)",
39332         "mu",
39333         "230"
39334       ],
39335       [
39336         "Mayotte",
39337         "yt",
39338         "262",
39339         1
39340       ],
39341       [
39342         "Mexico (México)",
39343         "mx",
39344         "52"
39345       ],
39346       [
39347         "Micronesia",
39348         "fm",
39349         "691"
39350       ],
39351       [
39352         "Moldova (Republica Moldova)",
39353         "md",
39354         "373"
39355       ],
39356       [
39357         "Monaco",
39358         "mc",
39359         "377"
39360       ],
39361       [
39362         "Mongolia (Монгол)",
39363         "mn",
39364         "976"
39365       ],
39366       [
39367         "Montenegro (Crna Gora)",
39368         "me",
39369         "382"
39370       ],
39371       [
39372         "Montserrat",
39373         "ms",
39374         "1664"
39375       ],
39376       [
39377         "Morocco (‫المغرب‬‎)",
39378         "ma",
39379         "212",
39380         0
39381       ],
39382       [
39383         "Mozambique (Moçambique)",
39384         "mz",
39385         "258"
39386       ],
39387       [
39388         "Myanmar (Burma) (မြန်မာ)",
39389         "mm",
39390         "95"
39391       ],
39392       [
39393         "Namibia (Namibië)",
39394         "na",
39395         "264"
39396       ],
39397       [
39398         "Nauru",
39399         "nr",
39400         "674"
39401       ],
39402       [
39403         "Nepal (नेपाल)",
39404         "np",
39405         "977"
39406       ],
39407       [
39408         "Netherlands (Nederland)",
39409         "nl",
39410         "31"
39411       ],
39412       [
39413         "New Caledonia (Nouvelle-Calédonie)",
39414         "nc",
39415         "687"
39416       ],
39417       [
39418         "New Zealand",
39419         "nz",
39420         "64"
39421       ],
39422       [
39423         "Nicaragua",
39424         "ni",
39425         "505"
39426       ],
39427       [
39428         "Niger (Nijar)",
39429         "ne",
39430         "227"
39431       ],
39432       [
39433         "Nigeria",
39434         "ng",
39435         "234"
39436       ],
39437       [
39438         "Niue",
39439         "nu",
39440         "683"
39441       ],
39442       [
39443         "Norfolk Island",
39444         "nf",
39445         "672"
39446       ],
39447       [
39448         "North Korea (조선 민주주의 인민 공화국)",
39449         "kp",
39450         "850"
39451       ],
39452       [
39453         "Northern Mariana Islands",
39454         "mp",
39455         "1670"
39456       ],
39457       [
39458         "Norway (Norge)",
39459         "no",
39460         "47",
39461         0
39462       ],
39463       [
39464         "Oman (‫عُمان‬‎)",
39465         "om",
39466         "968"
39467       ],
39468       [
39469         "Pakistan (‫پاکستان‬‎)",
39470         "pk",
39471         "92"
39472       ],
39473       [
39474         "Palau",
39475         "pw",
39476         "680"
39477       ],
39478       [
39479         "Palestine (‫فلسطين‬‎)",
39480         "ps",
39481         "970"
39482       ],
39483       [
39484         "Panama (Panamá)",
39485         "pa",
39486         "507"
39487       ],
39488       [
39489         "Papua New Guinea",
39490         "pg",
39491         "675"
39492       ],
39493       [
39494         "Paraguay",
39495         "py",
39496         "595"
39497       ],
39498       [
39499         "Peru (Perú)",
39500         "pe",
39501         "51"
39502       ],
39503       [
39504         "Philippines",
39505         "ph",
39506         "63"
39507       ],
39508       [
39509         "Poland (Polska)",
39510         "pl",
39511         "48"
39512       ],
39513       [
39514         "Portugal",
39515         "pt",
39516         "351"
39517       ],
39518       [
39519         "Puerto Rico",
39520         "pr",
39521         "1",
39522         3,
39523         ["787", "939"]
39524       ],
39525       [
39526         "Qatar (‫قطر‬‎)",
39527         "qa",
39528         "974"
39529       ],
39530       [
39531         "Réunion (La Réunion)",
39532         "re",
39533         "262",
39534         0
39535       ],
39536       [
39537         "Romania (România)",
39538         "ro",
39539         "40"
39540       ],
39541       [
39542         "Russia (Россия)",
39543         "ru",
39544         "7",
39545         0
39546       ],
39547       [
39548         "Rwanda",
39549         "rw",
39550         "250"
39551       ],
39552       [
39553         "Saint Barthélemy",
39554         "bl",
39555         "590",
39556         1
39557       ],
39558       [
39559         "Saint Helena",
39560         "sh",
39561         "290"
39562       ],
39563       [
39564         "Saint Kitts and Nevis",
39565         "kn",
39566         "1869"
39567       ],
39568       [
39569         "Saint Lucia",
39570         "lc",
39571         "1758"
39572       ],
39573       [
39574         "Saint Martin (Saint-Martin (partie française))",
39575         "mf",
39576         "590",
39577         2
39578       ],
39579       [
39580         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39581         "pm",
39582         "508"
39583       ],
39584       [
39585         "Saint Vincent and the Grenadines",
39586         "vc",
39587         "1784"
39588       ],
39589       [
39590         "Samoa",
39591         "ws",
39592         "685"
39593       ],
39594       [
39595         "San Marino",
39596         "sm",
39597         "378"
39598       ],
39599       [
39600         "São Tomé and Príncipe (São Tomé e Príncipe)",
39601         "st",
39602         "239"
39603       ],
39604       [
39605         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39606         "sa",
39607         "966"
39608       ],
39609       [
39610         "Senegal (Sénégal)",
39611         "sn",
39612         "221"
39613       ],
39614       [
39615         "Serbia (Србија)",
39616         "rs",
39617         "381"
39618       ],
39619       [
39620         "Seychelles",
39621         "sc",
39622         "248"
39623       ],
39624       [
39625         "Sierra Leone",
39626         "sl",
39627         "232"
39628       ],
39629       [
39630         "Singapore",
39631         "sg",
39632         "65"
39633       ],
39634       [
39635         "Sint Maarten",
39636         "sx",
39637         "1721"
39638       ],
39639       [
39640         "Slovakia (Slovensko)",
39641         "sk",
39642         "421"
39643       ],
39644       [
39645         "Slovenia (Slovenija)",
39646         "si",
39647         "386"
39648       ],
39649       [
39650         "Solomon Islands",
39651         "sb",
39652         "677"
39653       ],
39654       [
39655         "Somalia (Soomaaliya)",
39656         "so",
39657         "252"
39658       ],
39659       [
39660         "South Africa",
39661         "za",
39662         "27"
39663       ],
39664       [
39665         "South Korea (대한민국)",
39666         "kr",
39667         "82"
39668       ],
39669       [
39670         "South Sudan (‫جنوب السودان‬‎)",
39671         "ss",
39672         "211"
39673       ],
39674       [
39675         "Spain (España)",
39676         "es",
39677         "34"
39678       ],
39679       [
39680         "Sri Lanka (ශ්‍රී ලංකාව)",
39681         "lk",
39682         "94"
39683       ],
39684       [
39685         "Sudan (‫السودان‬‎)",
39686         "sd",
39687         "249"
39688       ],
39689       [
39690         "Suriname",
39691         "sr",
39692         "597"
39693       ],
39694       [
39695         "Svalbard and Jan Mayen",
39696         "sj",
39697         "47",
39698         1
39699       ],
39700       [
39701         "Swaziland",
39702         "sz",
39703         "268"
39704       ],
39705       [
39706         "Sweden (Sverige)",
39707         "se",
39708         "46"
39709       ],
39710       [
39711         "Switzerland (Schweiz)",
39712         "ch",
39713         "41"
39714       ],
39715       [
39716         "Syria (‫سوريا‬‎)",
39717         "sy",
39718         "963"
39719       ],
39720       [
39721         "Taiwan (台灣)",
39722         "tw",
39723         "886"
39724       ],
39725       [
39726         "Tajikistan",
39727         "tj",
39728         "992"
39729       ],
39730       [
39731         "Tanzania",
39732         "tz",
39733         "255"
39734       ],
39735       [
39736         "Thailand (ไทย)",
39737         "th",
39738         "66"
39739       ],
39740       [
39741         "Timor-Leste",
39742         "tl",
39743         "670"
39744       ],
39745       [
39746         "Togo",
39747         "tg",
39748         "228"
39749       ],
39750       [
39751         "Tokelau",
39752         "tk",
39753         "690"
39754       ],
39755       [
39756         "Tonga",
39757         "to",
39758         "676"
39759       ],
39760       [
39761         "Trinidad and Tobago",
39762         "tt",
39763         "1868"
39764       ],
39765       [
39766         "Tunisia (‫تونس‬‎)",
39767         "tn",
39768         "216"
39769       ],
39770       [
39771         "Turkey (Türkiye)",
39772         "tr",
39773         "90"
39774       ],
39775       [
39776         "Turkmenistan",
39777         "tm",
39778         "993"
39779       ],
39780       [
39781         "Turks and Caicos Islands",
39782         "tc",
39783         "1649"
39784       ],
39785       [
39786         "Tuvalu",
39787         "tv",
39788         "688"
39789       ],
39790       [
39791         "U.S. Virgin Islands",
39792         "vi",
39793         "1340"
39794       ],
39795       [
39796         "Uganda",
39797         "ug",
39798         "256"
39799       ],
39800       [
39801         "Ukraine (Україна)",
39802         "ua",
39803         "380"
39804       ],
39805       [
39806         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39807         "ae",
39808         "971"
39809       ],
39810       [
39811         "United Kingdom",
39812         "gb",
39813         "44",
39814         0
39815       ],
39816       [
39817         "United States",
39818         "us",
39819         "1",
39820         0
39821       ],
39822       [
39823         "Uruguay",
39824         "uy",
39825         "598"
39826       ],
39827       [
39828         "Uzbekistan (Oʻzbekiston)",
39829         "uz",
39830         "998"
39831       ],
39832       [
39833         "Vanuatu",
39834         "vu",
39835         "678"
39836       ],
39837       [
39838         "Vatican City (Città del Vaticano)",
39839         "va",
39840         "39",
39841         1
39842       ],
39843       [
39844         "Venezuela",
39845         "ve",
39846         "58"
39847       ],
39848       [
39849         "Vietnam (Việt Nam)",
39850         "vn",
39851         "84"
39852       ],
39853       [
39854         "Wallis and Futuna (Wallis-et-Futuna)",
39855         "wf",
39856         "681"
39857       ],
39858       [
39859         "Western Sahara (‫الصحراء الغربية‬‎)",
39860         "eh",
39861         "212",
39862         1
39863       ],
39864       [
39865         "Yemen (‫اليمن‬‎)",
39866         "ye",
39867         "967"
39868       ],
39869       [
39870         "Zambia",
39871         "zm",
39872         "260"
39873       ],
39874       [
39875         "Zimbabwe",
39876         "zw",
39877         "263"
39878       ],
39879       [
39880         "Åland Islands",
39881         "ax",
39882         "358",
39883         1
39884       ]
39885   ];
39886   
39887   return d;
39888 }/**
39889 *    This script refer to:
39890 *    Title: International Telephone Input
39891 *    Author: Jack O'Connor
39892 *    Code version:  v12.1.12
39893 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39894 **/
39895
39896 /**
39897  * @class Roo.bootstrap.PhoneInput
39898  * @extends Roo.bootstrap.TriggerField
39899  * An input with International dial-code selection
39900  
39901  * @cfg {String} defaultDialCode default '+852'
39902  * @cfg {Array} preferedCountries default []
39903   
39904  * @constructor
39905  * Create a new PhoneInput.
39906  * @param {Object} config Configuration options
39907  */
39908
39909 Roo.bootstrap.PhoneInput = function(config) {
39910     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39911 };
39912
39913 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39914         
39915         listWidth: undefined,
39916         
39917         selectedClass: 'active',
39918         
39919         invalidClass : "has-warning",
39920         
39921         validClass: 'has-success',
39922         
39923         allowed: '0123456789',
39924         
39925         max_length: 15,
39926         
39927         /**
39928          * @cfg {String} defaultDialCode The default dial code when initializing the input
39929          */
39930         defaultDialCode: '+852',
39931         
39932         /**
39933          * @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
39934          */
39935         preferedCountries: false,
39936         
39937         getAutoCreate : function()
39938         {
39939             var data = Roo.bootstrap.PhoneInputData();
39940             var align = this.labelAlign || this.parentLabelAlign();
39941             var id = Roo.id();
39942             
39943             this.allCountries = [];
39944             this.dialCodeMapping = [];
39945             
39946             for (var i = 0; i < data.length; i++) {
39947               var c = data[i];
39948               this.allCountries[i] = {
39949                 name: c[0],
39950                 iso2: c[1],
39951                 dialCode: c[2],
39952                 priority: c[3] || 0,
39953                 areaCodes: c[4] || null
39954               };
39955               this.dialCodeMapping[c[2]] = {
39956                   name: c[0],
39957                   iso2: c[1],
39958                   priority: c[3] || 0,
39959                   areaCodes: c[4] || null
39960               };
39961             }
39962             
39963             var cfg = {
39964                 cls: 'form-group',
39965                 cn: []
39966             };
39967             
39968             var input =  {
39969                 tag: 'input',
39970                 id : id,
39971                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39972                 maxlength: this.max_length,
39973                 cls : 'form-control tel-input',
39974                 autocomplete: 'new-password'
39975             };
39976             
39977             var hiddenInput = {
39978                 tag: 'input',
39979                 type: 'hidden',
39980                 cls: 'hidden-tel-input'
39981             };
39982             
39983             if (this.name) {
39984                 hiddenInput.name = this.name;
39985             }
39986             
39987             if (this.disabled) {
39988                 input.disabled = true;
39989             }
39990             
39991             var flag_container = {
39992                 tag: 'div',
39993                 cls: 'flag-box',
39994                 cn: [
39995                     {
39996                         tag: 'div',
39997                         cls: 'flag'
39998                     },
39999                     {
40000                         tag: 'div',
40001                         cls: 'caret'
40002                     }
40003                 ]
40004             };
40005             
40006             var box = {
40007                 tag: 'div',
40008                 cls: this.hasFeedback ? 'has-feedback' : '',
40009                 cn: [
40010                     hiddenInput,
40011                     input,
40012                     {
40013                         tag: 'input',
40014                         cls: 'dial-code-holder',
40015                         disabled: true
40016                     }
40017                 ]
40018             };
40019             
40020             var container = {
40021                 cls: 'roo-select2-container input-group',
40022                 cn: [
40023                     flag_container,
40024                     box
40025                 ]
40026             };
40027             
40028             if (this.fieldLabel.length) {
40029                 var indicator = {
40030                     tag: 'i',
40031                     tooltip: 'This field is required'
40032                 };
40033                 
40034                 var label = {
40035                     tag: 'label',
40036                     'for':  id,
40037                     cls: 'control-label',
40038                     cn: []
40039                 };
40040                 
40041                 var label_text = {
40042                     tag: 'span',
40043                     html: this.fieldLabel
40044                 };
40045                 
40046                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40047                 label.cn = [
40048                     indicator,
40049                     label_text
40050                 ];
40051                 
40052                 if(this.indicatorpos == 'right') {
40053                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40054                     label.cn = [
40055                         label_text,
40056                         indicator
40057                     ];
40058                 }
40059                 
40060                 if(align == 'left') {
40061                     container = {
40062                         tag: 'div',
40063                         cn: [
40064                             container
40065                         ]
40066                     };
40067                     
40068                     if(this.labelWidth > 12){
40069                         label.style = "width: " + this.labelWidth + 'px';
40070                     }
40071                     if(this.labelWidth < 13 && this.labelmd == 0){
40072                         this.labelmd = this.labelWidth;
40073                     }
40074                     if(this.labellg > 0){
40075                         label.cls += ' col-lg-' + this.labellg;
40076                         input.cls += ' col-lg-' + (12 - this.labellg);
40077                     }
40078                     if(this.labelmd > 0){
40079                         label.cls += ' col-md-' + this.labelmd;
40080                         container.cls += ' col-md-' + (12 - this.labelmd);
40081                     }
40082                     if(this.labelsm > 0){
40083                         label.cls += ' col-sm-' + this.labelsm;
40084                         container.cls += ' col-sm-' + (12 - this.labelsm);
40085                     }
40086                     if(this.labelxs > 0){
40087                         label.cls += ' col-xs-' + this.labelxs;
40088                         container.cls += ' col-xs-' + (12 - this.labelxs);
40089                     }
40090                 }
40091             }
40092             
40093             cfg.cn = [
40094                 label,
40095                 container
40096             ];
40097             
40098             var settings = this;
40099             
40100             ['xs','sm','md','lg'].map(function(size){
40101                 if (settings[size]) {
40102                     cfg.cls += ' col-' + size + '-' + settings[size];
40103                 }
40104             });
40105             
40106             this.store = new Roo.data.Store({
40107                 proxy : new Roo.data.MemoryProxy({}),
40108                 reader : new Roo.data.JsonReader({
40109                     fields : [
40110                         {
40111                             'name' : 'name',
40112                             'type' : 'string'
40113                         },
40114                         {
40115                             'name' : 'iso2',
40116                             'type' : 'string'
40117                         },
40118                         {
40119                             'name' : 'dialCode',
40120                             'type' : 'string'
40121                         },
40122                         {
40123                             'name' : 'priority',
40124                             'type' : 'string'
40125                         },
40126                         {
40127                             'name' : 'areaCodes',
40128                             'type' : 'string'
40129                         }
40130                     ]
40131                 })
40132             });
40133             
40134             if(!this.preferedCountries) {
40135                 this.preferedCountries = [
40136                     'hk',
40137                     'gb',
40138                     'us'
40139                 ];
40140             }
40141             
40142             var p = this.preferedCountries.reverse();
40143             
40144             if(p) {
40145                 for (var i = 0; i < p.length; i++) {
40146                     for (var j = 0; j < this.allCountries.length; j++) {
40147                         if(this.allCountries[j].iso2 == p[i]) {
40148                             var t = this.allCountries[j];
40149                             this.allCountries.splice(j,1);
40150                             this.allCountries.unshift(t);
40151                         }
40152                     } 
40153                 }
40154             }
40155             
40156             this.store.proxy.data = {
40157                 success: true,
40158                 data: this.allCountries
40159             };
40160             
40161             return cfg;
40162         },
40163         
40164         initEvents : function()
40165         {
40166             this.createList();
40167             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40168             
40169             this.indicator = this.indicatorEl();
40170             this.flag = this.flagEl();
40171             this.dialCodeHolder = this.dialCodeHolderEl();
40172             
40173             this.trigger = this.el.select('div.flag-box',true).first();
40174             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40175             
40176             var _this = this;
40177             
40178             (function(){
40179                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40180                 _this.list.setWidth(lw);
40181             }).defer(100);
40182             
40183             this.list.on('mouseover', this.onViewOver, this);
40184             this.list.on('mousemove', this.onViewMove, this);
40185             this.inputEl().on("keyup", this.onKeyUp, this);
40186             this.inputEl().on("keypress", this.onKeyPress, this);
40187             
40188             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40189
40190             this.view = new Roo.View(this.list, this.tpl, {
40191                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40192             });
40193             
40194             this.view.on('click', this.onViewClick, this);
40195             this.setValue(this.defaultDialCode);
40196         },
40197         
40198         onTriggerClick : function(e)
40199         {
40200             Roo.log('trigger click');
40201             if(this.disabled){
40202                 return;
40203             }
40204             
40205             if(this.isExpanded()){
40206                 this.collapse();
40207                 this.hasFocus = false;
40208             }else {
40209                 this.store.load({});
40210                 this.hasFocus = true;
40211                 this.expand();
40212             }
40213         },
40214         
40215         isExpanded : function()
40216         {
40217             return this.list.isVisible();
40218         },
40219         
40220         collapse : function()
40221         {
40222             if(!this.isExpanded()){
40223                 return;
40224             }
40225             this.list.hide();
40226             Roo.get(document).un('mousedown', this.collapseIf, this);
40227             Roo.get(document).un('mousewheel', this.collapseIf, this);
40228             this.fireEvent('collapse', this);
40229             this.validate();
40230         },
40231         
40232         expand : function()
40233         {
40234             Roo.log('expand');
40235
40236             if(this.isExpanded() || !this.hasFocus){
40237                 return;
40238             }
40239             
40240             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40241             this.list.setWidth(lw);
40242             
40243             this.list.show();
40244             this.restrictHeight();
40245             
40246             Roo.get(document).on('mousedown', this.collapseIf, this);
40247             Roo.get(document).on('mousewheel', this.collapseIf, this);
40248             
40249             this.fireEvent('expand', this);
40250         },
40251         
40252         restrictHeight : function()
40253         {
40254             this.list.alignTo(this.inputEl(), this.listAlign);
40255             this.list.alignTo(this.inputEl(), this.listAlign);
40256         },
40257         
40258         onViewOver : function(e, t)
40259         {
40260             if(this.inKeyMode){
40261                 return;
40262             }
40263             var item = this.view.findItemFromChild(t);
40264             
40265             if(item){
40266                 var index = this.view.indexOf(item);
40267                 this.select(index, false);
40268             }
40269         },
40270
40271         // private
40272         onViewClick : function(view, doFocus, el, e)
40273         {
40274             var index = this.view.getSelectedIndexes()[0];
40275             
40276             var r = this.store.getAt(index);
40277             
40278             if(r){
40279                 this.onSelect(r, index);
40280             }
40281             if(doFocus !== false && !this.blockFocus){
40282                 this.inputEl().focus();
40283             }
40284         },
40285         
40286         onViewMove : function(e, t)
40287         {
40288             this.inKeyMode = false;
40289         },
40290         
40291         select : function(index, scrollIntoView)
40292         {
40293             this.selectedIndex = index;
40294             this.view.select(index);
40295             if(scrollIntoView !== false){
40296                 var el = this.view.getNode(index);
40297                 if(el){
40298                     this.list.scrollChildIntoView(el, false);
40299                 }
40300             }
40301         },
40302         
40303         createList : function()
40304         {
40305             this.list = Roo.get(document.body).createChild({
40306                 tag: 'ul',
40307                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40308                 style: 'display:none'
40309             });
40310             
40311             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40312         },
40313         
40314         collapseIf : function(e)
40315         {
40316             var in_combo  = e.within(this.el);
40317             var in_list =  e.within(this.list);
40318             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40319             
40320             if (in_combo || in_list || is_list) {
40321                 return;
40322             }
40323             this.collapse();
40324         },
40325         
40326         onSelect : function(record, index)
40327         {
40328             if(this.fireEvent('beforeselect', this, record, index) !== false){
40329                 
40330                 this.setFlagClass(record.data.iso2);
40331                 this.setDialCode(record.data.dialCode);
40332                 this.hasFocus = false;
40333                 this.collapse();
40334                 this.fireEvent('select', this, record, index);
40335             }
40336         },
40337         
40338         flagEl : function()
40339         {
40340             var flag = this.el.select('div.flag',true).first();
40341             if(!flag){
40342                 return false;
40343             }
40344             return flag;
40345         },
40346         
40347         dialCodeHolderEl : function()
40348         {
40349             var d = this.el.select('input.dial-code-holder',true).first();
40350             if(!d){
40351                 return false;
40352             }
40353             return d;
40354         },
40355         
40356         setDialCode : function(v)
40357         {
40358             this.dialCodeHolder.dom.value = '+'+v;
40359         },
40360         
40361         setFlagClass : function(n)
40362         {
40363             this.flag.dom.className = 'flag '+n;
40364         },
40365         
40366         getValue : function()
40367         {
40368             var v = this.inputEl().getValue();
40369             if(this.dialCodeHolder) {
40370                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40371             }
40372             return v;
40373         },
40374         
40375         setValue : function(v)
40376         {
40377             var d = this.getDialCode(v);
40378             
40379             //invalid dial code
40380             if(v.length == 0 || !d || d.length == 0) {
40381                 if(this.rendered){
40382                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40383                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40384                 }
40385                 return;
40386             }
40387             
40388             //valid dial code
40389             this.setFlagClass(this.dialCodeMapping[d].iso2);
40390             this.setDialCode(d);
40391             this.inputEl().dom.value = v.replace('+'+d,'');
40392             this.hiddenEl().dom.value = this.getValue();
40393             
40394             this.validate();
40395         },
40396         
40397         getDialCode : function(v)
40398         {
40399             v = v ||  '';
40400             
40401             if (v.length == 0) {
40402                 return this.dialCodeHolder.dom.value;
40403             }
40404             
40405             var dialCode = "";
40406             if (v.charAt(0) != "+") {
40407                 return false;
40408             }
40409             var numericChars = "";
40410             for (var i = 1; i < v.length; i++) {
40411               var c = v.charAt(i);
40412               if (!isNaN(c)) {
40413                 numericChars += c;
40414                 if (this.dialCodeMapping[numericChars]) {
40415                   dialCode = v.substr(1, i);
40416                 }
40417                 if (numericChars.length == 4) {
40418                   break;
40419                 }
40420               }
40421             }
40422             return dialCode;
40423         },
40424         
40425         reset : function()
40426         {
40427             this.setValue(this.defaultDialCode);
40428             this.validate();
40429         },
40430         
40431         hiddenEl : function()
40432         {
40433             return this.el.select('input.hidden-tel-input',true).first();
40434         },
40435         
40436         // after setting val
40437         onKeyUp : function(e){
40438             this.setValue(this.getValue());
40439         },
40440         
40441         onKeyPress : function(e){
40442             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40443                 e.stopEvent();
40444             }
40445         }
40446         
40447 });
40448 /**
40449  * @class Roo.bootstrap.MoneyField
40450  * @extends Roo.bootstrap.ComboBox
40451  * Bootstrap MoneyField class
40452  * 
40453  * @constructor
40454  * Create a new MoneyField.
40455  * @param {Object} config Configuration options
40456  */
40457
40458 Roo.bootstrap.MoneyField = function(config) {
40459     
40460     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40461     
40462 };
40463
40464 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40465     
40466     /**
40467      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40468      */
40469     allowDecimals : true,
40470     /**
40471      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40472      */
40473     decimalSeparator : ".",
40474     /**
40475      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40476      */
40477     decimalPrecision : 0,
40478     /**
40479      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40480      */
40481     allowNegative : true,
40482     /**
40483      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40484      */
40485     allowZero: true,
40486     /**
40487      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40488      */
40489     minValue : Number.NEGATIVE_INFINITY,
40490     /**
40491      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40492      */
40493     maxValue : Number.MAX_VALUE,
40494     /**
40495      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40496      */
40497     minText : "The minimum value for this field is {0}",
40498     /**
40499      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40500      */
40501     maxText : "The maximum value for this field is {0}",
40502     /**
40503      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40504      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40505      */
40506     nanText : "{0} is not a valid number",
40507     /**
40508      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40509      */
40510     castInt : true,
40511     /**
40512      * @cfg {String} defaults currency of the MoneyField
40513      * value should be in lkey
40514      */
40515     defaultCurrency : false,
40516     /**
40517      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40518      */
40519     thousandsDelimiter : false,
40520     /**
40521      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40522      */
40523     max_length: false,
40524     
40525     inputlg : 9,
40526     inputmd : 9,
40527     inputsm : 9,
40528     inputxs : 6,
40529     
40530     store : false,
40531     
40532     getAutoCreate : function()
40533     {
40534         var align = this.labelAlign || this.parentLabelAlign();
40535         
40536         var id = Roo.id();
40537
40538         var cfg = {
40539             cls: 'form-group',
40540             cn: []
40541         };
40542
40543         var input =  {
40544             tag: 'input',
40545             id : id,
40546             cls : 'form-control roo-money-amount-input',
40547             autocomplete: 'new-password'
40548         };
40549         
40550         var hiddenInput = {
40551             tag: 'input',
40552             type: 'hidden',
40553             id: Roo.id(),
40554             cls: 'hidden-number-input'
40555         };
40556         
40557         if(this.max_length) {
40558             input.maxlength = this.max_length; 
40559         }
40560         
40561         if (this.name) {
40562             hiddenInput.name = this.name;
40563         }
40564
40565         if (this.disabled) {
40566             input.disabled = true;
40567         }
40568
40569         var clg = 12 - this.inputlg;
40570         var cmd = 12 - this.inputmd;
40571         var csm = 12 - this.inputsm;
40572         var cxs = 12 - this.inputxs;
40573         
40574         var container = {
40575             tag : 'div',
40576             cls : 'row roo-money-field',
40577             cn : [
40578                 {
40579                     tag : 'div',
40580                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40581                     cn : [
40582                         {
40583                             tag : 'div',
40584                             cls: 'roo-select2-container input-group',
40585                             cn: [
40586                                 {
40587                                     tag : 'input',
40588                                     cls : 'form-control roo-money-currency-input',
40589                                     autocomplete: 'new-password',
40590                                     readOnly : 1,
40591                                     name : this.currencyName
40592                                 },
40593                                 {
40594                                     tag :'span',
40595                                     cls : 'input-group-addon',
40596                                     cn : [
40597                                         {
40598                                             tag: 'span',
40599                                             cls: 'caret'
40600                                         }
40601                                     ]
40602                                 }
40603                             ]
40604                         }
40605                     ]
40606                 },
40607                 {
40608                     tag : 'div',
40609                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40610                     cn : [
40611                         {
40612                             tag: 'div',
40613                             cls: this.hasFeedback ? 'has-feedback' : '',
40614                             cn: [
40615                                 input
40616                             ]
40617                         }
40618                     ]
40619                 }
40620             ]
40621             
40622         };
40623         
40624         if (this.fieldLabel.length) {
40625             var indicator = {
40626                 tag: 'i',
40627                 tooltip: 'This field is required'
40628             };
40629
40630             var label = {
40631                 tag: 'label',
40632                 'for':  id,
40633                 cls: 'control-label',
40634                 cn: []
40635             };
40636
40637             var label_text = {
40638                 tag: 'span',
40639                 html: this.fieldLabel
40640             };
40641
40642             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40643             label.cn = [
40644                 indicator,
40645                 label_text
40646             ];
40647
40648             if(this.indicatorpos == 'right') {
40649                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40650                 label.cn = [
40651                     label_text,
40652                     indicator
40653                 ];
40654             }
40655
40656             if(align == 'left') {
40657                 container = {
40658                     tag: 'div',
40659                     cn: [
40660                         container
40661                     ]
40662                 };
40663
40664                 if(this.labelWidth > 12){
40665                     label.style = "width: " + this.labelWidth + 'px';
40666                 }
40667                 if(this.labelWidth < 13 && this.labelmd == 0){
40668                     this.labelmd = this.labelWidth;
40669                 }
40670                 if(this.labellg > 0){
40671                     label.cls += ' col-lg-' + this.labellg;
40672                     input.cls += ' col-lg-' + (12 - this.labellg);
40673                 }
40674                 if(this.labelmd > 0){
40675                     label.cls += ' col-md-' + this.labelmd;
40676                     container.cls += ' col-md-' + (12 - this.labelmd);
40677                 }
40678                 if(this.labelsm > 0){
40679                     label.cls += ' col-sm-' + this.labelsm;
40680                     container.cls += ' col-sm-' + (12 - this.labelsm);
40681                 }
40682                 if(this.labelxs > 0){
40683                     label.cls += ' col-xs-' + this.labelxs;
40684                     container.cls += ' col-xs-' + (12 - this.labelxs);
40685                 }
40686             }
40687         }
40688
40689         cfg.cn = [
40690             label,
40691             container,
40692             hiddenInput
40693         ];
40694         
40695         var settings = this;
40696
40697         ['xs','sm','md','lg'].map(function(size){
40698             if (settings[size]) {
40699                 cfg.cls += ' col-' + size + '-' + settings[size];
40700             }
40701         });
40702         
40703         return cfg;
40704     },
40705     
40706     initEvents : function()
40707     {
40708         this.indicator = this.indicatorEl();
40709         
40710         this.initCurrencyEvent();
40711         
40712         this.initNumberEvent();
40713     },
40714     
40715     initCurrencyEvent : function()
40716     {
40717         if (!this.store) {
40718             throw "can not find store for combo";
40719         }
40720         
40721         this.store = Roo.factory(this.store, Roo.data);
40722         this.store.parent = this;
40723         
40724         this.createList();
40725         
40726         this.triggerEl = this.el.select('.input-group-addon', true).first();
40727         
40728         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40729         
40730         var _this = this;
40731         
40732         (function(){
40733             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40734             _this.list.setWidth(lw);
40735         }).defer(100);
40736         
40737         this.list.on('mouseover', this.onViewOver, this);
40738         this.list.on('mousemove', this.onViewMove, this);
40739         this.list.on('scroll', this.onViewScroll, this);
40740         
40741         if(!this.tpl){
40742             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40743         }
40744         
40745         this.view = new Roo.View(this.list, this.tpl, {
40746             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40747         });
40748         
40749         this.view.on('click', this.onViewClick, this);
40750         
40751         this.store.on('beforeload', this.onBeforeLoad, this);
40752         this.store.on('load', this.onLoad, this);
40753         this.store.on('loadexception', this.onLoadException, this);
40754         
40755         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40756             "up" : function(e){
40757                 this.inKeyMode = true;
40758                 this.selectPrev();
40759             },
40760
40761             "down" : function(e){
40762                 if(!this.isExpanded()){
40763                     this.onTriggerClick();
40764                 }else{
40765                     this.inKeyMode = true;
40766                     this.selectNext();
40767                 }
40768             },
40769
40770             "enter" : function(e){
40771                 this.collapse();
40772                 
40773                 if(this.fireEvent("specialkey", this, e)){
40774                     this.onViewClick(false);
40775                 }
40776                 
40777                 return true;
40778             },
40779
40780             "esc" : function(e){
40781                 this.collapse();
40782             },
40783
40784             "tab" : function(e){
40785                 this.collapse();
40786                 
40787                 if(this.fireEvent("specialkey", this, e)){
40788                     this.onViewClick(false);
40789                 }
40790                 
40791                 return true;
40792             },
40793
40794             scope : this,
40795
40796             doRelay : function(foo, bar, hname){
40797                 if(hname == 'down' || this.scope.isExpanded()){
40798                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40799                 }
40800                 return true;
40801             },
40802
40803             forceKeyDown: true
40804         });
40805         
40806         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40807         
40808     },
40809     
40810     initNumberEvent : function(e)
40811     {
40812         this.inputEl().on("keydown" , this.fireKey,  this);
40813         this.inputEl().on("focus", this.onFocus,  this);
40814         this.inputEl().on("blur", this.onBlur,  this);
40815         
40816         this.inputEl().relayEvent('keyup', this);
40817         
40818         if(this.indicator){
40819             this.indicator.addClass('invisible');
40820         }
40821  
40822         this.originalValue = this.getValue();
40823         
40824         if(this.validationEvent == 'keyup'){
40825             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40826             this.inputEl().on('keyup', this.filterValidation, this);
40827         }
40828         else if(this.validationEvent !== false){
40829             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40830         }
40831         
40832         if(this.selectOnFocus){
40833             this.on("focus", this.preFocus, this);
40834             
40835         }
40836         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40837             this.inputEl().on("keypress", this.filterKeys, this);
40838         } else {
40839             this.inputEl().relayEvent('keypress', this);
40840         }
40841         
40842         var allowed = "0123456789";
40843         
40844         if(this.allowDecimals){
40845             allowed += this.decimalSeparator;
40846         }
40847         
40848         if(this.allowNegative){
40849             allowed += "-";
40850         }
40851         
40852         if(this.thousandsDelimiter) {
40853             allowed += ",";
40854         }
40855         
40856         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40857         
40858         var keyPress = function(e){
40859             
40860             var k = e.getKey();
40861             
40862             var c = e.getCharCode();
40863             
40864             if(
40865                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40866                     allowed.indexOf(String.fromCharCode(c)) === -1
40867             ){
40868                 e.stopEvent();
40869                 return;
40870             }
40871             
40872             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40873                 return;
40874             }
40875             
40876             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40877                 e.stopEvent();
40878             }
40879         };
40880         
40881         this.inputEl().on("keypress", keyPress, this);
40882         
40883     },
40884     
40885     onTriggerClick : function(e)
40886     {   
40887         if(this.disabled){
40888             return;
40889         }
40890         
40891         this.page = 0;
40892         this.loadNext = false;
40893         
40894         if(this.isExpanded()){
40895             this.collapse();
40896             return;
40897         }
40898         
40899         this.hasFocus = true;
40900         
40901         if(this.triggerAction == 'all') {
40902             this.doQuery(this.allQuery, true);
40903             return;
40904         }
40905         
40906         this.doQuery(this.getRawValue());
40907     },
40908     
40909     getCurrency : function()
40910     {   
40911         var v = this.currencyEl().getValue();
40912         
40913         return v;
40914     },
40915     
40916     restrictHeight : function()
40917     {
40918         this.list.alignTo(this.currencyEl(), this.listAlign);
40919         this.list.alignTo(this.currencyEl(), this.listAlign);
40920     },
40921     
40922     onViewClick : function(view, doFocus, el, e)
40923     {
40924         var index = this.view.getSelectedIndexes()[0];
40925         
40926         var r = this.store.getAt(index);
40927         
40928         if(r){
40929             this.onSelect(r, index);
40930         }
40931     },
40932     
40933     onSelect : function(record, index){
40934         
40935         if(this.fireEvent('beforeselect', this, record, index) !== false){
40936         
40937             this.setFromCurrencyData(index > -1 ? record.data : false);
40938             
40939             this.collapse();
40940             
40941             this.fireEvent('select', this, record, index);
40942         }
40943     },
40944     
40945     setFromCurrencyData : function(o)
40946     {
40947         var currency = '';
40948         
40949         this.lastCurrency = o;
40950         
40951         if (this.currencyField) {
40952             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40953         } else {
40954             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40955         }
40956         
40957         this.lastSelectionText = currency;
40958         
40959         //setting default currency
40960         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40961             this.setCurrency(this.defaultCurrency);
40962             return;
40963         }
40964         
40965         this.setCurrency(currency);
40966     },
40967     
40968     setFromData : function(o)
40969     {
40970         var c = {};
40971         
40972         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40973         
40974         this.setFromCurrencyData(c);
40975         
40976         var value = '';
40977         
40978         if (this.name) {
40979             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40980         } else {
40981             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40982         }
40983         
40984         this.setValue(value);
40985         
40986     },
40987     
40988     setCurrency : function(v)
40989     {   
40990         this.currencyValue = v;
40991         
40992         if(this.rendered){
40993             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40994             this.validate();
40995         }
40996     },
40997     
40998     setValue : function(v)
40999     {
41000         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41001         
41002         this.value = v;
41003         
41004         if(this.rendered){
41005             
41006             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41007             
41008             this.inputEl().dom.value = (v == '') ? '' :
41009                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41010             
41011             if(!this.allowZero && v === '0') {
41012                 this.hiddenEl().dom.value = '';
41013                 this.inputEl().dom.value = '';
41014             }
41015             
41016             this.validate();
41017         }
41018     },
41019     
41020     getRawValue : function()
41021     {
41022         var v = this.inputEl().getValue();
41023         
41024         return v;
41025     },
41026     
41027     getValue : function()
41028     {
41029         return this.fixPrecision(this.parseValue(this.getRawValue()));
41030     },
41031     
41032     parseValue : function(value)
41033     {
41034         if(this.thousandsDelimiter) {
41035             value += "";
41036             r = new RegExp(",", "g");
41037             value = value.replace(r, "");
41038         }
41039         
41040         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41041         return isNaN(value) ? '' : value;
41042         
41043     },
41044     
41045     fixPrecision : function(value)
41046     {
41047         if(this.thousandsDelimiter) {
41048             value += "";
41049             r = new RegExp(",", "g");
41050             value = value.replace(r, "");
41051         }
41052         
41053         var nan = isNaN(value);
41054         
41055         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41056             return nan ? '' : value;
41057         }
41058         return parseFloat(value).toFixed(this.decimalPrecision);
41059     },
41060     
41061     decimalPrecisionFcn : function(v)
41062     {
41063         return Math.floor(v);
41064     },
41065     
41066     validateValue : function(value)
41067     {
41068         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41069             return false;
41070         }
41071         
41072         var num = this.parseValue(value);
41073         
41074         if(isNaN(num)){
41075             this.markInvalid(String.format(this.nanText, value));
41076             return false;
41077         }
41078         
41079         if(num < this.minValue){
41080             this.markInvalid(String.format(this.minText, this.minValue));
41081             return false;
41082         }
41083         
41084         if(num > this.maxValue){
41085             this.markInvalid(String.format(this.maxText, this.maxValue));
41086             return false;
41087         }
41088         
41089         return true;
41090     },
41091     
41092     validate : function()
41093     {
41094         if(this.disabled || this.allowBlank){
41095             this.markValid();
41096             return true;
41097         }
41098         
41099         var currency = this.getCurrency();
41100         
41101         if(this.validateValue(this.getRawValue()) && currency.length){
41102             this.markValid();
41103             return true;
41104         }
41105         
41106         this.markInvalid();
41107         return false;
41108     },
41109     
41110     getName: function()
41111     {
41112         return this.name;
41113     },
41114     
41115     beforeBlur : function()
41116     {
41117         if(!this.castInt){
41118             return;
41119         }
41120         
41121         var v = this.parseValue(this.getRawValue());
41122         
41123         if(v || v == 0){
41124             this.setValue(v);
41125         }
41126     },
41127     
41128     onBlur : function()
41129     {
41130         this.beforeBlur();
41131         
41132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41133             //this.el.removeClass(this.focusClass);
41134         }
41135         
41136         this.hasFocus = false;
41137         
41138         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41139             this.validate();
41140         }
41141         
41142         var v = this.getValue();
41143         
41144         if(String(v) !== String(this.startValue)){
41145             this.fireEvent('change', this, v, this.startValue);
41146         }
41147         
41148         this.fireEvent("blur", this);
41149     },
41150     
41151     inputEl : function()
41152     {
41153         return this.el.select('.roo-money-amount-input', true).first();
41154     },
41155     
41156     currencyEl : function()
41157     {
41158         return this.el.select('.roo-money-currency-input', true).first();
41159     },
41160     
41161     hiddenEl : function()
41162     {
41163         return this.el.select('input.hidden-number-input',true).first();
41164     }
41165     
41166 });