bd66565324a78ec7e5baf39de313dd1370036bb8
[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 && Roo.bootstrap.version == 3) {
2774             header.push({
2775                 tag: 'button',
2776                 cls : 'close',
2777                 html : '&times'
2778             });
2779         }
2780
2781         header.push(title);
2782
2783         if (this.allow_close && Roo.bootstrap.version == 4) {
2784             header.push({
2785                 tag: 'button',
2786                 cls : 'close',
2787                 html : '&times'
2788             });
2789         }
2790         
2791         var size = '';
2792
2793         if(this.size.length){
2794             size = 'modal-' + this.size;
2795         }
2796
2797         var modal = {
2798             cls: "modal",
2799              cn : [
2800                 {
2801                     cls: "modal-dialog " + size,
2802                     cn : [
2803                         {
2804                             cls : "modal-content",
2805                             cn : [
2806                                 {
2807                                     cls : 'modal-header',
2808                                     cn : header
2809                                 },
2810                                 bdy,
2811                                 {
2812                                     cls : 'modal-footer',
2813                                     cn : [
2814                                         {
2815                                             tag: 'div',
2816                                             cls: 'btn-' + this.buttonPosition
2817                                         }
2818                                     ]
2819
2820                                 }
2821
2822
2823                             ]
2824
2825                         }
2826                     ]
2827
2828                 }
2829             ]
2830         };
2831
2832         if(this.animate){
2833             modal.cls += ' fade';
2834         }
2835
2836         return modal;
2837
2838     },
2839     getChildContainer : function() {
2840
2841          return this.bodyEl;
2842
2843     },
2844     getButtonContainer : function() {
2845          return this.el.select('.modal-footer div',true).first();
2846
2847     },
2848     initEvents : function()
2849     {
2850         if (this.allow_close) {
2851             this.closeEl.on('click', this.hide, this);
2852         }
2853         Roo.EventManager.onWindowResize(this.resize, this, true);
2854
2855
2856     },
2857
2858     resize : function()
2859     {
2860         this.maskEl.setSize(
2861             Roo.lib.Dom.getViewWidth(true),
2862             Roo.lib.Dom.getViewHeight(true)
2863         );
2864         
2865         if (this.fitwindow) {
2866             this.setSize(
2867                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2868                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2869             );
2870             return;
2871         }
2872         
2873         if(this.max_width !== 0) {
2874             
2875             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2876             
2877             if(this.height) {
2878                 this.setSize(w, this.height);
2879                 return;
2880             }
2881             
2882             if(this.max_height) {
2883                 this.setSize(w,Math.min(
2884                     this.max_height,
2885                     Roo.lib.Dom.getViewportHeight(true) - 60
2886                 ));
2887                 
2888                 return;
2889             }
2890             
2891             if(!this.fit_content) {
2892                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2893                 return;
2894             }
2895             
2896             this.setSize(w, Math.min(
2897                 60 +
2898                 this.headerEl.getHeight() + 
2899                 this.footerEl.getHeight() + 
2900                 this.getChildHeight(this.bodyEl.dom.childNodes),
2901                 Roo.lib.Dom.getViewportHeight(true) - 60)
2902             );
2903         }
2904         
2905     },
2906
2907     setSize : function(w,h)
2908     {
2909         if (!w && !h) {
2910             return;
2911         }
2912         
2913         this.resizeTo(w,h);
2914     },
2915
2916     show : function() {
2917
2918         if (!this.rendered) {
2919             this.render();
2920         }
2921
2922         //this.el.setStyle('display', 'block');
2923         this.el.removeClass('hideing');
2924         this.el.dom.style.display='block';
2925         
2926         Roo.get(document.body).addClass('modal-open');
2927  
2928         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2929             var _this = this;
2930             (function(){
2931                 this.el.addClass('show');
2932                 this.el.addClass('in');
2933             }).defer(50, this);
2934         }else{
2935             this.el.addClass('show');
2936             this.el.addClass('in');
2937         }
2938
2939         // not sure how we can show data in here..
2940         //if (this.tmpl) {
2941         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2942         //}
2943
2944         Roo.get(document.body).addClass("x-body-masked");
2945         
2946         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2947         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2948         this.maskEl.dom.style.display = 'block';
2949         this.maskEl.addClass('show');
2950         
2951         
2952         this.resize();
2953         
2954         this.fireEvent('show', this);
2955
2956         // set zindex here - otherwise it appears to be ignored...
2957         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2958
2959         (function () {
2960             this.items.forEach( function(e) {
2961                 e.layout ? e.layout() : false;
2962
2963             });
2964         }).defer(100,this);
2965
2966     },
2967     hide : function()
2968     {
2969         if(this.fireEvent("beforehide", this) !== false){
2970             
2971             this.maskEl.removeClass('show');
2972             
2973             this.maskEl.dom.style.display = '';
2974             Roo.get(document.body).removeClass("x-body-masked");
2975             this.el.removeClass('in');
2976             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2977
2978             if(this.animate){ // why
2979                 this.el.addClass('hideing');
2980                 this.el.removeClass('show');
2981                 (function(){
2982                     if (!this.el.hasClass('hideing')) {
2983                         return; // it's been shown again...
2984                     }
2985                     
2986                     this.el.dom.style.display='';
2987
2988                     Roo.get(document.body).removeClass('modal-open');
2989                     this.el.removeClass('hideing');
2990                 }).defer(150,this);
2991                 
2992             }else{
2993                 this.el.removeClass('show');
2994                 this.el.dom.style.display='';
2995                 Roo.get(document.body).removeClass('modal-open');
2996
2997             }
2998             this.fireEvent('hide', this);
2999         }
3000     },
3001     isVisible : function()
3002     {
3003         
3004         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3005         
3006     },
3007
3008     addButton : function(str, cb)
3009     {
3010
3011
3012         var b = Roo.apply({}, { html : str } );
3013         b.xns = b.xns || Roo.bootstrap;
3014         b.xtype = b.xtype || 'Button';
3015         if (typeof(b.listeners) == 'undefined') {
3016             b.listeners = { click : cb.createDelegate(this)  };
3017         }
3018
3019         var btn = Roo.factory(b);
3020
3021         btn.render(this.el.select('.modal-footer div').first());
3022
3023         return btn;
3024
3025     },
3026
3027     setDefaultButton : function(btn)
3028     {
3029         //this.el.select('.modal-footer').()
3030     },
3031     diff : false,
3032
3033     resizeTo: function(w,h)
3034     {
3035         // skip.. ?? why??
3036
3037         this.dialogEl.setWidth(w);
3038         if (this.diff === false) {
3039             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3040         }
3041
3042         this.bodyEl.setHeight(h - this.diff);
3043
3044         this.fireEvent('resize', this);
3045
3046     },
3047     setContentSize  : function(w, h)
3048     {
3049
3050     },
3051     onButtonClick: function(btn,e)
3052     {
3053         //Roo.log([a,b,c]);
3054         this.fireEvent('btnclick', btn.name, e);
3055     },
3056      /**
3057      * Set the title of the Dialog
3058      * @param {String} str new Title
3059      */
3060     setTitle: function(str) {
3061         this.titleEl.dom.innerHTML = str;
3062     },
3063     /**
3064      * Set the body of the Dialog
3065      * @param {String} str new Title
3066      */
3067     setBody: function(str) {
3068         this.bodyEl.dom.innerHTML = str;
3069     },
3070     /**
3071      * Set the body of the Dialog using the template
3072      * @param {Obj} data - apply this data to the template and replace the body contents.
3073      */
3074     applyBody: function(obj)
3075     {
3076         if (!this.tmpl) {
3077             Roo.log("Error - using apply Body without a template");
3078             //code
3079         }
3080         this.tmpl.overwrite(this.bodyEl, obj);
3081     },
3082     
3083     getChildHeight : function(child_nodes)
3084     {
3085         if(
3086             !child_nodes ||
3087             child_nodes.length == 0
3088         ) {
3089             return;
3090         }
3091         
3092         var child_height = 0;
3093         
3094         for(var i = 0; i < child_nodes.length; i++) {
3095             
3096             /*
3097             * for modal with tabs...
3098             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3099                 
3100                 var layout_childs = child_nodes[i].childNodes;
3101                 
3102                 for(var j = 0; j < layout_childs.length; j++) {
3103                     
3104                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3105                         
3106                         var layout_body_childs = layout_childs[j].childNodes;
3107                         
3108                         for(var k = 0; k < layout_body_childs.length; k++) {
3109                             
3110                             if(layout_body_childs[k].classList.contains('navbar')) {
3111                                 child_height += layout_body_childs[k].offsetHeight;
3112                                 continue;
3113                             }
3114                             
3115                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3116                                 
3117                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3118                                 
3119                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3120                                     
3121                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3122                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3123                                         continue;
3124                                     }
3125                                     
3126                                 }
3127                                 
3128                             }
3129                             
3130                         }
3131                     }
3132                 }
3133                 continue;
3134             }
3135             */
3136             
3137             child_height += child_nodes[i].offsetHeight;
3138             // Roo.log(child_nodes[i].offsetHeight);
3139         }
3140         
3141         return child_height;
3142     }
3143
3144 });
3145
3146
3147 Roo.apply(Roo.bootstrap.Modal,  {
3148     /**
3149          * Button config that displays a single OK button
3150          * @type Object
3151          */
3152         OK :  [{
3153             name : 'ok',
3154             weight : 'primary',
3155             html : 'OK'
3156         }],
3157         /**
3158          * Button config that displays Yes and No buttons
3159          * @type Object
3160          */
3161         YESNO : [
3162             {
3163                 name  : 'no',
3164                 html : 'No'
3165             },
3166             {
3167                 name  :'yes',
3168                 weight : 'primary',
3169                 html : 'Yes'
3170             }
3171         ],
3172
3173         /**
3174          * Button config that displays OK and Cancel buttons
3175          * @type Object
3176          */
3177         OKCANCEL : [
3178             {
3179                name : 'cancel',
3180                 html : 'Cancel'
3181             },
3182             {
3183                 name : 'ok',
3184                 weight : 'primary',
3185                 html : 'OK'
3186             }
3187         ],
3188         /**
3189          * Button config that displays Yes, No and Cancel buttons
3190          * @type Object
3191          */
3192         YESNOCANCEL : [
3193             {
3194                 name : 'yes',
3195                 weight : 'primary',
3196                 html : 'Yes'
3197             },
3198             {
3199                 name : 'no',
3200                 html : 'No'
3201             },
3202             {
3203                 name : 'cancel',
3204                 html : 'Cancel'
3205             }
3206         ],
3207         
3208         zIndex : 10001
3209 });
3210 /*
3211  * - LGPL
3212  *
3213  * messagebox - can be used as a replace
3214  * 
3215  */
3216 /**
3217  * @class Roo.MessageBox
3218  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3219  * Example usage:
3220  *<pre><code>
3221 // Basic alert:
3222 Roo.Msg.alert('Status', 'Changes saved successfully.');
3223
3224 // Prompt for user data:
3225 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3226     if (btn == 'ok'){
3227         // process text value...
3228     }
3229 });
3230
3231 // Show a dialog using config options:
3232 Roo.Msg.show({
3233    title:'Save Changes?',
3234    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3235    buttons: Roo.Msg.YESNOCANCEL,
3236    fn: processResult,
3237    animEl: 'elId'
3238 });
3239 </code></pre>
3240  * @singleton
3241  */
3242 Roo.bootstrap.MessageBox = function(){
3243     var dlg, opt, mask, waitTimer;
3244     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3245     var buttons, activeTextEl, bwidth;
3246
3247     
3248     // private
3249     var handleButton = function(button){
3250         dlg.hide();
3251         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3252     };
3253
3254     // private
3255     var handleHide = function(){
3256         if(opt && opt.cls){
3257             dlg.el.removeClass(opt.cls);
3258         }
3259         //if(waitTimer){
3260         //    Roo.TaskMgr.stop(waitTimer);
3261         //    waitTimer = null;
3262         //}
3263     };
3264
3265     // private
3266     var updateButtons = function(b){
3267         var width = 0;
3268         if(!b){
3269             buttons["ok"].hide();
3270             buttons["cancel"].hide();
3271             buttons["yes"].hide();
3272             buttons["no"].hide();
3273             //dlg.footer.dom.style.display = 'none';
3274             return width;
3275         }
3276         dlg.footerEl.dom.style.display = '';
3277         for(var k in buttons){
3278             if(typeof buttons[k] != "function"){
3279                 if(b[k]){
3280                     buttons[k].show();
3281                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3282                     width += buttons[k].el.getWidth()+15;
3283                 }else{
3284                     buttons[k].hide();
3285                 }
3286             }
3287         }
3288         return width;
3289     };
3290
3291     // private
3292     var handleEsc = function(d, k, e){
3293         if(opt && opt.closable !== false){
3294             dlg.hide();
3295         }
3296         if(e){
3297             e.stopEvent();
3298         }
3299     };
3300
3301     return {
3302         /**
3303          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3304          * @return {Roo.BasicDialog} The BasicDialog element
3305          */
3306         getDialog : function(){
3307            if(!dlg){
3308                 dlg = new Roo.bootstrap.Modal( {
3309                     //draggable: true,
3310                     //resizable:false,
3311                     //constraintoviewport:false,
3312                     //fixedcenter:true,
3313                     //collapsible : false,
3314                     //shim:true,
3315                     //modal: true,
3316                 //    width: 'auto',
3317                   //  height:100,
3318                     //buttonAlign:"center",
3319                     closeClick : function(){
3320                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3321                             handleButton("no");
3322                         }else{
3323                             handleButton("cancel");
3324                         }
3325                     }
3326                 });
3327                 dlg.render();
3328                 dlg.on("hide", handleHide);
3329                 mask = dlg.mask;
3330                 //dlg.addKeyListener(27, handleEsc);
3331                 buttons = {};
3332                 this.buttons = buttons;
3333                 var bt = this.buttonText;
3334                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3335                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3336                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3337                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3338                 //Roo.log(buttons);
3339                 bodyEl = dlg.bodyEl.createChild({
3340
3341                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3342                         '<textarea class="roo-mb-textarea"></textarea>' +
3343                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3344                 });
3345                 msgEl = bodyEl.dom.firstChild;
3346                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3347                 textboxEl.enableDisplayMode();
3348                 textboxEl.addKeyListener([10,13], function(){
3349                     if(dlg.isVisible() && opt && opt.buttons){
3350                         if(opt.buttons.ok){
3351                             handleButton("ok");
3352                         }else if(opt.buttons.yes){
3353                             handleButton("yes");
3354                         }
3355                     }
3356                 });
3357                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3358                 textareaEl.enableDisplayMode();
3359                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3360                 progressEl.enableDisplayMode();
3361                 
3362                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3363                 var pf = progressEl.dom.firstChild;
3364                 if (pf) {
3365                     pp = Roo.get(pf.firstChild);
3366                     pp.setHeight(pf.offsetHeight);
3367                 }
3368                 
3369             }
3370             return dlg;
3371         },
3372
3373         /**
3374          * Updates the message box body text
3375          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3376          * the XHTML-compliant non-breaking space character '&amp;#160;')
3377          * @return {Roo.MessageBox} This message box
3378          */
3379         updateText : function(text)
3380         {
3381             if(!dlg.isVisible() && !opt.width){
3382                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3383                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3384             }
3385             msgEl.innerHTML = text || '&#160;';
3386       
3387             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3388             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3389             var w = Math.max(
3390                     Math.min(opt.width || cw , this.maxWidth), 
3391                     Math.max(opt.minWidth || this.minWidth, bwidth)
3392             );
3393             if(opt.prompt){
3394                 activeTextEl.setWidth(w);
3395             }
3396             if(dlg.isVisible()){
3397                 dlg.fixedcenter = false;
3398             }
3399             // to big, make it scroll. = But as usual stupid IE does not support
3400             // !important..
3401             
3402             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3403                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3404                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3405             } else {
3406                 bodyEl.dom.style.height = '';
3407                 bodyEl.dom.style.overflowY = '';
3408             }
3409             if (cw > w) {
3410                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3411             } else {
3412                 bodyEl.dom.style.overflowX = '';
3413             }
3414             
3415             dlg.setContentSize(w, bodyEl.getHeight());
3416             if(dlg.isVisible()){
3417                 dlg.fixedcenter = true;
3418             }
3419             return this;
3420         },
3421
3422         /**
3423          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3424          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3425          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3426          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3427          * @return {Roo.MessageBox} This message box
3428          */
3429         updateProgress : function(value, text){
3430             if(text){
3431                 this.updateText(text);
3432             }
3433             
3434             if (pp) { // weird bug on my firefox - for some reason this is not defined
3435                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3436                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3437             }
3438             return this;
3439         },        
3440
3441         /**
3442          * Returns true if the message box is currently displayed
3443          * @return {Boolean} True if the message box is visible, else false
3444          */
3445         isVisible : function(){
3446             return dlg && dlg.isVisible();  
3447         },
3448
3449         /**
3450          * Hides the message box if it is displayed
3451          */
3452         hide : function(){
3453             if(this.isVisible()){
3454                 dlg.hide();
3455             }  
3456         },
3457
3458         /**
3459          * Displays a new message box, or reinitializes an existing message box, based on the config options
3460          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3461          * The following config object properties are supported:
3462          * <pre>
3463 Property    Type             Description
3464 ----------  ---------------  ------------------------------------------------------------------------------------
3465 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3466                                    closes (defaults to undefined)
3467 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3468                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3469 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3470                                    progress and wait dialogs will ignore this property and always hide the
3471                                    close button as they can only be closed programmatically.
3472 cls               String           A custom CSS class to apply to the message box element
3473 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3474                                    displayed (defaults to 75)
3475 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3476                                    function will be btn (the name of the button that was clicked, if applicable,
3477                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3478                                    Progress and wait dialogs will ignore this option since they do not respond to
3479                                    user actions and can only be closed programmatically, so any required function
3480                                    should be called by the same code after it closes the dialog.
3481 icon              String           A CSS class that provides a background image to be used as an icon for
3482                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3483 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3484 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3485 modal             Boolean          False to allow user interaction with the page while the message box is
3486                                    displayed (defaults to true)
3487 msg               String           A string that will replace the existing message box body text (defaults
3488                                    to the XHTML-compliant non-breaking space character '&#160;')
3489 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3490 progress          Boolean          True to display a progress bar (defaults to false)
3491 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3492 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3493 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3494 title             String           The title text
3495 value             String           The string value to set into the active textbox element if displayed
3496 wait              Boolean          True to display a progress bar (defaults to false)
3497 width             Number           The width of the dialog in pixels
3498 </pre>
3499          *
3500          * Example usage:
3501          * <pre><code>
3502 Roo.Msg.show({
3503    title: 'Address',
3504    msg: 'Please enter your address:',
3505    width: 300,
3506    buttons: Roo.MessageBox.OKCANCEL,
3507    multiline: true,
3508    fn: saveAddress,
3509    animEl: 'addAddressBtn'
3510 });
3511 </code></pre>
3512          * @param {Object} config Configuration options
3513          * @return {Roo.MessageBox} This message box
3514          */
3515         show : function(options)
3516         {
3517             
3518             // this causes nightmares if you show one dialog after another
3519             // especially on callbacks..
3520              
3521             if(this.isVisible()){
3522                 
3523                 this.hide();
3524                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3525                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3526                 Roo.log("New Dialog Message:" +  options.msg )
3527                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3528                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3529                 
3530             }
3531             var d = this.getDialog();
3532             opt = options;
3533             d.setTitle(opt.title || "&#160;");
3534             d.closeEl.setDisplayed(opt.closable !== false);
3535             activeTextEl = textboxEl;
3536             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3537             if(opt.prompt){
3538                 if(opt.multiline){
3539                     textboxEl.hide();
3540                     textareaEl.show();
3541                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3542                         opt.multiline : this.defaultTextHeight);
3543                     activeTextEl = textareaEl;
3544                 }else{
3545                     textboxEl.show();
3546                     textareaEl.hide();
3547                 }
3548             }else{
3549                 textboxEl.hide();
3550                 textareaEl.hide();
3551             }
3552             progressEl.setDisplayed(opt.progress === true);
3553             this.updateProgress(0);
3554             activeTextEl.dom.value = opt.value || "";
3555             if(opt.prompt){
3556                 dlg.setDefaultButton(activeTextEl);
3557             }else{
3558                 var bs = opt.buttons;
3559                 var db = null;
3560                 if(bs && bs.ok){
3561                     db = buttons["ok"];
3562                 }else if(bs && bs.yes){
3563                     db = buttons["yes"];
3564                 }
3565                 dlg.setDefaultButton(db);
3566             }
3567             bwidth = updateButtons(opt.buttons);
3568             this.updateText(opt.msg);
3569             if(opt.cls){
3570                 d.el.addClass(opt.cls);
3571             }
3572             d.proxyDrag = opt.proxyDrag === true;
3573             d.modal = opt.modal !== false;
3574             d.mask = opt.modal !== false ? mask : false;
3575             if(!d.isVisible()){
3576                 // force it to the end of the z-index stack so it gets a cursor in FF
3577                 document.body.appendChild(dlg.el.dom);
3578                 d.animateTarget = null;
3579                 d.show(options.animEl);
3580             }
3581             return this;
3582         },
3583
3584         /**
3585          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3586          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3587          * and closing the message box when the process is complete.
3588          * @param {String} title The title bar text
3589          * @param {String} msg The message box body text
3590          * @return {Roo.MessageBox} This message box
3591          */
3592         progress : function(title, msg){
3593             this.show({
3594                 title : title,
3595                 msg : msg,
3596                 buttons: false,
3597                 progress:true,
3598                 closable:false,
3599                 minWidth: this.minProgressWidth,
3600                 modal : true
3601             });
3602             return this;
3603         },
3604
3605         /**
3606          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3607          * If a callback function is passed it will be called after the user clicks the button, and the
3608          * id of the button that was clicked will be passed as the only parameter to the callback
3609          * (could also be the top-right close button).
3610          * @param {String} title The title bar text
3611          * @param {String} msg The message box body text
3612          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3613          * @param {Object} scope (optional) The scope of the callback function
3614          * @return {Roo.MessageBox} This message box
3615          */
3616         alert : function(title, msg, fn, scope)
3617         {
3618             this.show({
3619                 title : title,
3620                 msg : msg,
3621                 buttons: this.OK,
3622                 fn: fn,
3623                 closable : false,
3624                 scope : scope,
3625                 modal : true
3626             });
3627             return this;
3628         },
3629
3630         /**
3631          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3632          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3633          * You are responsible for closing the message box when the process is complete.
3634          * @param {String} msg The message box body text
3635          * @param {String} title (optional) The title bar text
3636          * @return {Roo.MessageBox} This message box
3637          */
3638         wait : function(msg, title){
3639             this.show({
3640                 title : title,
3641                 msg : msg,
3642                 buttons: false,
3643                 closable:false,
3644                 progress:true,
3645                 modal:true,
3646                 width:300,
3647                 wait:true
3648             });
3649             waitTimer = Roo.TaskMgr.start({
3650                 run: function(i){
3651                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3652                 },
3653                 interval: 1000
3654             });
3655             return this;
3656         },
3657
3658         /**
3659          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3660          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3661          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
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          * @return {Roo.MessageBox} This message box
3667          */
3668         confirm : function(title, msg, fn, scope){
3669             this.show({
3670                 title : title,
3671                 msg : msg,
3672                 buttons: this.YESNO,
3673                 fn: fn,
3674                 scope : scope,
3675                 modal : true
3676             });
3677             return this;
3678         },
3679
3680         /**
3681          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3682          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3683          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3684          * (could also be the top-right close button) and the text that was entered will be passed as the two
3685          * parameters to the callback.
3686          * @param {String} title The title bar text
3687          * @param {String} msg The message box body text
3688          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3689          * @param {Object} scope (optional) The scope of the callback function
3690          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3691          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3692          * @return {Roo.MessageBox} This message box
3693          */
3694         prompt : function(title, msg, fn, scope, multiline){
3695             this.show({
3696                 title : title,
3697                 msg : msg,
3698                 buttons: this.OKCANCEL,
3699                 fn: fn,
3700                 minWidth:250,
3701                 scope : scope,
3702                 prompt:true,
3703                 multiline: multiline,
3704                 modal : true
3705             });
3706             return this;
3707         },
3708
3709         /**
3710          * Button config that displays a single OK button
3711          * @type Object
3712          */
3713         OK : {ok:true},
3714         /**
3715          * Button config that displays Yes and No buttons
3716          * @type Object
3717          */
3718         YESNO : {yes:true, no:true},
3719         /**
3720          * Button config that displays OK and Cancel buttons
3721          * @type Object
3722          */
3723         OKCANCEL : {ok:true, cancel:true},
3724         /**
3725          * Button config that displays Yes, No and Cancel buttons
3726          * @type Object
3727          */
3728         YESNOCANCEL : {yes:true, no:true, cancel:true},
3729
3730         /**
3731          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3732          * @type Number
3733          */
3734         defaultTextHeight : 75,
3735         /**
3736          * The maximum width in pixels of the message box (defaults to 600)
3737          * @type Number
3738          */
3739         maxWidth : 600,
3740         /**
3741          * The minimum width in pixels of the message box (defaults to 100)
3742          * @type Number
3743          */
3744         minWidth : 100,
3745         /**
3746          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3747          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3748          * @type Number
3749          */
3750         minProgressWidth : 250,
3751         /**
3752          * An object containing the default button text strings that can be overriden for localized language support.
3753          * Supported properties are: ok, cancel, yes and no.
3754          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3755          * @type Object
3756          */
3757         buttonText : {
3758             ok : "OK",
3759             cancel : "Cancel",
3760             yes : "Yes",
3761             no : "No"
3762         }
3763     };
3764 }();
3765
3766 /**
3767  * Shorthand for {@link Roo.MessageBox}
3768  */
3769 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3770 Roo.Msg = Roo.Msg || Roo.MessageBox;
3771 /*
3772  * - LGPL
3773  *
3774  * navbar
3775  * 
3776  */
3777
3778 /**
3779  * @class Roo.bootstrap.Navbar
3780  * @extends Roo.bootstrap.Component
3781  * Bootstrap Navbar class
3782
3783  * @constructor
3784  * Create a new Navbar
3785  * @param {Object} config The config object
3786  */
3787
3788
3789 Roo.bootstrap.Navbar = function(config){
3790     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3791     this.addEvents({
3792         // raw events
3793         /**
3794          * @event beforetoggle
3795          * Fire before toggle the menu
3796          * @param {Roo.EventObject} e
3797          */
3798         "beforetoggle" : true
3799     });
3800 };
3801
3802 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3803     
3804     
3805    
3806     // private
3807     navItems : false,
3808     loadMask : false,
3809     
3810     
3811     getAutoCreate : function(){
3812         
3813         
3814         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3815         
3816     },
3817     
3818     initEvents :function ()
3819     {
3820         //Roo.log(this.el.select('.navbar-toggle',true));
3821         this.el.select('.navbar-toggle',true).on('click', function() {
3822             if(this.fireEvent('beforetoggle', this) !== false){
3823                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3824             }
3825             
3826         }, this);
3827         
3828         var mark = {
3829             tag: "div",
3830             cls:"x-dlg-mask"
3831         };
3832         
3833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3834         
3835         var size = this.el.getSize();
3836         this.maskEl.setSize(size.width, size.height);
3837         this.maskEl.enableDisplayMode("block");
3838         this.maskEl.hide();
3839         
3840         if(this.loadMask){
3841             this.maskEl.show();
3842         }
3843     },
3844     
3845     
3846     getChildContainer : function()
3847     {
3848         if (this.el.select('.collapse').getCount()) {
3849             return this.el.select('.collapse',true).first();
3850         }
3851         
3852         return this.el;
3853     },
3854     
3855     mask : function()
3856     {
3857         this.maskEl.show();
3858     },
3859     
3860     unmask : function()
3861     {
3862         this.maskEl.hide();
3863     } 
3864     
3865     
3866     
3867     
3868 });
3869
3870
3871
3872  
3873
3874  /*
3875  * - LGPL
3876  *
3877  * navbar
3878  * 
3879  */
3880
3881 /**
3882  * @class Roo.bootstrap.NavSimplebar
3883  * @extends Roo.bootstrap.Navbar
3884  * Bootstrap Sidebar class
3885  *
3886  * @cfg {Boolean} inverse is inverted color
3887  * 
3888  * @cfg {String} type (nav | pills | tabs)
3889  * @cfg {Boolean} arrangement stacked | justified
3890  * @cfg {String} align (left | right) alignment
3891  * 
3892  * @cfg {Boolean} main (true|false) main nav bar? default false
3893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3894  * 
3895  * @cfg {String} tag (header|footer|nav|div) default is nav 
3896
3897  * 
3898  * 
3899  * 
3900  * @constructor
3901  * Create a new Sidebar
3902  * @param {Object} config The config object
3903  */
3904
3905
3906 Roo.bootstrap.NavSimplebar = function(config){
3907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3908 };
3909
3910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3911     
3912     inverse: false,
3913     
3914     type: false,
3915     arrangement: '',
3916     align : false,
3917     
3918     
3919     
3920     main : false,
3921     
3922     
3923     tag : false,
3924     
3925     
3926     getAutoCreate : function(){
3927         
3928         
3929         var cfg = {
3930             tag : this.tag || 'div',
3931             cls : 'navbar'
3932         };
3933           
3934         
3935         cfg.cn = [
3936             {
3937                 cls: 'nav',
3938                 tag : 'ul'
3939             }
3940         ];
3941         
3942          
3943         this.type = this.type || 'nav';
3944         if (['tabs','pills'].indexOf(this.type)!==-1) {
3945             cfg.cn[0].cls += ' nav-' + this.type
3946         
3947         
3948         } else {
3949             if (this.type!=='nav') {
3950                 Roo.log('nav type must be nav/tabs/pills')
3951             }
3952             cfg.cn[0].cls += ' navbar-nav'
3953         }
3954         
3955         
3956         
3957         
3958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3959             cfg.cn[0].cls += ' nav-' + this.arrangement;
3960         }
3961         
3962         
3963         if (this.align === 'right') {
3964             cfg.cn[0].cls += ' navbar-right';
3965         }
3966         
3967         if (this.inverse) {
3968             cfg.cls += ' navbar-inverse';
3969             
3970         }
3971         
3972         
3973         return cfg;
3974     
3975         
3976     }
3977     
3978     
3979     
3980 });
3981
3982
3983
3984  
3985
3986  
3987        /*
3988  * - LGPL
3989  *
3990  * navbar
3991  * navbar-fixed-top
3992  * navbar-expand-md  fixed-top 
3993  */
3994
3995 /**
3996  * @class Roo.bootstrap.NavHeaderbar
3997  * @extends Roo.bootstrap.NavSimplebar
3998  * Bootstrap Sidebar class
3999  *
4000  * @cfg {String} brand what is brand
4001  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4002  * @cfg {String} brand_href href of the brand
4003  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4004  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4005  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4006  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4007  * 
4008  * @constructor
4009  * Create a new Sidebar
4010  * @param {Object} config The config object
4011  */
4012
4013
4014 Roo.bootstrap.NavHeaderbar = function(config){
4015     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4016       
4017 };
4018
4019 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4020     
4021     position: '',
4022     brand: '',
4023     brand_href: false,
4024     srButton : true,
4025     autohide : false,
4026     desktopCenter : false,
4027    
4028     
4029     getAutoCreate : function(){
4030         
4031         var   cfg = {
4032             tag: this.nav || 'nav',
4033             cls: 'navbar navbar-expand-md',
4034             role: 'navigation',
4035             cn: []
4036         };
4037         
4038         var cn = cfg.cn;
4039         if (this.desktopCenter) {
4040             cn.push({cls : 'container', cn : []});
4041             cn = cn[0].cn;
4042         }
4043         
4044         if(this.srButton){
4045             cn.push({
4046                 tag: 'div',
4047                 cls: 'navbar-header',
4048                 cn: [
4049                     {
4050                         tag: 'button',
4051                         type: 'button',
4052                         cls: 'navbar-toggle navbar-toggler',
4053                         'data-toggle': 'collapse',
4054                         cn: [
4055                             {
4056                                 tag: 'span',
4057                                 cls: 'sr-only',
4058                                 html: 'Toggle navigation'
4059                             },
4060                             {
4061                                 tag: 'span',
4062                                 cls: 'icon-bar navbar-toggler-icon'
4063                             },
4064                             {
4065                                 tag: 'span',
4066                                 cls: 'icon-bar'
4067                             },
4068                             {
4069                                 tag: 'span',
4070                                 cls: 'icon-bar'
4071                             }
4072                         ]
4073                     }
4074                 ]
4075             });
4076         }
4077         
4078         cn.push({
4079             tag: 'div',
4080             cls: 'collapse navbar-collapse',
4081             cn : []
4082         });
4083         
4084         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4085         
4086         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4087             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4088             
4089             // tag can override this..
4090             
4091             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4092         }
4093         
4094         if (this.brand !== '') {
4095             cn[0].cn.push({
4096                 tag: 'a',
4097                 href: this.brand_href ? this.brand_href : '#',
4098                 cls: 'navbar-brand',
4099                 cn: [
4100                 this.brand
4101                 ]
4102             });
4103         }
4104         
4105         if(this.main){
4106             cfg.cls += ' main-nav';
4107         }
4108         
4109         
4110         return cfg;
4111
4112         
4113     },
4114     getHeaderChildContainer : function()
4115     {
4116         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4117             return this.el.select('.navbar-header',true).first();
4118         }
4119         
4120         return this.getChildContainer();
4121     },
4122     
4123     
4124     initEvents : function()
4125     {
4126         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4127         
4128         if (this.autohide) {
4129             
4130             var prevScroll = 0;
4131             var ft = this.el;
4132             
4133             Roo.get(document).on('scroll',function(e) {
4134                 var ns = Roo.get(document).getScroll().top;
4135                 var os = prevScroll;
4136                 prevScroll = ns;
4137                 
4138                 if(ns > os){
4139                     ft.removeClass('slideDown');
4140                     ft.addClass('slideUp');
4141                     return;
4142                 }
4143                 ft.removeClass('slideUp');
4144                 ft.addClass('slideDown');
4145                  
4146               
4147           },this);
4148         }
4149     }    
4150     
4151 });
4152
4153
4154
4155  
4156
4157  /*
4158  * - LGPL
4159  *
4160  * navbar
4161  * 
4162  */
4163
4164 /**
4165  * @class Roo.bootstrap.NavSidebar
4166  * @extends Roo.bootstrap.Navbar
4167  * Bootstrap Sidebar class
4168  * 
4169  * @constructor
4170  * Create a new Sidebar
4171  * @param {Object} config The config object
4172  */
4173
4174
4175 Roo.bootstrap.NavSidebar = function(config){
4176     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4177 };
4178
4179 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4180     
4181     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4182     
4183     getAutoCreate : function(){
4184         
4185         
4186         return  {
4187             tag: 'div',
4188             cls: 'sidebar sidebar-nav'
4189         };
4190     
4191         
4192     }
4193     
4194     
4195     
4196 });
4197
4198
4199
4200  
4201
4202  /*
4203  * - LGPL
4204  *
4205  * nav group
4206  * 
4207  */
4208
4209 /**
4210  * @class Roo.bootstrap.NavGroup
4211  * @extends Roo.bootstrap.Component
4212  * Bootstrap NavGroup class
4213  * @cfg {String} align (left|right)
4214  * @cfg {Boolean} inverse
4215  * @cfg {String} type (nav|pills|tab) default nav
4216  * @cfg {String} navId - reference Id for navbar.
4217
4218  * 
4219  * @constructor
4220  * Create a new nav group
4221  * @param {Object} config The config object
4222  */
4223
4224 Roo.bootstrap.NavGroup = function(config){
4225     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4226     this.navItems = [];
4227    
4228     Roo.bootstrap.NavGroup.register(this);
4229      this.addEvents({
4230         /**
4231              * @event changed
4232              * Fires when the active item changes
4233              * @param {Roo.bootstrap.NavGroup} this
4234              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4235              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4236          */
4237         'changed': true
4238      });
4239     
4240 };
4241
4242 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4243     
4244     align: '',
4245     inverse: false,
4246     form: false,
4247     type: 'nav',
4248     navId : '',
4249     // private
4250     
4251     navItems : false, 
4252     
4253     getAutoCreate : function()
4254     {
4255         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4256         
4257         cfg = {
4258             tag : 'ul',
4259             cls: 'nav' 
4260         };
4261         
4262         if (['tabs','pills'].indexOf(this.type)!==-1) {
4263             cfg.cls += ' nav-' + this.type
4264         } else {
4265             if (this.type!=='nav') {
4266                 Roo.log('nav type must be nav/tabs/pills')
4267             }
4268             cfg.cls += ' navbar-nav'
4269         }
4270         
4271         if (this.parent() && this.parent().sidebar) {
4272             cfg = {
4273                 tag: 'ul',
4274                 cls: 'dashboard-menu sidebar-menu'
4275             };
4276             
4277             return cfg;
4278         }
4279         
4280         if (this.form === true) {
4281             cfg = {
4282                 tag: 'form',
4283                 cls: 'navbar-form'
4284             };
4285             
4286             if (this.align === 'right') {
4287                 cfg.cls += ' navbar-right ml-md-auto';
4288             } else {
4289                 cfg.cls += ' navbar-left';
4290             }
4291         }
4292         
4293         if (this.align === 'right') {
4294             cfg.cls += ' navbar-right ml-md-auto';
4295         } else {
4296             cfg.cls += ' mr-auto';
4297         }
4298         
4299         if (this.inverse) {
4300             cfg.cls += ' navbar-inverse';
4301             
4302         }
4303         
4304         
4305         return cfg;
4306     },
4307     /**
4308     * sets the active Navigation item
4309     * @param {Roo.bootstrap.NavItem} the new current navitem
4310     */
4311     setActiveItem : function(item)
4312     {
4313         var prev = false;
4314         Roo.each(this.navItems, function(v){
4315             if (v == item) {
4316                 return ;
4317             }
4318             if (v.isActive()) {
4319                 v.setActive(false, true);
4320                 prev = v;
4321                 
4322             }
4323             
4324         });
4325
4326         item.setActive(true, true);
4327         this.fireEvent('changed', this, item, prev);
4328         
4329         
4330     },
4331     /**
4332     * gets the active Navigation item
4333     * @return {Roo.bootstrap.NavItem} the current navitem
4334     */
4335     getActive : function()
4336     {
4337         
4338         var prev = false;
4339         Roo.each(this.navItems, function(v){
4340             
4341             if (v.isActive()) {
4342                 prev = v;
4343                 
4344             }
4345             
4346         });
4347         return prev;
4348     },
4349     
4350     indexOfNav : function()
4351     {
4352         
4353         var prev = false;
4354         Roo.each(this.navItems, function(v,i){
4355             
4356             if (v.isActive()) {
4357                 prev = i;
4358                 
4359             }
4360             
4361         });
4362         return prev;
4363     },
4364     /**
4365     * adds a Navigation item
4366     * @param {Roo.bootstrap.NavItem} the navitem to add
4367     */
4368     addItem : function(cfg)
4369     {
4370         var cn = new Roo.bootstrap.NavItem(cfg);
4371         this.register(cn);
4372         cn.parentId = this.id;
4373         cn.onRender(this.el, null);
4374         return cn;
4375     },
4376     /**
4377     * register a Navigation item
4378     * @param {Roo.bootstrap.NavItem} the navitem to add
4379     */
4380     register : function(item)
4381     {
4382         this.navItems.push( item);
4383         item.navId = this.navId;
4384     
4385     },
4386     
4387     /**
4388     * clear all the Navigation item
4389     */
4390    
4391     clearAll : function()
4392     {
4393         this.navItems = [];
4394         this.el.dom.innerHTML = '';
4395     },
4396     
4397     getNavItem: function(tabId)
4398     {
4399         var ret = false;
4400         Roo.each(this.navItems, function(e) {
4401             if (e.tabId == tabId) {
4402                ret =  e;
4403                return false;
4404             }
4405             return true;
4406             
4407         });
4408         return ret;
4409     },
4410     
4411     setActiveNext : function()
4412     {
4413         var i = this.indexOfNav(this.getActive());
4414         if (i > this.navItems.length) {
4415             return;
4416         }
4417         this.setActiveItem(this.navItems[i+1]);
4418     },
4419     setActivePrev : function()
4420     {
4421         var i = this.indexOfNav(this.getActive());
4422         if (i  < 1) {
4423             return;
4424         }
4425         this.setActiveItem(this.navItems[i-1]);
4426     },
4427     clearWasActive : function(except) {
4428         Roo.each(this.navItems, function(e) {
4429             if (e.tabId != except.tabId && e.was_active) {
4430                e.was_active = false;
4431                return false;
4432             }
4433             return true;
4434             
4435         });
4436     },
4437     getWasActive : function ()
4438     {
4439         var r = false;
4440         Roo.each(this.navItems, function(e) {
4441             if (e.was_active) {
4442                r = e;
4443                return false;
4444             }
4445             return true;
4446             
4447         });
4448         return r;
4449     }
4450     
4451     
4452 });
4453
4454  
4455 Roo.apply(Roo.bootstrap.NavGroup, {
4456     
4457     groups: {},
4458      /**
4459     * register a Navigation Group
4460     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4461     */
4462     register : function(navgrp)
4463     {
4464         this.groups[navgrp.navId] = navgrp;
4465         
4466     },
4467     /**
4468     * fetch a Navigation Group based on the navigation ID
4469     * @param {string} the navgroup to add
4470     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4471     */
4472     get: function(navId) {
4473         if (typeof(this.groups[navId]) == 'undefined') {
4474             return false;
4475             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4476         }
4477         return this.groups[navId] ;
4478     }
4479     
4480     
4481     
4482 });
4483
4484  /*
4485  * - LGPL
4486  *
4487  * row
4488  * 
4489  */
4490
4491 /**
4492  * @class Roo.bootstrap.NavItem
4493  * @extends Roo.bootstrap.Component
4494  * Bootstrap Navbar.NavItem class
4495  * @cfg {String} href  link to
4496  * @cfg {String} html content of button
4497  * @cfg {String} badge text inside badge
4498  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4499  * @cfg {String} glyphicon name of glyphicon
4500  * @cfg {String} icon name of font awesome icon
4501  * @cfg {Boolean} active Is item active
4502  * @cfg {Boolean} disabled Is item disabled
4503  
4504  * @cfg {Boolean} preventDefault (true | false) default false
4505  * @cfg {String} tabId the tab that this item activates.
4506  * @cfg {String} tagtype (a|span) render as a href or span?
4507  * @cfg {Boolean} animateRef (true|false) link to element default false  
4508   
4509  * @constructor
4510  * Create a new Navbar Item
4511  * @param {Object} config The config object
4512  */
4513 Roo.bootstrap.NavItem = function(config){
4514     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4515     this.addEvents({
4516         // raw events
4517         /**
4518          * @event click
4519          * The raw click event for the entire grid.
4520          * @param {Roo.EventObject} e
4521          */
4522         "click" : true,
4523          /**
4524             * @event changed
4525             * Fires when the active item active state changes
4526             * @param {Roo.bootstrap.NavItem} this
4527             * @param {boolean} state the new state
4528              
4529          */
4530         'changed': true,
4531         /**
4532             * @event scrollto
4533             * Fires when scroll to element
4534             * @param {Roo.bootstrap.NavItem} this
4535             * @param {Object} options
4536             * @param {Roo.EventObject} e
4537              
4538          */
4539         'scrollto': true
4540     });
4541    
4542 };
4543
4544 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4545     
4546     href: false,
4547     html: '',
4548     badge: '',
4549     icon: false,
4550     glyphicon: false,
4551     active: false,
4552     preventDefault : false,
4553     tabId : false,
4554     tagtype : 'a',
4555     disabled : false,
4556     animateRef : false,
4557     was_active : false,
4558     
4559     getAutoCreate : function(){
4560          
4561         var cfg = {
4562             tag: 'li',
4563             cls: 'nav-item'
4564             
4565         };
4566         
4567         if (this.active) {
4568             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4569         }
4570         if (this.disabled) {
4571             cfg.cls += ' disabled';
4572         }
4573         
4574         if (this.href || this.html || this.glyphicon || this.icon) {
4575             cfg.cn = [
4576                 {
4577                     tag: this.tagtype,
4578                     href : this.href || "#",
4579                     html: this.html || ''
4580                 }
4581             ];
4582             if (this.tagtype == 'a') {
4583                 cfg.cn[0].cls = 'nav-link';
4584             }
4585             if (this.icon) {
4586                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4587             }
4588
4589             if(this.glyphicon) {
4590                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4591             }
4592             
4593             if (this.menu) {
4594                 
4595                 cfg.cn[0].html += " <span class='caret'></span>";
4596              
4597             }
4598             
4599             if (this.badge !== '') {
4600                  
4601                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4602             }
4603         }
4604         
4605         
4606         
4607         return cfg;
4608     },
4609     initEvents: function() 
4610     {
4611         if (typeof (this.menu) != 'undefined') {
4612             this.menu.parentType = this.xtype;
4613             this.menu.triggerEl = this.el;
4614             this.menu = this.addxtype(Roo.apply({}, this.menu));
4615         }
4616         
4617         this.el.select('a',true).on('click', this.onClick, this);
4618         
4619         if(this.tagtype == 'span'){
4620             this.el.select('span',true).on('click', this.onClick, this);
4621         }
4622        
4623         // at this point parent should be available..
4624         this.parent().register(this);
4625     },
4626     
4627     onClick : function(e)
4628     {
4629         if (e.getTarget('.dropdown-menu-item')) {
4630             // did you click on a menu itemm.... - then don't trigger onclick..
4631             return;
4632         }
4633         
4634         if(
4635                 this.preventDefault || 
4636                 this.href == '#' 
4637         ){
4638             Roo.log("NavItem - prevent Default?");
4639             e.preventDefault();
4640         }
4641         
4642         if (this.disabled) {
4643             return;
4644         }
4645         
4646         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4647         if (tg && tg.transition) {
4648             Roo.log("waiting for the transitionend");
4649             return;
4650         }
4651         
4652         
4653         
4654         //Roo.log("fire event clicked");
4655         if(this.fireEvent('click', this, e) === false){
4656             return;
4657         };
4658         
4659         if(this.tagtype == 'span'){
4660             return;
4661         }
4662         
4663         //Roo.log(this.href);
4664         var ael = this.el.select('a',true).first();
4665         //Roo.log(ael);
4666         
4667         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4668             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4669             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4670                 return; // ignore... - it's a 'hash' to another page.
4671             }
4672             Roo.log("NavItem - prevent Default?");
4673             e.preventDefault();
4674             this.scrollToElement(e);
4675         }
4676         
4677         
4678         var p =  this.parent();
4679    
4680         if (['tabs','pills'].indexOf(p.type)!==-1) {
4681             if (typeof(p.setActiveItem) !== 'undefined') {
4682                 p.setActiveItem(this);
4683             }
4684         }
4685         
4686         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4687         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4688             // remove the collapsed menu expand...
4689             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4690         }
4691     },
4692     
4693     isActive: function () {
4694         return this.active
4695     },
4696     setActive : function(state, fire, is_was_active)
4697     {
4698         if (this.active && !state && this.navId) {
4699             this.was_active = true;
4700             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4701             if (nv) {
4702                 nv.clearWasActive(this);
4703             }
4704             
4705         }
4706         this.active = state;
4707         
4708         if (!state ) {
4709             this.el.removeClass('active');
4710         } else if (!this.el.hasClass('active')) {
4711             this.el.addClass('active');
4712         }
4713         if (fire) {
4714             this.fireEvent('changed', this, state);
4715         }
4716         
4717         // show a panel if it's registered and related..
4718         
4719         if (!this.navId || !this.tabId || !state || is_was_active) {
4720             return;
4721         }
4722         
4723         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4724         if (!tg) {
4725             return;
4726         }
4727         var pan = tg.getPanelByName(this.tabId);
4728         if (!pan) {
4729             return;
4730         }
4731         // if we can not flip to new panel - go back to old nav highlight..
4732         if (false == tg.showPanel(pan)) {
4733             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4734             if (nv) {
4735                 var onav = nv.getWasActive();
4736                 if (onav) {
4737                     onav.setActive(true, false, true);
4738                 }
4739             }
4740             
4741         }
4742         
4743         
4744         
4745     },
4746      // this should not be here...
4747     setDisabled : function(state)
4748     {
4749         this.disabled = state;
4750         if (!state ) {
4751             this.el.removeClass('disabled');
4752         } else if (!this.el.hasClass('disabled')) {
4753             this.el.addClass('disabled');
4754         }
4755         
4756     },
4757     
4758     /**
4759      * Fetch the element to display the tooltip on.
4760      * @return {Roo.Element} defaults to this.el
4761      */
4762     tooltipEl : function()
4763     {
4764         return this.el.select('' + this.tagtype + '', true).first();
4765     },
4766     
4767     scrollToElement : function(e)
4768     {
4769         var c = document.body;
4770         
4771         /*
4772          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4773          */
4774         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4775             c = document.documentElement;
4776         }
4777         
4778         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4779         
4780         if(!target){
4781             return;
4782         }
4783
4784         var o = target.calcOffsetsTo(c);
4785         
4786         var options = {
4787             target : target,
4788             value : o[1]
4789         };
4790         
4791         this.fireEvent('scrollto', this, options, e);
4792         
4793         Roo.get(c).scrollTo('top', options.value, true);
4794         
4795         return;
4796     }
4797 });
4798  
4799
4800  /*
4801  * - LGPL
4802  *
4803  * sidebar item
4804  *
4805  *  li
4806  *    <span> icon </span>
4807  *    <span> text </span>
4808  *    <span>badge </span>
4809  */
4810
4811 /**
4812  * @class Roo.bootstrap.NavSidebarItem
4813  * @extends Roo.bootstrap.NavItem
4814  * Bootstrap Navbar.NavSidebarItem class
4815  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4816  * {Boolean} open is the menu open
4817  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4818  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4819  * {String} buttonSize (sm|md|lg)the extra classes for the button
4820  * {Boolean} showArrow show arrow next to the text (default true)
4821  * @constructor
4822  * Create a new Navbar Button
4823  * @param {Object} config The config object
4824  */
4825 Roo.bootstrap.NavSidebarItem = function(config){
4826     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4827     this.addEvents({
4828         // raw events
4829         /**
4830          * @event click
4831          * The raw click event for the entire grid.
4832          * @param {Roo.EventObject} e
4833          */
4834         "click" : true,
4835          /**
4836             * @event changed
4837             * Fires when the active item active state changes
4838             * @param {Roo.bootstrap.NavSidebarItem} this
4839             * @param {boolean} state the new state
4840              
4841          */
4842         'changed': true
4843     });
4844    
4845 };
4846
4847 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4848     
4849     badgeWeight : 'default',
4850     
4851     open: false,
4852     
4853     buttonView : false,
4854     
4855     buttonWeight : 'default',
4856     
4857     buttonSize : 'md',
4858     
4859     showArrow : true,
4860     
4861     getAutoCreate : function(){
4862         
4863         
4864         var a = {
4865                 tag: 'a',
4866                 href : this.href || '#',
4867                 cls: '',
4868                 html : '',
4869                 cn : []
4870         };
4871         
4872         if(this.buttonView){
4873             a = {
4874                 tag: 'button',
4875                 href : this.href || '#',
4876                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4877                 html : this.html,
4878                 cn : []
4879             };
4880         }
4881         
4882         var cfg = {
4883             tag: 'li',
4884             cls: '',
4885             cn: [ a ]
4886         };
4887         
4888         if (this.active) {
4889             cfg.cls += ' active';
4890         }
4891         
4892         if (this.disabled) {
4893             cfg.cls += ' disabled';
4894         }
4895         if (this.open) {
4896             cfg.cls += ' open x-open';
4897         }
4898         // left icon..
4899         if (this.glyphicon || this.icon) {
4900             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4901             a.cn.push({ tag : 'i', cls : c }) ;
4902         }
4903         
4904         if(!this.buttonView){
4905             var span = {
4906                 tag: 'span',
4907                 html : this.html || ''
4908             };
4909
4910             a.cn.push(span);
4911             
4912         }
4913         
4914         if (this.badge !== '') {
4915             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4916         }
4917         
4918         if (this.menu) {
4919             
4920             if(this.showArrow){
4921                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4922             }
4923             
4924             a.cls += ' dropdown-toggle treeview' ;
4925         }
4926         
4927         return cfg;
4928     },
4929     
4930     initEvents : function()
4931     { 
4932         if (typeof (this.menu) != 'undefined') {
4933             this.menu.parentType = this.xtype;
4934             this.menu.triggerEl = this.el;
4935             this.menu = this.addxtype(Roo.apply({}, this.menu));
4936         }
4937         
4938         this.el.on('click', this.onClick, this);
4939         
4940         if(this.badge !== ''){
4941             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4942         }
4943         
4944     },
4945     
4946     onClick : function(e)
4947     {
4948         if(this.disabled){
4949             e.preventDefault();
4950             return;
4951         }
4952         
4953         if(this.preventDefault){
4954             e.preventDefault();
4955         }
4956         
4957         this.fireEvent('click', this);
4958     },
4959     
4960     disable : function()
4961     {
4962         this.setDisabled(true);
4963     },
4964     
4965     enable : function()
4966     {
4967         this.setDisabled(false);
4968     },
4969     
4970     setDisabled : function(state)
4971     {
4972         if(this.disabled == state){
4973             return;
4974         }
4975         
4976         this.disabled = state;
4977         
4978         if (state) {
4979             this.el.addClass('disabled');
4980             return;
4981         }
4982         
4983         this.el.removeClass('disabled');
4984         
4985         return;
4986     },
4987     
4988     setActive : function(state)
4989     {
4990         if(this.active == state){
4991             return;
4992         }
4993         
4994         this.active = state;
4995         
4996         if (state) {
4997             this.el.addClass('active');
4998             return;
4999         }
5000         
5001         this.el.removeClass('active');
5002         
5003         return;
5004     },
5005     
5006     isActive: function () 
5007     {
5008         return this.active;
5009     },
5010     
5011     setBadge : function(str)
5012     {
5013         if(!this.badgeEl){
5014             return;
5015         }
5016         
5017         this.badgeEl.dom.innerHTML = str;
5018     }
5019     
5020    
5021      
5022  
5023 });
5024  
5025
5026  /*
5027  * - LGPL
5028  *
5029  * row
5030  * 
5031  */
5032
5033 /**
5034  * @class Roo.bootstrap.Row
5035  * @extends Roo.bootstrap.Component
5036  * Bootstrap Row class (contains columns...)
5037  * 
5038  * @constructor
5039  * Create a new Row
5040  * @param {Object} config The config object
5041  */
5042
5043 Roo.bootstrap.Row = function(config){
5044     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5045 };
5046
5047 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5048     
5049     getAutoCreate : function(){
5050        return {
5051             cls: 'row clearfix'
5052        };
5053     }
5054     
5055     
5056 });
5057
5058  
5059
5060  /*
5061  * - LGPL
5062  *
5063  * element
5064  * 
5065  */
5066
5067 /**
5068  * @class Roo.bootstrap.Element
5069  * @extends Roo.bootstrap.Component
5070  * Bootstrap Element class
5071  * @cfg {String} html contents of the element
5072  * @cfg {String} tag tag of the element
5073  * @cfg {String} cls class of the element
5074  * @cfg {Boolean} preventDefault (true|false) default false
5075  * @cfg {Boolean} clickable (true|false) default false
5076  * 
5077  * @constructor
5078  * Create a new Element
5079  * @param {Object} config The config object
5080  */
5081
5082 Roo.bootstrap.Element = function(config){
5083     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5084     
5085     this.addEvents({
5086         // raw events
5087         /**
5088          * @event click
5089          * When a element is chick
5090          * @param {Roo.bootstrap.Element} this
5091          * @param {Roo.EventObject} e
5092          */
5093         "click" : true
5094     });
5095 };
5096
5097 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5098     
5099     tag: 'div',
5100     cls: '',
5101     html: '',
5102     preventDefault: false, 
5103     clickable: false,
5104     
5105     getAutoCreate : function(){
5106         
5107         var cfg = {
5108             tag: this.tag,
5109             // cls: this.cls, double assign in parent class Component.js :: onRender
5110             html: this.html
5111         };
5112         
5113         return cfg;
5114     },
5115     
5116     initEvents: function() 
5117     {
5118         Roo.bootstrap.Element.superclass.initEvents.call(this);
5119         
5120         if(this.clickable){
5121             this.el.on('click', this.onClick, this);
5122         }
5123         
5124     },
5125     
5126     onClick : function(e)
5127     {
5128         if(this.preventDefault){
5129             e.preventDefault();
5130         }
5131         
5132         this.fireEvent('click', this, e);
5133     },
5134     
5135     getValue : function()
5136     {
5137         return this.el.dom.innerHTML;
5138     },
5139     
5140     setValue : function(value)
5141     {
5142         this.el.dom.innerHTML = value;
5143     }
5144    
5145 });
5146
5147  
5148
5149  /*
5150  * - LGPL
5151  *
5152  * pagination
5153  * 
5154  */
5155
5156 /**
5157  * @class Roo.bootstrap.Pagination
5158  * @extends Roo.bootstrap.Component
5159  * Bootstrap Pagination class
5160  * @cfg {String} size xs | sm | md | lg
5161  * @cfg {Boolean} inverse false | true
5162  * 
5163  * @constructor
5164  * Create a new Pagination
5165  * @param {Object} config The config object
5166  */
5167
5168 Roo.bootstrap.Pagination = function(config){
5169     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5170 };
5171
5172 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5173     
5174     cls: false,
5175     size: false,
5176     inverse: false,
5177     
5178     getAutoCreate : function(){
5179         var cfg = {
5180             tag: 'ul',
5181                 cls: 'pagination'
5182         };
5183         if (this.inverse) {
5184             cfg.cls += ' inverse';
5185         }
5186         if (this.html) {
5187             cfg.html=this.html;
5188         }
5189         if (this.cls) {
5190             cfg.cls += " " + this.cls;
5191         }
5192         return cfg;
5193     }
5194    
5195 });
5196
5197  
5198
5199  /*
5200  * - LGPL
5201  *
5202  * Pagination item
5203  * 
5204  */
5205
5206
5207 /**
5208  * @class Roo.bootstrap.PaginationItem
5209  * @extends Roo.bootstrap.Component
5210  * Bootstrap PaginationItem class
5211  * @cfg {String} html text
5212  * @cfg {String} href the link
5213  * @cfg {Boolean} preventDefault (true | false) default true
5214  * @cfg {Boolean} active (true | false) default false
5215  * @cfg {Boolean} disabled default false
5216  * 
5217  * 
5218  * @constructor
5219  * Create a new PaginationItem
5220  * @param {Object} config The config object
5221  */
5222
5223
5224 Roo.bootstrap.PaginationItem = function(config){
5225     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5226     this.addEvents({
5227         // raw events
5228         /**
5229          * @event click
5230          * The raw click event for the entire grid.
5231          * @param {Roo.EventObject} e
5232          */
5233         "click" : true
5234     });
5235 };
5236
5237 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5238     
5239     href : false,
5240     html : false,
5241     preventDefault: true,
5242     active : false,
5243     cls : false,
5244     disabled: false,
5245     
5246     getAutoCreate : function(){
5247         var cfg= {
5248             tag: 'li',
5249             cn: [
5250                 {
5251                     tag : 'a',
5252                     href : this.href ? this.href : '#',
5253                     html : this.html ? this.html : ''
5254                 }
5255             ]
5256         };
5257         
5258         if(this.cls){
5259             cfg.cls = this.cls;
5260         }
5261         
5262         if(this.disabled){
5263             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5264         }
5265         
5266         if(this.active){
5267             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5268         }
5269         
5270         return cfg;
5271     },
5272     
5273     initEvents: function() {
5274         
5275         this.el.on('click', this.onClick, this);
5276         
5277     },
5278     onClick : function(e)
5279     {
5280         Roo.log('PaginationItem on click ');
5281         if(this.preventDefault){
5282             e.preventDefault();
5283         }
5284         
5285         if(this.disabled){
5286             return;
5287         }
5288         
5289         this.fireEvent('click', this, e);
5290     }
5291    
5292 });
5293
5294  
5295
5296  /*
5297  * - LGPL
5298  *
5299  * slider
5300  * 
5301  */
5302
5303
5304 /**
5305  * @class Roo.bootstrap.Slider
5306  * @extends Roo.bootstrap.Component
5307  * Bootstrap Slider class
5308  *    
5309  * @constructor
5310  * Create a new Slider
5311  * @param {Object} config The config object
5312  */
5313
5314 Roo.bootstrap.Slider = function(config){
5315     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5316 };
5317
5318 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5319     
5320     getAutoCreate : function(){
5321         
5322         var cfg = {
5323             tag: 'div',
5324             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5325             cn: [
5326                 {
5327                     tag: 'a',
5328                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5329                 }
5330             ]
5331         };
5332         
5333         return cfg;
5334     }
5335    
5336 });
5337
5338  /*
5339  * Based on:
5340  * Ext JS Library 1.1.1
5341  * Copyright(c) 2006-2007, Ext JS, LLC.
5342  *
5343  * Originally Released Under LGPL - original licence link has changed is not relivant.
5344  *
5345  * Fork - LGPL
5346  * <script type="text/javascript">
5347  */
5348  
5349
5350 /**
5351  * @class Roo.grid.ColumnModel
5352  * @extends Roo.util.Observable
5353  * This is the default implementation of a ColumnModel used by the Grid. It defines
5354  * the columns in the grid.
5355  * <br>Usage:<br>
5356  <pre><code>
5357  var colModel = new Roo.grid.ColumnModel([
5358         {header: "Ticker", width: 60, sortable: true, locked: true},
5359         {header: "Company Name", width: 150, sortable: true},
5360         {header: "Market Cap.", width: 100, sortable: true},
5361         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5362         {header: "Employees", width: 100, sortable: true, resizable: false}
5363  ]);
5364  </code></pre>
5365  * <p>
5366  
5367  * The config options listed for this class are options which may appear in each
5368  * individual column definition.
5369  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5370  * @constructor
5371  * @param {Object} config An Array of column config objects. See this class's
5372  * config objects for details.
5373 */
5374 Roo.grid.ColumnModel = function(config){
5375         /**
5376      * The config passed into the constructor
5377      */
5378     this.config = config;
5379     this.lookup = {};
5380
5381     // if no id, create one
5382     // if the column does not have a dataIndex mapping,
5383     // map it to the order it is in the config
5384     for(var i = 0, len = config.length; i < len; i++){
5385         var c = config[i];
5386         if(typeof c.dataIndex == "undefined"){
5387             c.dataIndex = i;
5388         }
5389         if(typeof c.renderer == "string"){
5390             c.renderer = Roo.util.Format[c.renderer];
5391         }
5392         if(typeof c.id == "undefined"){
5393             c.id = Roo.id();
5394         }
5395         if(c.editor && c.editor.xtype){
5396             c.editor  = Roo.factory(c.editor, Roo.grid);
5397         }
5398         if(c.editor && c.editor.isFormField){
5399             c.editor = new Roo.grid.GridEditor(c.editor);
5400         }
5401         this.lookup[c.id] = c;
5402     }
5403
5404     /**
5405      * The width of columns which have no width specified (defaults to 100)
5406      * @type Number
5407      */
5408     this.defaultWidth = 100;
5409
5410     /**
5411      * Default sortable of columns which have no sortable specified (defaults to false)
5412      * @type Boolean
5413      */
5414     this.defaultSortable = false;
5415
5416     this.addEvents({
5417         /**
5418              * @event widthchange
5419              * Fires when the width of a column changes.
5420              * @param {ColumnModel} this
5421              * @param {Number} columnIndex The column index
5422              * @param {Number} newWidth The new width
5423              */
5424             "widthchange": true,
5425         /**
5426              * @event headerchange
5427              * Fires when the text of a header changes.
5428              * @param {ColumnModel} this
5429              * @param {Number} columnIndex The column index
5430              * @param {Number} newText The new header text
5431              */
5432             "headerchange": true,
5433         /**
5434              * @event hiddenchange
5435              * Fires when a column is hidden or "unhidden".
5436              * @param {ColumnModel} this
5437              * @param {Number} columnIndex The column index
5438              * @param {Boolean} hidden true if hidden, false otherwise
5439              */
5440             "hiddenchange": true,
5441             /**
5442          * @event columnmoved
5443          * Fires when a column is moved.
5444          * @param {ColumnModel} this
5445          * @param {Number} oldIndex
5446          * @param {Number} newIndex
5447          */
5448         "columnmoved" : true,
5449         /**
5450          * @event columlockchange
5451          * Fires when a column's locked state is changed
5452          * @param {ColumnModel} this
5453          * @param {Number} colIndex
5454          * @param {Boolean} locked true if locked
5455          */
5456         "columnlockchange" : true
5457     });
5458     Roo.grid.ColumnModel.superclass.constructor.call(this);
5459 };
5460 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5461     /**
5462      * @cfg {String} header The header text to display in the Grid view.
5463      */
5464     /**
5465      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5466      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5467      * specified, the column's index is used as an index into the Record's data Array.
5468      */
5469     /**
5470      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5471      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5472      */
5473     /**
5474      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5475      * Defaults to the value of the {@link #defaultSortable} property.
5476      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5477      */
5478     /**
5479      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5480      */
5481     /**
5482      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5483      */
5484     /**
5485      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5486      */
5487     /**
5488      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5489      */
5490     /**
5491      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5492      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5493      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5494      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5495      */
5496        /**
5497      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5498      */
5499     /**
5500      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5501      */
5502     /**
5503      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5504      */
5505     /**
5506      * @cfg {String} cursor (Optional)
5507      */
5508     /**
5509      * @cfg {String} tooltip (Optional)
5510      */
5511     /**
5512      * @cfg {Number} xs (Optional)
5513      */
5514     /**
5515      * @cfg {Number} sm (Optional)
5516      */
5517     /**
5518      * @cfg {Number} md (Optional)
5519      */
5520     /**
5521      * @cfg {Number} lg (Optional)
5522      */
5523     /**
5524      * Returns the id of the column at the specified index.
5525      * @param {Number} index The column index
5526      * @return {String} the id
5527      */
5528     getColumnId : function(index){
5529         return this.config[index].id;
5530     },
5531
5532     /**
5533      * Returns the column for a specified id.
5534      * @param {String} id The column id
5535      * @return {Object} the column
5536      */
5537     getColumnById : function(id){
5538         return this.lookup[id];
5539     },
5540
5541     
5542     /**
5543      * Returns the column for a specified dataIndex.
5544      * @param {String} dataIndex The column dataIndex
5545      * @return {Object|Boolean} the column or false if not found
5546      */
5547     getColumnByDataIndex: function(dataIndex){
5548         var index = this.findColumnIndex(dataIndex);
5549         return index > -1 ? this.config[index] : false;
5550     },
5551     
5552     /**
5553      * Returns the index for a specified column id.
5554      * @param {String} id The column id
5555      * @return {Number} the index, or -1 if not found
5556      */
5557     getIndexById : function(id){
5558         for(var i = 0, len = this.config.length; i < len; i++){
5559             if(this.config[i].id == id){
5560                 return i;
5561             }
5562         }
5563         return -1;
5564     },
5565     
5566     /**
5567      * Returns the index for a specified column dataIndex.
5568      * @param {String} dataIndex The column dataIndex
5569      * @return {Number} the index, or -1 if not found
5570      */
5571     
5572     findColumnIndex : function(dataIndex){
5573         for(var i = 0, len = this.config.length; i < len; i++){
5574             if(this.config[i].dataIndex == dataIndex){
5575                 return i;
5576             }
5577         }
5578         return -1;
5579     },
5580     
5581     
5582     moveColumn : function(oldIndex, newIndex){
5583         var c = this.config[oldIndex];
5584         this.config.splice(oldIndex, 1);
5585         this.config.splice(newIndex, 0, c);
5586         this.dataMap = null;
5587         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5588     },
5589
5590     isLocked : function(colIndex){
5591         return this.config[colIndex].locked === true;
5592     },
5593
5594     setLocked : function(colIndex, value, suppressEvent){
5595         if(this.isLocked(colIndex) == value){
5596             return;
5597         }
5598         this.config[colIndex].locked = value;
5599         if(!suppressEvent){
5600             this.fireEvent("columnlockchange", this, colIndex, value);
5601         }
5602     },
5603
5604     getTotalLockedWidth : function(){
5605         var totalWidth = 0;
5606         for(var i = 0; i < this.config.length; i++){
5607             if(this.isLocked(i) && !this.isHidden(i)){
5608                 this.totalWidth += this.getColumnWidth(i);
5609             }
5610         }
5611         return totalWidth;
5612     },
5613
5614     getLockedCount : function(){
5615         for(var i = 0, len = this.config.length; i < len; i++){
5616             if(!this.isLocked(i)){
5617                 return i;
5618             }
5619         }
5620         
5621         return this.config.length;
5622     },
5623
5624     /**
5625      * Returns the number of columns.
5626      * @return {Number}
5627      */
5628     getColumnCount : function(visibleOnly){
5629         if(visibleOnly === true){
5630             var c = 0;
5631             for(var i = 0, len = this.config.length; i < len; i++){
5632                 if(!this.isHidden(i)){
5633                     c++;
5634                 }
5635             }
5636             return c;
5637         }
5638         return this.config.length;
5639     },
5640
5641     /**
5642      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5643      * @param {Function} fn
5644      * @param {Object} scope (optional)
5645      * @return {Array} result
5646      */
5647     getColumnsBy : function(fn, scope){
5648         var r = [];
5649         for(var i = 0, len = this.config.length; i < len; i++){
5650             var c = this.config[i];
5651             if(fn.call(scope||this, c, i) === true){
5652                 r[r.length] = c;
5653             }
5654         }
5655         return r;
5656     },
5657
5658     /**
5659      * Returns true if the specified column is sortable.
5660      * @param {Number} col The column index
5661      * @return {Boolean}
5662      */
5663     isSortable : function(col){
5664         if(typeof this.config[col].sortable == "undefined"){
5665             return this.defaultSortable;
5666         }
5667         return this.config[col].sortable;
5668     },
5669
5670     /**
5671      * Returns the rendering (formatting) function defined for the column.
5672      * @param {Number} col The column index.
5673      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5674      */
5675     getRenderer : function(col){
5676         if(!this.config[col].renderer){
5677             return Roo.grid.ColumnModel.defaultRenderer;
5678         }
5679         return this.config[col].renderer;
5680     },
5681
5682     /**
5683      * Sets the rendering (formatting) function for a column.
5684      * @param {Number} col The column index
5685      * @param {Function} fn The function to use to process the cell's raw data
5686      * to return HTML markup for the grid view. The render function is called with
5687      * the following parameters:<ul>
5688      * <li>Data value.</li>
5689      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5690      * <li>css A CSS style string to apply to the table cell.</li>
5691      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5692      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5693      * <li>Row index</li>
5694      * <li>Column index</li>
5695      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5696      */
5697     setRenderer : function(col, fn){
5698         this.config[col].renderer = fn;
5699     },
5700
5701     /**
5702      * Returns the width for the specified column.
5703      * @param {Number} col The column index
5704      * @return {Number}
5705      */
5706     getColumnWidth : function(col){
5707         return this.config[col].width * 1 || this.defaultWidth;
5708     },
5709
5710     /**
5711      * Sets the width for a column.
5712      * @param {Number} col The column index
5713      * @param {Number} width The new width
5714      */
5715     setColumnWidth : function(col, width, suppressEvent){
5716         this.config[col].width = width;
5717         this.totalWidth = null;
5718         if(!suppressEvent){
5719              this.fireEvent("widthchange", this, col, width);
5720         }
5721     },
5722
5723     /**
5724      * Returns the total width of all columns.
5725      * @param {Boolean} includeHidden True to include hidden column widths
5726      * @return {Number}
5727      */
5728     getTotalWidth : function(includeHidden){
5729         if(!this.totalWidth){
5730             this.totalWidth = 0;
5731             for(var i = 0, len = this.config.length; i < len; i++){
5732                 if(includeHidden || !this.isHidden(i)){
5733                     this.totalWidth += this.getColumnWidth(i);
5734                 }
5735             }
5736         }
5737         return this.totalWidth;
5738     },
5739
5740     /**
5741      * Returns the header for the specified column.
5742      * @param {Number} col The column index
5743      * @return {String}
5744      */
5745     getColumnHeader : function(col){
5746         return this.config[col].header;
5747     },
5748
5749     /**
5750      * Sets the header for a column.
5751      * @param {Number} col The column index
5752      * @param {String} header The new header
5753      */
5754     setColumnHeader : function(col, header){
5755         this.config[col].header = header;
5756         this.fireEvent("headerchange", this, col, header);
5757     },
5758
5759     /**
5760      * Returns the tooltip for the specified column.
5761      * @param {Number} col The column index
5762      * @return {String}
5763      */
5764     getColumnTooltip : function(col){
5765             return this.config[col].tooltip;
5766     },
5767     /**
5768      * Sets the tooltip for a column.
5769      * @param {Number} col The column index
5770      * @param {String} tooltip The new tooltip
5771      */
5772     setColumnTooltip : function(col, tooltip){
5773             this.config[col].tooltip = tooltip;
5774     },
5775
5776     /**
5777      * Returns the dataIndex for the specified column.
5778      * @param {Number} col The column index
5779      * @return {Number}
5780      */
5781     getDataIndex : function(col){
5782         return this.config[col].dataIndex;
5783     },
5784
5785     /**
5786      * Sets the dataIndex for a column.
5787      * @param {Number} col The column index
5788      * @param {Number} dataIndex The new dataIndex
5789      */
5790     setDataIndex : function(col, dataIndex){
5791         this.config[col].dataIndex = dataIndex;
5792     },
5793
5794     
5795     
5796     /**
5797      * Returns true if the cell is editable.
5798      * @param {Number} colIndex The column index
5799      * @param {Number} rowIndex The row index - this is nto actually used..?
5800      * @return {Boolean}
5801      */
5802     isCellEditable : function(colIndex, rowIndex){
5803         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5804     },
5805
5806     /**
5807      * Returns the editor defined for the cell/column.
5808      * return false or null to disable editing.
5809      * @param {Number} colIndex The column index
5810      * @param {Number} rowIndex The row index
5811      * @return {Object}
5812      */
5813     getCellEditor : function(colIndex, rowIndex){
5814         return this.config[colIndex].editor;
5815     },
5816
5817     /**
5818      * Sets if a column is editable.
5819      * @param {Number} col The column index
5820      * @param {Boolean} editable True if the column is editable
5821      */
5822     setEditable : function(col, editable){
5823         this.config[col].editable = editable;
5824     },
5825
5826
5827     /**
5828      * Returns true if the column is hidden.
5829      * @param {Number} colIndex The column index
5830      * @return {Boolean}
5831      */
5832     isHidden : function(colIndex){
5833         return this.config[colIndex].hidden;
5834     },
5835
5836
5837     /**
5838      * Returns true if the column width cannot be changed
5839      */
5840     isFixed : function(colIndex){
5841         return this.config[colIndex].fixed;
5842     },
5843
5844     /**
5845      * Returns true if the column can be resized
5846      * @return {Boolean}
5847      */
5848     isResizable : function(colIndex){
5849         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5850     },
5851     /**
5852      * Sets if a column is hidden.
5853      * @param {Number} colIndex The column index
5854      * @param {Boolean} hidden True if the column is hidden
5855      */
5856     setHidden : function(colIndex, hidden){
5857         this.config[colIndex].hidden = hidden;
5858         this.totalWidth = null;
5859         this.fireEvent("hiddenchange", this, colIndex, hidden);
5860     },
5861
5862     /**
5863      * Sets the editor for a column.
5864      * @param {Number} col The column index
5865      * @param {Object} editor The editor object
5866      */
5867     setEditor : function(col, editor){
5868         this.config[col].editor = editor;
5869     }
5870 });
5871
5872 Roo.grid.ColumnModel.defaultRenderer = function(value)
5873 {
5874     if(typeof value == "object") {
5875         return value;
5876     }
5877         if(typeof value == "string" && value.length < 1){
5878             return "&#160;";
5879         }
5880     
5881         return String.format("{0}", value);
5882 };
5883
5884 // Alias for backwards compatibility
5885 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5886 /*
5887  * Based on:
5888  * Ext JS Library 1.1.1
5889  * Copyright(c) 2006-2007, Ext JS, LLC.
5890  *
5891  * Originally Released Under LGPL - original licence link has changed is not relivant.
5892  *
5893  * Fork - LGPL
5894  * <script type="text/javascript">
5895  */
5896  
5897 /**
5898  * @class Roo.LoadMask
5899  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5900  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5901  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5902  * element's UpdateManager load indicator and will be destroyed after the initial load.
5903  * @constructor
5904  * Create a new LoadMask
5905  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5906  * @param {Object} config The config object
5907  */
5908 Roo.LoadMask = function(el, config){
5909     this.el = Roo.get(el);
5910     Roo.apply(this, config);
5911     if(this.store){
5912         this.store.on('beforeload', this.onBeforeLoad, this);
5913         this.store.on('load', this.onLoad, this);
5914         this.store.on('loadexception', this.onLoadException, this);
5915         this.removeMask = false;
5916     }else{
5917         var um = this.el.getUpdateManager();
5918         um.showLoadIndicator = false; // disable the default indicator
5919         um.on('beforeupdate', this.onBeforeLoad, this);
5920         um.on('update', this.onLoad, this);
5921         um.on('failure', this.onLoad, this);
5922         this.removeMask = true;
5923     }
5924 };
5925
5926 Roo.LoadMask.prototype = {
5927     /**
5928      * @cfg {Boolean} removeMask
5929      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5930      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5931      */
5932     /**
5933      * @cfg {String} msg
5934      * The text to display in a centered loading message box (defaults to 'Loading...')
5935      */
5936     msg : 'Loading...',
5937     /**
5938      * @cfg {String} msgCls
5939      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5940      */
5941     msgCls : 'x-mask-loading',
5942
5943     /**
5944      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5945      * @type Boolean
5946      */
5947     disabled: false,
5948
5949     /**
5950      * Disables the mask to prevent it from being displayed
5951      */
5952     disable : function(){
5953        this.disabled = true;
5954     },
5955
5956     /**
5957      * Enables the mask so that it can be displayed
5958      */
5959     enable : function(){
5960         this.disabled = false;
5961     },
5962     
5963     onLoadException : function()
5964     {
5965         Roo.log(arguments);
5966         
5967         if (typeof(arguments[3]) != 'undefined') {
5968             Roo.MessageBox.alert("Error loading",arguments[3]);
5969         } 
5970         /*
5971         try {
5972             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5973                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5974             }   
5975         } catch(e) {
5976             
5977         }
5978         */
5979     
5980         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5981     },
5982     // private
5983     onLoad : function()
5984     {
5985         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5986     },
5987
5988     // private
5989     onBeforeLoad : function(){
5990         if(!this.disabled){
5991             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5992         }
5993     },
5994
5995     // private
5996     destroy : function(){
5997         if(this.store){
5998             this.store.un('beforeload', this.onBeforeLoad, this);
5999             this.store.un('load', this.onLoad, this);
6000             this.store.un('loadexception', this.onLoadException, this);
6001         }else{
6002             var um = this.el.getUpdateManager();
6003             um.un('beforeupdate', this.onBeforeLoad, this);
6004             um.un('update', this.onLoad, this);
6005             um.un('failure', this.onLoad, this);
6006         }
6007     }
6008 };/*
6009  * - LGPL
6010  *
6011  * table
6012  * 
6013  */
6014
6015 /**
6016  * @class Roo.bootstrap.Table
6017  * @extends Roo.bootstrap.Component
6018  * Bootstrap Table class
6019  * @cfg {String} cls table class
6020  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6021  * @cfg {String} bgcolor Specifies the background color for a table
6022  * @cfg {Number} border Specifies whether the table cells should have borders or not
6023  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6024  * @cfg {Number} cellspacing Specifies the space between cells
6025  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6026  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6027  * @cfg {String} sortable Specifies that the table should be sortable
6028  * @cfg {String} summary Specifies a summary of the content of a table
6029  * @cfg {Number} width Specifies the width of a table
6030  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6031  * 
6032  * @cfg {boolean} striped Should the rows be alternative striped
6033  * @cfg {boolean} bordered Add borders to the table
6034  * @cfg {boolean} hover Add hover highlighting
6035  * @cfg {boolean} condensed Format condensed
6036  * @cfg {boolean} responsive Format condensed
6037  * @cfg {Boolean} loadMask (true|false) default false
6038  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6039  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6040  * @cfg {Boolean} rowSelection (true|false) default false
6041  * @cfg {Boolean} cellSelection (true|false) default false
6042  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6043  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6044  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6045  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6046  
6047  * 
6048  * @constructor
6049  * Create a new Table
6050  * @param {Object} config The config object
6051  */
6052
6053 Roo.bootstrap.Table = function(config){
6054     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6055     
6056   
6057     
6058     // BC...
6059     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6060     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6061     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6062     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6063     
6064     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6065     if (this.sm) {
6066         this.sm.grid = this;
6067         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6068         this.sm = this.selModel;
6069         this.sm.xmodule = this.xmodule || false;
6070     }
6071     
6072     if (this.cm && typeof(this.cm.config) == 'undefined') {
6073         this.colModel = new Roo.grid.ColumnModel(this.cm);
6074         this.cm = this.colModel;
6075         this.cm.xmodule = this.xmodule || false;
6076     }
6077     if (this.store) {
6078         this.store= Roo.factory(this.store, Roo.data);
6079         this.ds = this.store;
6080         this.ds.xmodule = this.xmodule || false;
6081          
6082     }
6083     if (this.footer && this.store) {
6084         this.footer.dataSource = this.ds;
6085         this.footer = Roo.factory(this.footer);
6086     }
6087     
6088     /** @private */
6089     this.addEvents({
6090         /**
6091          * @event cellclick
6092          * Fires when a cell is clicked
6093          * @param {Roo.bootstrap.Table} this
6094          * @param {Roo.Element} el
6095          * @param {Number} rowIndex
6096          * @param {Number} columnIndex
6097          * @param {Roo.EventObject} e
6098          */
6099         "cellclick" : true,
6100         /**
6101          * @event celldblclick
6102          * Fires when a cell is double clicked
6103          * @param {Roo.bootstrap.Table} this
6104          * @param {Roo.Element} el
6105          * @param {Number} rowIndex
6106          * @param {Number} columnIndex
6107          * @param {Roo.EventObject} e
6108          */
6109         "celldblclick" : true,
6110         /**
6111          * @event rowclick
6112          * Fires when a row is clicked
6113          * @param {Roo.bootstrap.Table} this
6114          * @param {Roo.Element} el
6115          * @param {Number} rowIndex
6116          * @param {Roo.EventObject} e
6117          */
6118         "rowclick" : true,
6119         /**
6120          * @event rowdblclick
6121          * Fires when a row is double clicked
6122          * @param {Roo.bootstrap.Table} this
6123          * @param {Roo.Element} el
6124          * @param {Number} rowIndex
6125          * @param {Roo.EventObject} e
6126          */
6127         "rowdblclick" : true,
6128         /**
6129          * @event mouseover
6130          * Fires when a mouseover occur
6131          * @param {Roo.bootstrap.Table} this
6132          * @param {Roo.Element} el
6133          * @param {Number} rowIndex
6134          * @param {Number} columnIndex
6135          * @param {Roo.EventObject} e
6136          */
6137         "mouseover" : true,
6138         /**
6139          * @event mouseout
6140          * Fires when a mouseout occur
6141          * @param {Roo.bootstrap.Table} this
6142          * @param {Roo.Element} el
6143          * @param {Number} rowIndex
6144          * @param {Number} columnIndex
6145          * @param {Roo.EventObject} e
6146          */
6147         "mouseout" : true,
6148         /**
6149          * @event rowclass
6150          * Fires when a row is rendered, so you can change add a style to it.
6151          * @param {Roo.bootstrap.Table} this
6152          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6153          */
6154         'rowclass' : true,
6155           /**
6156          * @event rowsrendered
6157          * Fires when all the  rows have been rendered
6158          * @param {Roo.bootstrap.Table} this
6159          */
6160         'rowsrendered' : true,
6161         /**
6162          * @event contextmenu
6163          * The raw contextmenu event for the entire grid.
6164          * @param {Roo.EventObject} e
6165          */
6166         "contextmenu" : true,
6167         /**
6168          * @event rowcontextmenu
6169          * Fires when a row is right clicked
6170          * @param {Roo.bootstrap.Table} this
6171          * @param {Number} rowIndex
6172          * @param {Roo.EventObject} e
6173          */
6174         "rowcontextmenu" : true,
6175         /**
6176          * @event cellcontextmenu
6177          * Fires when a cell is right clicked
6178          * @param {Roo.bootstrap.Table} this
6179          * @param {Number} rowIndex
6180          * @param {Number} cellIndex
6181          * @param {Roo.EventObject} e
6182          */
6183          "cellcontextmenu" : true,
6184          /**
6185          * @event headercontextmenu
6186          * Fires when a header is right clicked
6187          * @param {Roo.bootstrap.Table} this
6188          * @param {Number} columnIndex
6189          * @param {Roo.EventObject} e
6190          */
6191         "headercontextmenu" : true
6192     });
6193 };
6194
6195 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6196     
6197     cls: false,
6198     align: false,
6199     bgcolor: false,
6200     border: false,
6201     cellpadding: false,
6202     cellspacing: false,
6203     frame: false,
6204     rules: false,
6205     sortable: false,
6206     summary: false,
6207     width: false,
6208     striped : false,
6209     scrollBody : false,
6210     bordered: false,
6211     hover:  false,
6212     condensed : false,
6213     responsive : false,
6214     sm : false,
6215     cm : false,
6216     store : false,
6217     loadMask : false,
6218     footerShow : true,
6219     headerShow : true,
6220   
6221     rowSelection : false,
6222     cellSelection : false,
6223     layout : false,
6224     
6225     // Roo.Element - the tbody
6226     mainBody: false,
6227     // Roo.Element - thead element
6228     mainHead: false,
6229     
6230     container: false, // used by gridpanel...
6231     
6232     lazyLoad : false,
6233     
6234     CSS : Roo.util.CSS,
6235     
6236     auto_hide_footer : false,
6237     
6238     getAutoCreate : function()
6239     {
6240         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6241         
6242         cfg = {
6243             tag: 'table',
6244             cls : 'table',
6245             cn : []
6246         };
6247         if (this.scrollBody) {
6248             cfg.cls += ' table-body-fixed';
6249         }    
6250         if (this.striped) {
6251             cfg.cls += ' table-striped';
6252         }
6253         
6254         if (this.hover) {
6255             cfg.cls += ' table-hover';
6256         }
6257         if (this.bordered) {
6258             cfg.cls += ' table-bordered';
6259         }
6260         if (this.condensed) {
6261             cfg.cls += ' table-condensed';
6262         }
6263         if (this.responsive) {
6264             cfg.cls += ' table-responsive';
6265         }
6266         
6267         if (this.cls) {
6268             cfg.cls+=  ' ' +this.cls;
6269         }
6270         
6271         // this lot should be simplifed...
6272         var _t = this;
6273         var cp = [
6274             'align',
6275             'bgcolor',
6276             'border',
6277             'cellpadding',
6278             'cellspacing',
6279             'frame',
6280             'rules',
6281             'sortable',
6282             'summary',
6283             'width'
6284         ].forEach(function(k) {
6285             if (_t[k]) {
6286                 cfg[k] = _t[k];
6287             }
6288         });
6289         
6290         
6291         if (this.layout) {
6292             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6293         }
6294         
6295         if(this.store || this.cm){
6296             if(this.headerShow){
6297                 cfg.cn.push(this.renderHeader());
6298             }
6299             
6300             cfg.cn.push(this.renderBody());
6301             
6302             if(this.footerShow){
6303                 cfg.cn.push(this.renderFooter());
6304             }
6305             // where does this come from?
6306             //cfg.cls+=  ' TableGrid';
6307         }
6308         
6309         return { cn : [ cfg ] };
6310     },
6311     
6312     initEvents : function()
6313     {   
6314         if(!this.store || !this.cm){
6315             return;
6316         }
6317         if (this.selModel) {
6318             this.selModel.initEvents();
6319         }
6320         
6321         
6322         //Roo.log('initEvents with ds!!!!');
6323         
6324         this.mainBody = this.el.select('tbody', true).first();
6325         this.mainHead = this.el.select('thead', true).first();
6326         this.mainFoot = this.el.select('tfoot', true).first();
6327         
6328         
6329         
6330         var _this = this;
6331         
6332         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6333             e.on('click', _this.sort, _this);
6334         });
6335         
6336         this.mainBody.on("click", this.onClick, this);
6337         this.mainBody.on("dblclick", this.onDblClick, this);
6338         
6339         // why is this done????? = it breaks dialogs??
6340         //this.parent().el.setStyle('position', 'relative');
6341         
6342         
6343         if (this.footer) {
6344             this.footer.parentId = this.id;
6345             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6346             
6347             if(this.lazyLoad){
6348                 this.el.select('tfoot tr td').first().addClass('hide');
6349             }
6350         } 
6351         
6352         if(this.loadMask) {
6353             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6354         }
6355         
6356         this.store.on('load', this.onLoad, this);
6357         this.store.on('beforeload', this.onBeforeLoad, this);
6358         this.store.on('update', this.onUpdate, this);
6359         this.store.on('add', this.onAdd, this);
6360         this.store.on("clear", this.clear, this);
6361         
6362         this.el.on("contextmenu", this.onContextMenu, this);
6363         
6364         this.mainBody.on('scroll', this.onBodyScroll, this);
6365         
6366         this.cm.on("headerchange", this.onHeaderChange, this);
6367         
6368         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6369         
6370     },
6371     
6372     onContextMenu : function(e, t)
6373     {
6374         this.processEvent("contextmenu", e);
6375     },
6376     
6377     processEvent : function(name, e)
6378     {
6379         if (name != 'touchstart' ) {
6380             this.fireEvent(name, e);    
6381         }
6382         
6383         var t = e.getTarget();
6384         
6385         var cell = Roo.get(t);
6386         
6387         if(!cell){
6388             return;
6389         }
6390         
6391         if(cell.findParent('tfoot', false, true)){
6392             return;
6393         }
6394         
6395         if(cell.findParent('thead', false, true)){
6396             
6397             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6398                 cell = Roo.get(t).findParent('th', false, true);
6399                 if (!cell) {
6400                     Roo.log("failed to find th in thead?");
6401                     Roo.log(e.getTarget());
6402                     return;
6403                 }
6404             }
6405             
6406             var cellIndex = cell.dom.cellIndex;
6407             
6408             var ename = name == 'touchstart' ? 'click' : name;
6409             this.fireEvent("header" + ename, this, cellIndex, e);
6410             
6411             return;
6412         }
6413         
6414         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6415             cell = Roo.get(t).findParent('td', false, true);
6416             if (!cell) {
6417                 Roo.log("failed to find th in tbody?");
6418                 Roo.log(e.getTarget());
6419                 return;
6420             }
6421         }
6422         
6423         var row = cell.findParent('tr', false, true);
6424         var cellIndex = cell.dom.cellIndex;
6425         var rowIndex = row.dom.rowIndex - 1;
6426         
6427         if(row !== false){
6428             
6429             this.fireEvent("row" + name, this, rowIndex, e);
6430             
6431             if(cell !== false){
6432             
6433                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6434             }
6435         }
6436         
6437     },
6438     
6439     onMouseover : function(e, el)
6440     {
6441         var cell = Roo.get(el);
6442         
6443         if(!cell){
6444             return;
6445         }
6446         
6447         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6448             cell = cell.findParent('td', false, true);
6449         }
6450         
6451         var row = cell.findParent('tr', false, true);
6452         var cellIndex = cell.dom.cellIndex;
6453         var rowIndex = row.dom.rowIndex - 1; // start from 0
6454         
6455         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6456         
6457     },
6458     
6459     onMouseout : function(e, el)
6460     {
6461         var cell = Roo.get(el);
6462         
6463         if(!cell){
6464             return;
6465         }
6466         
6467         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6468             cell = cell.findParent('td', false, true);
6469         }
6470         
6471         var row = cell.findParent('tr', false, true);
6472         var cellIndex = cell.dom.cellIndex;
6473         var rowIndex = row.dom.rowIndex - 1; // start from 0
6474         
6475         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6476         
6477     },
6478     
6479     onClick : function(e, el)
6480     {
6481         var cell = Roo.get(el);
6482         
6483         if(!cell || (!this.cellSelection && !this.rowSelection)){
6484             return;
6485         }
6486         
6487         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6488             cell = cell.findParent('td', false, true);
6489         }
6490         
6491         if(!cell || typeof(cell) == 'undefined'){
6492             return;
6493         }
6494         
6495         var row = cell.findParent('tr', false, true);
6496         
6497         if(!row || typeof(row) == 'undefined'){
6498             return;
6499         }
6500         
6501         var cellIndex = cell.dom.cellIndex;
6502         var rowIndex = this.getRowIndex(row);
6503         
6504         // why??? - should these not be based on SelectionModel?
6505         if(this.cellSelection){
6506             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6507         }
6508         
6509         if(this.rowSelection){
6510             this.fireEvent('rowclick', this, row, rowIndex, e);
6511         }
6512         
6513         
6514     },
6515         
6516     onDblClick : function(e,el)
6517     {
6518         var cell = Roo.get(el);
6519         
6520         if(!cell || (!this.cellSelection && !this.rowSelection)){
6521             return;
6522         }
6523         
6524         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6525             cell = cell.findParent('td', false, true);
6526         }
6527         
6528         if(!cell || typeof(cell) == 'undefined'){
6529             return;
6530         }
6531         
6532         var row = cell.findParent('tr', false, true);
6533         
6534         if(!row || typeof(row) == 'undefined'){
6535             return;
6536         }
6537         
6538         var cellIndex = cell.dom.cellIndex;
6539         var rowIndex = this.getRowIndex(row);
6540         
6541         if(this.cellSelection){
6542             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6543         }
6544         
6545         if(this.rowSelection){
6546             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6547         }
6548     },
6549     
6550     sort : function(e,el)
6551     {
6552         var col = Roo.get(el);
6553         
6554         if(!col.hasClass('sortable')){
6555             return;
6556         }
6557         
6558         var sort = col.attr('sort');
6559         var dir = 'ASC';
6560         
6561         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6562             dir = 'DESC';
6563         }
6564         
6565         this.store.sortInfo = {field : sort, direction : dir};
6566         
6567         if (this.footer) {
6568             Roo.log("calling footer first");
6569             this.footer.onClick('first');
6570         } else {
6571         
6572             this.store.load({ params : { start : 0 } });
6573         }
6574     },
6575     
6576     renderHeader : function()
6577     {
6578         var header = {
6579             tag: 'thead',
6580             cn : []
6581         };
6582         
6583         var cm = this.cm;
6584         this.totalWidth = 0;
6585         
6586         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6587             
6588             var config = cm.config[i];
6589             
6590             var c = {
6591                 tag: 'th',
6592                 cls : 'x-hcol-' + i,
6593                 style : '',
6594                 html: cm.getColumnHeader(i)
6595             };
6596             
6597             var hh = '';
6598             
6599             if(typeof(config.sortable) != 'undefined' && config.sortable){
6600                 c.cls = 'sortable';
6601                 c.html = '<i class="glyphicon"></i>' + c.html;
6602             }
6603             
6604             if(typeof(config.lgHeader) != 'undefined'){
6605                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6606             }
6607             
6608             if(typeof(config.mdHeader) != 'undefined'){
6609                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6610             }
6611             
6612             if(typeof(config.smHeader) != 'undefined'){
6613                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6614             }
6615             
6616             if(typeof(config.xsHeader) != 'undefined'){
6617                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6618             }
6619             
6620             if(hh.length){
6621                 c.html = hh;
6622             }
6623             
6624             if(typeof(config.tooltip) != 'undefined'){
6625                 c.tooltip = config.tooltip;
6626             }
6627             
6628             if(typeof(config.colspan) != 'undefined'){
6629                 c.colspan = config.colspan;
6630             }
6631             
6632             if(typeof(config.hidden) != 'undefined' && config.hidden){
6633                 c.style += ' display:none;';
6634             }
6635             
6636             if(typeof(config.dataIndex) != 'undefined'){
6637                 c.sort = config.dataIndex;
6638             }
6639             
6640            
6641             
6642             if(typeof(config.align) != 'undefined' && config.align.length){
6643                 c.style += ' text-align:' + config.align + ';';
6644             }
6645             
6646             if(typeof(config.width) != 'undefined'){
6647                 c.style += ' width:' + config.width + 'px;';
6648                 this.totalWidth += config.width;
6649             } else {
6650                 this.totalWidth += 100; // assume minimum of 100 per column?
6651             }
6652             
6653             if(typeof(config.cls) != 'undefined'){
6654                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6655             }
6656             
6657             ['xs','sm','md','lg'].map(function(size){
6658                 
6659                 if(typeof(config[size]) == 'undefined'){
6660                     return;
6661                 }
6662                 
6663                 if (!config[size]) { // 0 = hidden
6664                     c.cls += ' hidden-' + size;
6665                     return;
6666                 }
6667                 
6668                 c.cls += ' col-' + size + '-' + config[size];
6669
6670             });
6671             
6672             header.cn.push(c)
6673         }
6674         
6675         return header;
6676     },
6677     
6678     renderBody : function()
6679     {
6680         var body = {
6681             tag: 'tbody',
6682             cn : [
6683                 {
6684                     tag: 'tr',
6685                     cn : [
6686                         {
6687                             tag : 'td',
6688                             colspan :  this.cm.getColumnCount()
6689                         }
6690                     ]
6691                 }
6692             ]
6693         };
6694         
6695         return body;
6696     },
6697     
6698     renderFooter : function()
6699     {
6700         var footer = {
6701             tag: 'tfoot',
6702             cn : [
6703                 {
6704                     tag: 'tr',
6705                     cn : [
6706                         {
6707                             tag : 'td',
6708                             colspan :  this.cm.getColumnCount()
6709                         }
6710                     ]
6711                 }
6712             ]
6713         };
6714         
6715         return footer;
6716     },
6717     
6718     
6719     
6720     onLoad : function()
6721     {
6722 //        Roo.log('ds onload');
6723         this.clear();
6724         
6725         var _this = this;
6726         var cm = this.cm;
6727         var ds = this.store;
6728         
6729         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6730             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6731             if (_this.store.sortInfo) {
6732                     
6733                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6734                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6735                 }
6736                 
6737                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6738                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6739                 }
6740             }
6741         });
6742         
6743         var tbody =  this.mainBody;
6744               
6745         if(ds.getCount() > 0){
6746             ds.data.each(function(d,rowIndex){
6747                 var row =  this.renderRow(cm, ds, rowIndex);
6748                 
6749                 tbody.createChild(row);
6750                 
6751                 var _this = this;
6752                 
6753                 if(row.cellObjects.length){
6754                     Roo.each(row.cellObjects, function(r){
6755                         _this.renderCellObject(r);
6756                     })
6757                 }
6758                 
6759             }, this);
6760         }
6761         
6762         var tfoot = this.el.select('tfoot', true).first();
6763         
6764         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6765             
6766             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6767             
6768             var total = this.ds.getTotalCount();
6769             
6770             if(this.footer.pageSize < total){
6771                 this.mainFoot.show();
6772             }
6773         }
6774         
6775         Roo.each(this.el.select('tbody td', true).elements, function(e){
6776             e.on('mouseover', _this.onMouseover, _this);
6777         });
6778         
6779         Roo.each(this.el.select('tbody td', true).elements, function(e){
6780             e.on('mouseout', _this.onMouseout, _this);
6781         });
6782         this.fireEvent('rowsrendered', this);
6783         
6784         this.autoSize();
6785     },
6786     
6787     
6788     onUpdate : function(ds,record)
6789     {
6790         this.refreshRow(record);
6791         this.autoSize();
6792     },
6793     
6794     onRemove : function(ds, record, index, isUpdate){
6795         if(isUpdate !== true){
6796             this.fireEvent("beforerowremoved", this, index, record);
6797         }
6798         var bt = this.mainBody.dom;
6799         
6800         var rows = this.el.select('tbody > tr', true).elements;
6801         
6802         if(typeof(rows[index]) != 'undefined'){
6803             bt.removeChild(rows[index].dom);
6804         }
6805         
6806 //        if(bt.rows[index]){
6807 //            bt.removeChild(bt.rows[index]);
6808 //        }
6809         
6810         if(isUpdate !== true){
6811             //this.stripeRows(index);
6812             //this.syncRowHeights(index, index);
6813             //this.layout();
6814             this.fireEvent("rowremoved", this, index, record);
6815         }
6816     },
6817     
6818     onAdd : function(ds, records, rowIndex)
6819     {
6820         //Roo.log('on Add called');
6821         // - note this does not handle multiple adding very well..
6822         var bt = this.mainBody.dom;
6823         for (var i =0 ; i < records.length;i++) {
6824             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6825             //Roo.log(records[i]);
6826             //Roo.log(this.store.getAt(rowIndex+i));
6827             this.insertRow(this.store, rowIndex + i, false);
6828             return;
6829         }
6830         
6831     },
6832     
6833     
6834     refreshRow : function(record){
6835         var ds = this.store, index;
6836         if(typeof record == 'number'){
6837             index = record;
6838             record = ds.getAt(index);
6839         }else{
6840             index = ds.indexOf(record);
6841         }
6842         this.insertRow(ds, index, true);
6843         this.autoSize();
6844         this.onRemove(ds, record, index+1, true);
6845         this.autoSize();
6846         //this.syncRowHeights(index, index);
6847         //this.layout();
6848         this.fireEvent("rowupdated", this, index, record);
6849     },
6850     
6851     insertRow : function(dm, rowIndex, isUpdate){
6852         
6853         if(!isUpdate){
6854             this.fireEvent("beforerowsinserted", this, rowIndex);
6855         }
6856             //var s = this.getScrollState();
6857         var row = this.renderRow(this.cm, this.store, rowIndex);
6858         // insert before rowIndex..
6859         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6860         
6861         var _this = this;
6862                 
6863         if(row.cellObjects.length){
6864             Roo.each(row.cellObjects, function(r){
6865                 _this.renderCellObject(r);
6866             })
6867         }
6868             
6869         if(!isUpdate){
6870             this.fireEvent("rowsinserted", this, rowIndex);
6871             //this.syncRowHeights(firstRow, lastRow);
6872             //this.stripeRows(firstRow);
6873             //this.layout();
6874         }
6875         
6876     },
6877     
6878     
6879     getRowDom : function(rowIndex)
6880     {
6881         var rows = this.el.select('tbody > tr', true).elements;
6882         
6883         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6884         
6885     },
6886     // returns the object tree for a tr..
6887   
6888     
6889     renderRow : function(cm, ds, rowIndex) 
6890     {
6891         var d = ds.getAt(rowIndex);
6892         
6893         var row = {
6894             tag : 'tr',
6895             cls : 'x-row-' + rowIndex,
6896             cn : []
6897         };
6898             
6899         var cellObjects = [];
6900         
6901         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6902             var config = cm.config[i];
6903             
6904             var renderer = cm.getRenderer(i);
6905             var value = '';
6906             var id = false;
6907             
6908             if(typeof(renderer) !== 'undefined'){
6909                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6910             }
6911             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6912             // and are rendered into the cells after the row is rendered - using the id for the element.
6913             
6914             if(typeof(value) === 'object'){
6915                 id = Roo.id();
6916                 cellObjects.push({
6917                     container : id,
6918                     cfg : value 
6919                 })
6920             }
6921             
6922             var rowcfg = {
6923                 record: d,
6924                 rowIndex : rowIndex,
6925                 colIndex : i,
6926                 rowClass : ''
6927             };
6928
6929             this.fireEvent('rowclass', this, rowcfg);
6930             
6931             var td = {
6932                 tag: 'td',
6933                 cls : rowcfg.rowClass + ' x-col-' + i,
6934                 style: '',
6935                 html: (typeof(value) === 'object') ? '' : value
6936             };
6937             
6938             if (id) {
6939                 td.id = id;
6940             }
6941             
6942             if(typeof(config.colspan) != 'undefined'){
6943                 td.colspan = config.colspan;
6944             }
6945             
6946             if(typeof(config.hidden) != 'undefined' && config.hidden){
6947                 td.style += ' display:none;';
6948             }
6949             
6950             if(typeof(config.align) != 'undefined' && config.align.length){
6951                 td.style += ' text-align:' + config.align + ';';
6952             }
6953             if(typeof(config.valign) != 'undefined' && config.valign.length){
6954                 td.style += ' vertical-align:' + config.valign + ';';
6955             }
6956             
6957             if(typeof(config.width) != 'undefined'){
6958                 td.style += ' width:' +  config.width + 'px;';
6959             }
6960             
6961             if(typeof(config.cursor) != 'undefined'){
6962                 td.style += ' cursor:' +  config.cursor + ';';
6963             }
6964             
6965             if(typeof(config.cls) != 'undefined'){
6966                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6967             }
6968             
6969             ['xs','sm','md','lg'].map(function(size){
6970                 
6971                 if(typeof(config[size]) == 'undefined'){
6972                     return;
6973                 }
6974                 
6975                 if (!config[size]) { // 0 = hidden
6976                     td.cls += ' hidden-' + size;
6977                     return;
6978                 }
6979                 
6980                 td.cls += ' col-' + size + '-' + config[size];
6981
6982             });
6983             
6984             row.cn.push(td);
6985            
6986         }
6987         
6988         row.cellObjects = cellObjects;
6989         
6990         return row;
6991           
6992     },
6993     
6994     
6995     
6996     onBeforeLoad : function()
6997     {
6998         
6999     },
7000      /**
7001      * Remove all rows
7002      */
7003     clear : function()
7004     {
7005         this.el.select('tbody', true).first().dom.innerHTML = '';
7006     },
7007     /**
7008      * Show or hide a row.
7009      * @param {Number} rowIndex to show or hide
7010      * @param {Boolean} state hide
7011      */
7012     setRowVisibility : function(rowIndex, state)
7013     {
7014         var bt = this.mainBody.dom;
7015         
7016         var rows = this.el.select('tbody > tr', true).elements;
7017         
7018         if(typeof(rows[rowIndex]) == 'undefined'){
7019             return;
7020         }
7021         rows[rowIndex].dom.style.display = state ? '' : 'none';
7022     },
7023     
7024     
7025     getSelectionModel : function(){
7026         if(!this.selModel){
7027             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7028         }
7029         return this.selModel;
7030     },
7031     /*
7032      * Render the Roo.bootstrap object from renderder
7033      */
7034     renderCellObject : function(r)
7035     {
7036         var _this = this;
7037         
7038         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7039         
7040         var t = r.cfg.render(r.container);
7041         
7042         if(r.cfg.cn){
7043             Roo.each(r.cfg.cn, function(c){
7044                 var child = {
7045                     container: t.getChildContainer(),
7046                     cfg: c
7047                 };
7048                 _this.renderCellObject(child);
7049             })
7050         }
7051     },
7052     
7053     getRowIndex : function(row)
7054     {
7055         var rowIndex = -1;
7056         
7057         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7058             if(el != row){
7059                 return;
7060             }
7061             
7062             rowIndex = index;
7063         });
7064         
7065         return rowIndex;
7066     },
7067      /**
7068      * Returns the grid's underlying element = used by panel.Grid
7069      * @return {Element} The element
7070      */
7071     getGridEl : function(){
7072         return this.el;
7073     },
7074      /**
7075      * Forces a resize - used by panel.Grid
7076      * @return {Element} The element
7077      */
7078     autoSize : function()
7079     {
7080         //var ctr = Roo.get(this.container.dom.parentElement);
7081         var ctr = Roo.get(this.el.dom);
7082         
7083         var thd = this.getGridEl().select('thead',true).first();
7084         var tbd = this.getGridEl().select('tbody', true).first();
7085         var tfd = this.getGridEl().select('tfoot', true).first();
7086         
7087         var cw = ctr.getWidth();
7088         
7089         if (tbd) {
7090             
7091             tbd.setSize(ctr.getWidth(),
7092                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7093             );
7094             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7095             cw -= barsize;
7096         }
7097         cw = Math.max(cw, this.totalWidth);
7098         this.getGridEl().select('tr',true).setWidth(cw);
7099         // resize 'expandable coloumn?
7100         
7101         return; // we doe not have a view in this design..
7102         
7103     },
7104     onBodyScroll: function()
7105     {
7106         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7107         if(this.mainHead){
7108             this.mainHead.setStyle({
7109                 'position' : 'relative',
7110                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7111             });
7112         }
7113         
7114         if(this.lazyLoad){
7115             
7116             var scrollHeight = this.mainBody.dom.scrollHeight;
7117             
7118             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7119             
7120             var height = this.mainBody.getHeight();
7121             
7122             if(scrollHeight - height == scrollTop) {
7123                 
7124                 var total = this.ds.getTotalCount();
7125                 
7126                 if(this.footer.cursor + this.footer.pageSize < total){
7127                     
7128                     this.footer.ds.load({
7129                         params : {
7130                             start : this.footer.cursor + this.footer.pageSize,
7131                             limit : this.footer.pageSize
7132                         },
7133                         add : true
7134                     });
7135                 }
7136             }
7137             
7138         }
7139     },
7140     
7141     onHeaderChange : function()
7142     {
7143         var header = this.renderHeader();
7144         var table = this.el.select('table', true).first();
7145         
7146         this.mainHead.remove();
7147         this.mainHead = table.createChild(header, this.mainBody, false);
7148     },
7149     
7150     onHiddenChange : function(colModel, colIndex, hidden)
7151     {
7152         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7153         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7154         
7155         this.CSS.updateRule(thSelector, "display", "");
7156         this.CSS.updateRule(tdSelector, "display", "");
7157         
7158         if(hidden){
7159             this.CSS.updateRule(thSelector, "display", "none");
7160             this.CSS.updateRule(tdSelector, "display", "none");
7161         }
7162         
7163         this.onHeaderChange();
7164         this.onLoad();
7165     },
7166     
7167     setColumnWidth: function(col_index, width)
7168     {
7169         // width = "md-2 xs-2..."
7170         if(!this.colModel.config[col_index]) {
7171             return;
7172         }
7173         
7174         var w = width.split(" ");
7175         
7176         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7177         
7178         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7179         
7180         
7181         for(var j = 0; j < w.length; j++) {
7182             
7183             if(!w[j]) {
7184                 continue;
7185             }
7186             
7187             var size_cls = w[j].split("-");
7188             
7189             if(!Number.isInteger(size_cls[1] * 1)) {
7190                 continue;
7191             }
7192             
7193             if(!this.colModel.config[col_index][size_cls[0]]) {
7194                 continue;
7195             }
7196             
7197             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7198                 continue;
7199             }
7200             
7201             h_row[0].classList.replace(
7202                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7203                 "col-"+size_cls[0]+"-"+size_cls[1]
7204             );
7205             
7206             for(var i = 0; i < rows.length; i++) {
7207                 
7208                 var size_cls = w[j].split("-");
7209                 
7210                 if(!Number.isInteger(size_cls[1] * 1)) {
7211                     continue;
7212                 }
7213                 
7214                 if(!this.colModel.config[col_index][size_cls[0]]) {
7215                     continue;
7216                 }
7217                 
7218                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7219                     continue;
7220                 }
7221                 
7222                 rows[i].classList.replace(
7223                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7224                     "col-"+size_cls[0]+"-"+size_cls[1]
7225                 );
7226             }
7227             
7228             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7229         }
7230     }
7231 });
7232
7233  
7234
7235  /*
7236  * - LGPL
7237  *
7238  * table cell
7239  * 
7240  */
7241
7242 /**
7243  * @class Roo.bootstrap.TableCell
7244  * @extends Roo.bootstrap.Component
7245  * Bootstrap TableCell class
7246  * @cfg {String} html cell contain text
7247  * @cfg {String} cls cell class
7248  * @cfg {String} tag cell tag (td|th) default td
7249  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7250  * @cfg {String} align Aligns the content in a cell
7251  * @cfg {String} axis Categorizes cells
7252  * @cfg {String} bgcolor Specifies the background color of a cell
7253  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7254  * @cfg {Number} colspan Specifies the number of columns a cell should span
7255  * @cfg {String} headers Specifies one or more header cells a cell is related to
7256  * @cfg {Number} height Sets the height of a cell
7257  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7258  * @cfg {Number} rowspan Sets the number of rows a cell should span
7259  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7260  * @cfg {String} valign Vertical aligns the content in a cell
7261  * @cfg {Number} width Specifies the width of a cell
7262  * 
7263  * @constructor
7264  * Create a new TableCell
7265  * @param {Object} config The config object
7266  */
7267
7268 Roo.bootstrap.TableCell = function(config){
7269     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7270 };
7271
7272 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7273     
7274     html: false,
7275     cls: false,
7276     tag: false,
7277     abbr: false,
7278     align: false,
7279     axis: false,
7280     bgcolor: false,
7281     charoff: false,
7282     colspan: false,
7283     headers: false,
7284     height: false,
7285     nowrap: false,
7286     rowspan: false,
7287     scope: false,
7288     valign: false,
7289     width: false,
7290     
7291     
7292     getAutoCreate : function(){
7293         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7294         
7295         cfg = {
7296             tag: 'td'
7297         };
7298         
7299         if(this.tag){
7300             cfg.tag = this.tag;
7301         }
7302         
7303         if (this.html) {
7304             cfg.html=this.html
7305         }
7306         if (this.cls) {
7307             cfg.cls=this.cls
7308         }
7309         if (this.abbr) {
7310             cfg.abbr=this.abbr
7311         }
7312         if (this.align) {
7313             cfg.align=this.align
7314         }
7315         if (this.axis) {
7316             cfg.axis=this.axis
7317         }
7318         if (this.bgcolor) {
7319             cfg.bgcolor=this.bgcolor
7320         }
7321         if (this.charoff) {
7322             cfg.charoff=this.charoff
7323         }
7324         if (this.colspan) {
7325             cfg.colspan=this.colspan
7326         }
7327         if (this.headers) {
7328             cfg.headers=this.headers
7329         }
7330         if (this.height) {
7331             cfg.height=this.height
7332         }
7333         if (this.nowrap) {
7334             cfg.nowrap=this.nowrap
7335         }
7336         if (this.rowspan) {
7337             cfg.rowspan=this.rowspan
7338         }
7339         if (this.scope) {
7340             cfg.scope=this.scope
7341         }
7342         if (this.valign) {
7343             cfg.valign=this.valign
7344         }
7345         if (this.width) {
7346             cfg.width=this.width
7347         }
7348         
7349         
7350         return cfg;
7351     }
7352    
7353 });
7354
7355  
7356
7357  /*
7358  * - LGPL
7359  *
7360  * table row
7361  * 
7362  */
7363
7364 /**
7365  * @class Roo.bootstrap.TableRow
7366  * @extends Roo.bootstrap.Component
7367  * Bootstrap TableRow class
7368  * @cfg {String} cls row class
7369  * @cfg {String} align Aligns the content in a table row
7370  * @cfg {String} bgcolor Specifies a background color for a table row
7371  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7372  * @cfg {String} valign Vertical aligns the content in a table row
7373  * 
7374  * @constructor
7375  * Create a new TableRow
7376  * @param {Object} config The config object
7377  */
7378
7379 Roo.bootstrap.TableRow = function(config){
7380     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7381 };
7382
7383 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7384     
7385     cls: false,
7386     align: false,
7387     bgcolor: false,
7388     charoff: false,
7389     valign: false,
7390     
7391     getAutoCreate : function(){
7392         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7393         
7394         cfg = {
7395             tag: 'tr'
7396         };
7397             
7398         if(this.cls){
7399             cfg.cls = this.cls;
7400         }
7401         if(this.align){
7402             cfg.align = this.align;
7403         }
7404         if(this.bgcolor){
7405             cfg.bgcolor = this.bgcolor;
7406         }
7407         if(this.charoff){
7408             cfg.charoff = this.charoff;
7409         }
7410         if(this.valign){
7411             cfg.valign = this.valign;
7412         }
7413         
7414         return cfg;
7415     }
7416    
7417 });
7418
7419  
7420
7421  /*
7422  * - LGPL
7423  *
7424  * table body
7425  * 
7426  */
7427
7428 /**
7429  * @class Roo.bootstrap.TableBody
7430  * @extends Roo.bootstrap.Component
7431  * Bootstrap TableBody class
7432  * @cfg {String} cls element class
7433  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7434  * @cfg {String} align Aligns the content inside the element
7435  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7436  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7437  * 
7438  * @constructor
7439  * Create a new TableBody
7440  * @param {Object} config The config object
7441  */
7442
7443 Roo.bootstrap.TableBody = function(config){
7444     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7445 };
7446
7447 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7448     
7449     cls: false,
7450     tag: false,
7451     align: false,
7452     charoff: false,
7453     valign: false,
7454     
7455     getAutoCreate : function(){
7456         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7457         
7458         cfg = {
7459             tag: 'tbody'
7460         };
7461             
7462         if (this.cls) {
7463             cfg.cls=this.cls
7464         }
7465         if(this.tag){
7466             cfg.tag = this.tag;
7467         }
7468         
7469         if(this.align){
7470             cfg.align = this.align;
7471         }
7472         if(this.charoff){
7473             cfg.charoff = this.charoff;
7474         }
7475         if(this.valign){
7476             cfg.valign = this.valign;
7477         }
7478         
7479         return cfg;
7480     }
7481     
7482     
7483 //    initEvents : function()
7484 //    {
7485 //        
7486 //        if(!this.store){
7487 //            return;
7488 //        }
7489 //        
7490 //        this.store = Roo.factory(this.store, Roo.data);
7491 //        this.store.on('load', this.onLoad, this);
7492 //        
7493 //        this.store.load();
7494 //        
7495 //    },
7496 //    
7497 //    onLoad: function () 
7498 //    {   
7499 //        this.fireEvent('load', this);
7500 //    }
7501 //    
7502 //   
7503 });
7504
7505  
7506
7507  /*
7508  * Based on:
7509  * Ext JS Library 1.1.1
7510  * Copyright(c) 2006-2007, Ext JS, LLC.
7511  *
7512  * Originally Released Under LGPL - original licence link has changed is not relivant.
7513  *
7514  * Fork - LGPL
7515  * <script type="text/javascript">
7516  */
7517
7518 // as we use this in bootstrap.
7519 Roo.namespace('Roo.form');
7520  /**
7521  * @class Roo.form.Action
7522  * Internal Class used to handle form actions
7523  * @constructor
7524  * @param {Roo.form.BasicForm} el The form element or its id
7525  * @param {Object} config Configuration options
7526  */
7527
7528  
7529  
7530 // define the action interface
7531 Roo.form.Action = function(form, options){
7532     this.form = form;
7533     this.options = options || {};
7534 };
7535 /**
7536  * Client Validation Failed
7537  * @const 
7538  */
7539 Roo.form.Action.CLIENT_INVALID = 'client';
7540 /**
7541  * Server Validation Failed
7542  * @const 
7543  */
7544 Roo.form.Action.SERVER_INVALID = 'server';
7545  /**
7546  * Connect to Server Failed
7547  * @const 
7548  */
7549 Roo.form.Action.CONNECT_FAILURE = 'connect';
7550 /**
7551  * Reading Data from Server Failed
7552  * @const 
7553  */
7554 Roo.form.Action.LOAD_FAILURE = 'load';
7555
7556 Roo.form.Action.prototype = {
7557     type : 'default',
7558     failureType : undefined,
7559     response : undefined,
7560     result : undefined,
7561
7562     // interface method
7563     run : function(options){
7564
7565     },
7566
7567     // interface method
7568     success : function(response){
7569
7570     },
7571
7572     // interface method
7573     handleResponse : function(response){
7574
7575     },
7576
7577     // default connection failure
7578     failure : function(response){
7579         
7580         this.response = response;
7581         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7582         this.form.afterAction(this, false);
7583     },
7584
7585     processResponse : function(response){
7586         this.response = response;
7587         if(!response.responseText){
7588             return true;
7589         }
7590         this.result = this.handleResponse(response);
7591         return this.result;
7592     },
7593
7594     // utility functions used internally
7595     getUrl : function(appendParams){
7596         var url = this.options.url || this.form.url || this.form.el.dom.action;
7597         if(appendParams){
7598             var p = this.getParams();
7599             if(p){
7600                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7601             }
7602         }
7603         return url;
7604     },
7605
7606     getMethod : function(){
7607         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7608     },
7609
7610     getParams : function(){
7611         var bp = this.form.baseParams;
7612         var p = this.options.params;
7613         if(p){
7614             if(typeof p == "object"){
7615                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7616             }else if(typeof p == 'string' && bp){
7617                 p += '&' + Roo.urlEncode(bp);
7618             }
7619         }else if(bp){
7620             p = Roo.urlEncode(bp);
7621         }
7622         return p;
7623     },
7624
7625     createCallback : function(){
7626         return {
7627             success: this.success,
7628             failure: this.failure,
7629             scope: this,
7630             timeout: (this.form.timeout*1000),
7631             upload: this.form.fileUpload ? this.success : undefined
7632         };
7633     }
7634 };
7635
7636 Roo.form.Action.Submit = function(form, options){
7637     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7638 };
7639
7640 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7641     type : 'submit',
7642
7643     haveProgress : false,
7644     uploadComplete : false,
7645     
7646     // uploadProgress indicator.
7647     uploadProgress : function()
7648     {
7649         if (!this.form.progressUrl) {
7650             return;
7651         }
7652         
7653         if (!this.haveProgress) {
7654             Roo.MessageBox.progress("Uploading", "Uploading");
7655         }
7656         if (this.uploadComplete) {
7657            Roo.MessageBox.hide();
7658            return;
7659         }
7660         
7661         this.haveProgress = true;
7662    
7663         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7664         
7665         var c = new Roo.data.Connection();
7666         c.request({
7667             url : this.form.progressUrl,
7668             params: {
7669                 id : uid
7670             },
7671             method: 'GET',
7672             success : function(req){
7673                //console.log(data);
7674                 var rdata = false;
7675                 var edata;
7676                 try  {
7677                    rdata = Roo.decode(req.responseText)
7678                 } catch (e) {
7679                     Roo.log("Invalid data from server..");
7680                     Roo.log(edata);
7681                     return;
7682                 }
7683                 if (!rdata || !rdata.success) {
7684                     Roo.log(rdata);
7685                     Roo.MessageBox.alert(Roo.encode(rdata));
7686                     return;
7687                 }
7688                 var data = rdata.data;
7689                 
7690                 if (this.uploadComplete) {
7691                    Roo.MessageBox.hide();
7692                    return;
7693                 }
7694                    
7695                 if (data){
7696                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7697                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7698                     );
7699                 }
7700                 this.uploadProgress.defer(2000,this);
7701             },
7702        
7703             failure: function(data) {
7704                 Roo.log('progress url failed ');
7705                 Roo.log(data);
7706             },
7707             scope : this
7708         });
7709            
7710     },
7711     
7712     
7713     run : function()
7714     {
7715         // run get Values on the form, so it syncs any secondary forms.
7716         this.form.getValues();
7717         
7718         var o = this.options;
7719         var method = this.getMethod();
7720         var isPost = method == 'POST';
7721         if(o.clientValidation === false || this.form.isValid()){
7722             
7723             if (this.form.progressUrl) {
7724                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7725                     (new Date() * 1) + '' + Math.random());
7726                     
7727             } 
7728             
7729             
7730             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7731                 form:this.form.el.dom,
7732                 url:this.getUrl(!isPost),
7733                 method: method,
7734                 params:isPost ? this.getParams() : null,
7735                 isUpload: this.form.fileUpload
7736             }));
7737             
7738             this.uploadProgress();
7739
7740         }else if (o.clientValidation !== false){ // client validation failed
7741             this.failureType = Roo.form.Action.CLIENT_INVALID;
7742             this.form.afterAction(this, false);
7743         }
7744     },
7745
7746     success : function(response)
7747     {
7748         this.uploadComplete= true;
7749         if (this.haveProgress) {
7750             Roo.MessageBox.hide();
7751         }
7752         
7753         
7754         var result = this.processResponse(response);
7755         if(result === true || result.success){
7756             this.form.afterAction(this, true);
7757             return;
7758         }
7759         if(result.errors){
7760             this.form.markInvalid(result.errors);
7761             this.failureType = Roo.form.Action.SERVER_INVALID;
7762         }
7763         this.form.afterAction(this, false);
7764     },
7765     failure : function(response)
7766     {
7767         this.uploadComplete= true;
7768         if (this.haveProgress) {
7769             Roo.MessageBox.hide();
7770         }
7771         
7772         this.response = response;
7773         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7774         this.form.afterAction(this, false);
7775     },
7776     
7777     handleResponse : function(response){
7778         if(this.form.errorReader){
7779             var rs = this.form.errorReader.read(response);
7780             var errors = [];
7781             if(rs.records){
7782                 for(var i = 0, len = rs.records.length; i < len; i++) {
7783                     var r = rs.records[i];
7784                     errors[i] = r.data;
7785                 }
7786             }
7787             if(errors.length < 1){
7788                 errors = null;
7789             }
7790             return {
7791                 success : rs.success,
7792                 errors : errors
7793             };
7794         }
7795         var ret = false;
7796         try {
7797             ret = Roo.decode(response.responseText);
7798         } catch (e) {
7799             ret = {
7800                 success: false,
7801                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7802                 errors : []
7803             };
7804         }
7805         return ret;
7806         
7807     }
7808 });
7809
7810
7811 Roo.form.Action.Load = function(form, options){
7812     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7813     this.reader = this.form.reader;
7814 };
7815
7816 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7817     type : 'load',
7818
7819     run : function(){
7820         
7821         Roo.Ajax.request(Roo.apply(
7822                 this.createCallback(), {
7823                     method:this.getMethod(),
7824                     url:this.getUrl(false),
7825                     params:this.getParams()
7826         }));
7827     },
7828
7829     success : function(response){
7830         
7831         var result = this.processResponse(response);
7832         if(result === true || !result.success || !result.data){
7833             this.failureType = Roo.form.Action.LOAD_FAILURE;
7834             this.form.afterAction(this, false);
7835             return;
7836         }
7837         this.form.clearInvalid();
7838         this.form.setValues(result.data);
7839         this.form.afterAction(this, true);
7840     },
7841
7842     handleResponse : function(response){
7843         if(this.form.reader){
7844             var rs = this.form.reader.read(response);
7845             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7846             return {
7847                 success : rs.success,
7848                 data : data
7849             };
7850         }
7851         return Roo.decode(response.responseText);
7852     }
7853 });
7854
7855 Roo.form.Action.ACTION_TYPES = {
7856     'load' : Roo.form.Action.Load,
7857     'submit' : Roo.form.Action.Submit
7858 };/*
7859  * - LGPL
7860  *
7861  * form
7862  *
7863  */
7864
7865 /**
7866  * @class Roo.bootstrap.Form
7867  * @extends Roo.bootstrap.Component
7868  * Bootstrap Form class
7869  * @cfg {String} method  GET | POST (default POST)
7870  * @cfg {String} labelAlign top | left (default top)
7871  * @cfg {String} align left  | right - for navbars
7872  * @cfg {Boolean} loadMask load mask when submit (default true)
7873
7874  *
7875  * @constructor
7876  * Create a new Form
7877  * @param {Object} config The config object
7878  */
7879
7880
7881 Roo.bootstrap.Form = function(config){
7882     
7883     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7884     
7885     Roo.bootstrap.Form.popover.apply();
7886     
7887     this.addEvents({
7888         /**
7889          * @event clientvalidation
7890          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7891          * @param {Form} this
7892          * @param {Boolean} valid true if the form has passed client-side validation
7893          */
7894         clientvalidation: true,
7895         /**
7896          * @event beforeaction
7897          * Fires before any action is performed. Return false to cancel the action.
7898          * @param {Form} this
7899          * @param {Action} action The action to be performed
7900          */
7901         beforeaction: true,
7902         /**
7903          * @event actionfailed
7904          * Fires when an action fails.
7905          * @param {Form} this
7906          * @param {Action} action The action that failed
7907          */
7908         actionfailed : true,
7909         /**
7910          * @event actioncomplete
7911          * Fires when an action is completed.
7912          * @param {Form} this
7913          * @param {Action} action The action that completed
7914          */
7915         actioncomplete : true
7916     });
7917 };
7918
7919 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7920
7921      /**
7922      * @cfg {String} method
7923      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7924      */
7925     method : 'POST',
7926     /**
7927      * @cfg {String} url
7928      * The URL to use for form actions if one isn't supplied in the action options.
7929      */
7930     /**
7931      * @cfg {Boolean} fileUpload
7932      * Set to true if this form is a file upload.
7933      */
7934
7935     /**
7936      * @cfg {Object} baseParams
7937      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7938      */
7939
7940     /**
7941      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7942      */
7943     timeout: 30,
7944     /**
7945      * @cfg {Sting} align (left|right) for navbar forms
7946      */
7947     align : 'left',
7948
7949     // private
7950     activeAction : null,
7951
7952     /**
7953      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7954      * element by passing it or its id or mask the form itself by passing in true.
7955      * @type Mixed
7956      */
7957     waitMsgTarget : false,
7958
7959     loadMask : true,
7960     
7961     /**
7962      * @cfg {Boolean} errorMask (true|false) default false
7963      */
7964     errorMask : false,
7965     
7966     /**
7967      * @cfg {Number} maskOffset Default 100
7968      */
7969     maskOffset : 100,
7970     
7971     /**
7972      * @cfg {Boolean} maskBody
7973      */
7974     maskBody : false,
7975
7976     getAutoCreate : function(){
7977
7978         var cfg = {
7979             tag: 'form',
7980             method : this.method || 'POST',
7981             id : this.id || Roo.id(),
7982             cls : ''
7983         };
7984         if (this.parent().xtype.match(/^Nav/)) {
7985             cfg.cls = 'navbar-form navbar-' + this.align;
7986
7987         }
7988
7989         if (this.labelAlign == 'left' ) {
7990             cfg.cls += ' form-horizontal';
7991         }
7992
7993
7994         return cfg;
7995     },
7996     initEvents : function()
7997     {
7998         this.el.on('submit', this.onSubmit, this);
7999         // this was added as random key presses on the form where triggering form submit.
8000         this.el.on('keypress', function(e) {
8001             if (e.getCharCode() != 13) {
8002                 return true;
8003             }
8004             // we might need to allow it for textareas.. and some other items.
8005             // check e.getTarget().
8006
8007             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8008                 return true;
8009             }
8010
8011             Roo.log("keypress blocked");
8012
8013             e.preventDefault();
8014             return false;
8015         });
8016         
8017     },
8018     // private
8019     onSubmit : function(e){
8020         e.stopEvent();
8021     },
8022
8023      /**
8024      * Returns true if client-side validation on the form is successful.
8025      * @return Boolean
8026      */
8027     isValid : function(){
8028         var items = this.getItems();
8029         var valid = true;
8030         var target = false;
8031         
8032         items.each(function(f){
8033             
8034             if(f.validate()){
8035                 return;
8036             }
8037             
8038             Roo.log('invalid field: ' + f.name);
8039             
8040             valid = false;
8041
8042             if(!target && f.el.isVisible(true)){
8043                 target = f;
8044             }
8045            
8046         });
8047         
8048         if(this.errorMask && !valid){
8049             Roo.bootstrap.Form.popover.mask(this, target);
8050         }
8051         
8052         return valid;
8053     },
8054     
8055     /**
8056      * Returns true if any fields in this form have changed since their original load.
8057      * @return Boolean
8058      */
8059     isDirty : function(){
8060         var dirty = false;
8061         var items = this.getItems();
8062         items.each(function(f){
8063            if(f.isDirty()){
8064                dirty = true;
8065                return false;
8066            }
8067            return true;
8068         });
8069         return dirty;
8070     },
8071      /**
8072      * Performs a predefined action (submit or load) or custom actions you define on this form.
8073      * @param {String} actionName The name of the action type
8074      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8075      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8076      * accept other config options):
8077      * <pre>
8078 Property          Type             Description
8079 ----------------  ---------------  ----------------------------------------------------------------------------------
8080 url               String           The url for the action (defaults to the form's url)
8081 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8082 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8083 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8084                                    validate the form on the client (defaults to false)
8085      * </pre>
8086      * @return {BasicForm} this
8087      */
8088     doAction : function(action, options){
8089         if(typeof action == 'string'){
8090             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8091         }
8092         if(this.fireEvent('beforeaction', this, action) !== false){
8093             this.beforeAction(action);
8094             action.run.defer(100, action);
8095         }
8096         return this;
8097     },
8098
8099     // private
8100     beforeAction : function(action){
8101         var o = action.options;
8102         
8103         if(this.loadMask){
8104             
8105             if(this.maskBody){
8106                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8107             } else {
8108                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8109             }
8110         }
8111         // not really supported yet.. ??
8112
8113         //if(this.waitMsgTarget === true){
8114         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8115         //}else if(this.waitMsgTarget){
8116         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8117         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8118         //}else {
8119         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8120        // }
8121
8122     },
8123
8124     // private
8125     afterAction : function(action, success){
8126         this.activeAction = null;
8127         var o = action.options;
8128
8129         if(this.loadMask){
8130             
8131             if(this.maskBody){
8132                 Roo.get(document.body).unmask();
8133             } else {
8134                 this.el.unmask();
8135             }
8136         }
8137         
8138         //if(this.waitMsgTarget === true){
8139 //            this.el.unmask();
8140         //}else if(this.waitMsgTarget){
8141         //    this.waitMsgTarget.unmask();
8142         //}else{
8143         //    Roo.MessageBox.updateProgress(1);
8144         //    Roo.MessageBox.hide();
8145        // }
8146         //
8147         if(success){
8148             if(o.reset){
8149                 this.reset();
8150             }
8151             Roo.callback(o.success, o.scope, [this, action]);
8152             this.fireEvent('actioncomplete', this, action);
8153
8154         }else{
8155
8156             // failure condition..
8157             // we have a scenario where updates need confirming.
8158             // eg. if a locking scenario exists..
8159             // we look for { errors : { needs_confirm : true }} in the response.
8160             if (
8161                 (typeof(action.result) != 'undefined')  &&
8162                 (typeof(action.result.errors) != 'undefined')  &&
8163                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8164            ){
8165                 var _t = this;
8166                 Roo.log("not supported yet");
8167                  /*
8168
8169                 Roo.MessageBox.confirm(
8170                     "Change requires confirmation",
8171                     action.result.errorMsg,
8172                     function(r) {
8173                         if (r != 'yes') {
8174                             return;
8175                         }
8176                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8177                     }
8178
8179                 );
8180                 */
8181
8182
8183                 return;
8184             }
8185
8186             Roo.callback(o.failure, o.scope, [this, action]);
8187             // show an error message if no failed handler is set..
8188             if (!this.hasListener('actionfailed')) {
8189                 Roo.log("need to add dialog support");
8190                 /*
8191                 Roo.MessageBox.alert("Error",
8192                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8193                         action.result.errorMsg :
8194                         "Saving Failed, please check your entries or try again"
8195                 );
8196                 */
8197             }
8198
8199             this.fireEvent('actionfailed', this, action);
8200         }
8201
8202     },
8203     /**
8204      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8205      * @param {String} id The value to search for
8206      * @return Field
8207      */
8208     findField : function(id){
8209         var items = this.getItems();
8210         var field = items.get(id);
8211         if(!field){
8212              items.each(function(f){
8213                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8214                     field = f;
8215                     return false;
8216                 }
8217                 return true;
8218             });
8219         }
8220         return field || null;
8221     },
8222      /**
8223      * Mark fields in this form invalid in bulk.
8224      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8225      * @return {BasicForm} this
8226      */
8227     markInvalid : function(errors){
8228         if(errors instanceof Array){
8229             for(var i = 0, len = errors.length; i < len; i++){
8230                 var fieldError = errors[i];
8231                 var f = this.findField(fieldError.id);
8232                 if(f){
8233                     f.markInvalid(fieldError.msg);
8234                 }
8235             }
8236         }else{
8237             var field, id;
8238             for(id in errors){
8239                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8240                     field.markInvalid(errors[id]);
8241                 }
8242             }
8243         }
8244         //Roo.each(this.childForms || [], function (f) {
8245         //    f.markInvalid(errors);
8246         //});
8247
8248         return this;
8249     },
8250
8251     /**
8252      * Set values for fields in this form in bulk.
8253      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8254      * @return {BasicForm} this
8255      */
8256     setValues : function(values){
8257         if(values instanceof Array){ // array of objects
8258             for(var i = 0, len = values.length; i < len; i++){
8259                 var v = values[i];
8260                 var f = this.findField(v.id);
8261                 if(f){
8262                     f.setValue(v.value);
8263                     if(this.trackResetOnLoad){
8264                         f.originalValue = f.getValue();
8265                     }
8266                 }
8267             }
8268         }else{ // object hash
8269             var field, id;
8270             for(id in values){
8271                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8272
8273                     if (field.setFromData &&
8274                         field.valueField &&
8275                         field.displayField &&
8276                         // combos' with local stores can
8277                         // be queried via setValue()
8278                         // to set their value..
8279                         (field.store && !field.store.isLocal)
8280                         ) {
8281                         // it's a combo
8282                         var sd = { };
8283                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8284                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8285                         field.setFromData(sd);
8286
8287                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8288                         
8289                         field.setFromData(values);
8290                         
8291                     } else {
8292                         field.setValue(values[id]);
8293                     }
8294
8295
8296                     if(this.trackResetOnLoad){
8297                         field.originalValue = field.getValue();
8298                     }
8299                 }
8300             }
8301         }
8302
8303         //Roo.each(this.childForms || [], function (f) {
8304         //    f.setValues(values);
8305         //});
8306
8307         return this;
8308     },
8309
8310     /**
8311      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8312      * they are returned as an array.
8313      * @param {Boolean} asString
8314      * @return {Object}
8315      */
8316     getValues : function(asString){
8317         //if (this.childForms) {
8318             // copy values from the child forms
8319         //    Roo.each(this.childForms, function (f) {
8320         //        this.setValues(f.getValues());
8321         //    }, this);
8322         //}
8323
8324
8325
8326         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8327         if(asString === true){
8328             return fs;
8329         }
8330         return Roo.urlDecode(fs);
8331     },
8332
8333     /**
8334      * Returns the fields in this form as an object with key/value pairs.
8335      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8336      * @return {Object}
8337      */
8338     getFieldValues : function(with_hidden)
8339     {
8340         var items = this.getItems();
8341         var ret = {};
8342         items.each(function(f){
8343             
8344             if (!f.getName()) {
8345                 return;
8346             }
8347             
8348             var v = f.getValue();
8349             
8350             if (f.inputType =='radio') {
8351                 if (typeof(ret[f.getName()]) == 'undefined') {
8352                     ret[f.getName()] = ''; // empty..
8353                 }
8354
8355                 if (!f.el.dom.checked) {
8356                     return;
8357
8358                 }
8359                 v = f.el.dom.value;
8360
8361             }
8362             
8363             if(f.xtype == 'MoneyField'){
8364                 ret[f.currencyName] = f.getCurrency();
8365             }
8366
8367             // not sure if this supported any more..
8368             if ((typeof(v) == 'object') && f.getRawValue) {
8369                 v = f.getRawValue() ; // dates..
8370             }
8371             // combo boxes where name != hiddenName...
8372             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8373                 ret[f.name] = f.getRawValue();
8374             }
8375             ret[f.getName()] = v;
8376         });
8377
8378         return ret;
8379     },
8380
8381     /**
8382      * Clears all invalid messages in this form.
8383      * @return {BasicForm} this
8384      */
8385     clearInvalid : function(){
8386         var items = this.getItems();
8387
8388         items.each(function(f){
8389            f.clearInvalid();
8390         });
8391
8392         return this;
8393     },
8394
8395     /**
8396      * Resets this form.
8397      * @return {BasicForm} this
8398      */
8399     reset : function(){
8400         var items = this.getItems();
8401         items.each(function(f){
8402             f.reset();
8403         });
8404
8405         Roo.each(this.childForms || [], function (f) {
8406             f.reset();
8407         });
8408
8409
8410         return this;
8411     },
8412     
8413     getItems : function()
8414     {
8415         var r=new Roo.util.MixedCollection(false, function(o){
8416             return o.id || (o.id = Roo.id());
8417         });
8418         var iter = function(el) {
8419             if (el.inputEl) {
8420                 r.add(el);
8421             }
8422             if (!el.items) {
8423                 return;
8424             }
8425             Roo.each(el.items,function(e) {
8426                 iter(e);
8427             });
8428         };
8429
8430         iter(this);
8431         return r;
8432     },
8433     
8434     hideFields : function(items)
8435     {
8436         Roo.each(items, function(i){
8437             
8438             var f = this.findField(i);
8439             
8440             if(!f){
8441                 return;
8442             }
8443             
8444             f.hide();
8445             
8446         }, this);
8447     },
8448     
8449     showFields : function(items)
8450     {
8451         Roo.each(items, function(i){
8452             
8453             var f = this.findField(i);
8454             
8455             if(!f){
8456                 return;
8457             }
8458             
8459             f.show();
8460             
8461         }, this);
8462     }
8463
8464 });
8465
8466 Roo.apply(Roo.bootstrap.Form, {
8467     
8468     popover : {
8469         
8470         padding : 5,
8471         
8472         isApplied : false,
8473         
8474         isMasked : false,
8475         
8476         form : false,
8477         
8478         target : false,
8479         
8480         toolTip : false,
8481         
8482         intervalID : false,
8483         
8484         maskEl : false,
8485         
8486         apply : function()
8487         {
8488             if(this.isApplied){
8489                 return;
8490             }
8491             
8492             this.maskEl = {
8493                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8494                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8495                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8496                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8497             };
8498             
8499             this.maskEl.top.enableDisplayMode("block");
8500             this.maskEl.left.enableDisplayMode("block");
8501             this.maskEl.bottom.enableDisplayMode("block");
8502             this.maskEl.right.enableDisplayMode("block");
8503             
8504             this.toolTip = new Roo.bootstrap.Tooltip({
8505                 cls : 'roo-form-error-popover',
8506                 alignment : {
8507                     'left' : ['r-l', [-2,0], 'right'],
8508                     'right' : ['l-r', [2,0], 'left'],
8509                     'bottom' : ['tl-bl', [0,2], 'top'],
8510                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8511                 }
8512             });
8513             
8514             this.toolTip.render(Roo.get(document.body));
8515
8516             this.toolTip.el.enableDisplayMode("block");
8517             
8518             Roo.get(document.body).on('click', function(){
8519                 this.unmask();
8520             }, this);
8521             
8522             Roo.get(document.body).on('touchstart', function(){
8523                 this.unmask();
8524             }, this);
8525             
8526             this.isApplied = true
8527         },
8528         
8529         mask : function(form, target)
8530         {
8531             this.form = form;
8532             
8533             this.target = target;
8534             
8535             if(!this.form.errorMask || !target.el){
8536                 return;
8537             }
8538             
8539             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8540             
8541             Roo.log(scrollable);
8542             
8543             var ot = this.target.el.calcOffsetsTo(scrollable);
8544             
8545             var scrollTo = ot[1] - this.form.maskOffset;
8546             
8547             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8548             
8549             scrollable.scrollTo('top', scrollTo);
8550             
8551             var box = this.target.el.getBox();
8552             Roo.log(box);
8553             var zIndex = Roo.bootstrap.Modal.zIndex++;
8554
8555             
8556             this.maskEl.top.setStyle('position', 'absolute');
8557             this.maskEl.top.setStyle('z-index', zIndex);
8558             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8559             this.maskEl.top.setLeft(0);
8560             this.maskEl.top.setTop(0);
8561             this.maskEl.top.show();
8562             
8563             this.maskEl.left.setStyle('position', 'absolute');
8564             this.maskEl.left.setStyle('z-index', zIndex);
8565             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8566             this.maskEl.left.setLeft(0);
8567             this.maskEl.left.setTop(box.y - this.padding);
8568             this.maskEl.left.show();
8569
8570             this.maskEl.bottom.setStyle('position', 'absolute');
8571             this.maskEl.bottom.setStyle('z-index', zIndex);
8572             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8573             this.maskEl.bottom.setLeft(0);
8574             this.maskEl.bottom.setTop(box.bottom + this.padding);
8575             this.maskEl.bottom.show();
8576
8577             this.maskEl.right.setStyle('position', 'absolute');
8578             this.maskEl.right.setStyle('z-index', zIndex);
8579             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8580             this.maskEl.right.setLeft(box.right + this.padding);
8581             this.maskEl.right.setTop(box.y - this.padding);
8582             this.maskEl.right.show();
8583
8584             this.toolTip.bindEl = this.target.el;
8585
8586             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8587
8588             var tip = this.target.blankText;
8589
8590             if(this.target.getValue() !== '' ) {
8591                 
8592                 if (this.target.invalidText.length) {
8593                     tip = this.target.invalidText;
8594                 } else if (this.target.regexText.length){
8595                     tip = this.target.regexText;
8596                 }
8597             }
8598
8599             this.toolTip.show(tip);
8600
8601             this.intervalID = window.setInterval(function() {
8602                 Roo.bootstrap.Form.popover.unmask();
8603             }, 10000);
8604
8605             window.onwheel = function(){ return false;};
8606             
8607             (function(){ this.isMasked = true; }).defer(500, this);
8608             
8609         },
8610         
8611         unmask : function()
8612         {
8613             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8614                 return;
8615             }
8616             
8617             this.maskEl.top.setStyle('position', 'absolute');
8618             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8619             this.maskEl.top.hide();
8620
8621             this.maskEl.left.setStyle('position', 'absolute');
8622             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8623             this.maskEl.left.hide();
8624
8625             this.maskEl.bottom.setStyle('position', 'absolute');
8626             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8627             this.maskEl.bottom.hide();
8628
8629             this.maskEl.right.setStyle('position', 'absolute');
8630             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8631             this.maskEl.right.hide();
8632             
8633             this.toolTip.hide();
8634             
8635             this.toolTip.el.hide();
8636             
8637             window.onwheel = function(){ return true;};
8638             
8639             if(this.intervalID){
8640                 window.clearInterval(this.intervalID);
8641                 this.intervalID = false;
8642             }
8643             
8644             this.isMasked = false;
8645             
8646         }
8647         
8648     }
8649     
8650 });
8651
8652 /*
8653  * Based on:
8654  * Ext JS Library 1.1.1
8655  * Copyright(c) 2006-2007, Ext JS, LLC.
8656  *
8657  * Originally Released Under LGPL - original licence link has changed is not relivant.
8658  *
8659  * Fork - LGPL
8660  * <script type="text/javascript">
8661  */
8662 /**
8663  * @class Roo.form.VTypes
8664  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8665  * @singleton
8666  */
8667 Roo.form.VTypes = function(){
8668     // closure these in so they are only created once.
8669     var alpha = /^[a-zA-Z_]+$/;
8670     var alphanum = /^[a-zA-Z0-9_]+$/;
8671     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8672     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8673
8674     // All these messages and functions are configurable
8675     return {
8676         /**
8677          * The function used to validate email addresses
8678          * @param {String} value The email address
8679          */
8680         'email' : function(v){
8681             return email.test(v);
8682         },
8683         /**
8684          * The error text to display when the email validation function returns false
8685          * @type String
8686          */
8687         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8688         /**
8689          * The keystroke filter mask to be applied on email input
8690          * @type RegExp
8691          */
8692         'emailMask' : /[a-z0-9_\.\-@]/i,
8693
8694         /**
8695          * The function used to validate URLs
8696          * @param {String} value The URL
8697          */
8698         'url' : function(v){
8699             return url.test(v);
8700         },
8701         /**
8702          * The error text to display when the url validation function returns false
8703          * @type String
8704          */
8705         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8706         
8707         /**
8708          * The function used to validate alpha values
8709          * @param {String} value The value
8710          */
8711         'alpha' : function(v){
8712             return alpha.test(v);
8713         },
8714         /**
8715          * The error text to display when the alpha validation function returns false
8716          * @type String
8717          */
8718         'alphaText' : 'This field should only contain letters and _',
8719         /**
8720          * The keystroke filter mask to be applied on alpha input
8721          * @type RegExp
8722          */
8723         'alphaMask' : /[a-z_]/i,
8724
8725         /**
8726          * The function used to validate alphanumeric values
8727          * @param {String} value The value
8728          */
8729         'alphanum' : function(v){
8730             return alphanum.test(v);
8731         },
8732         /**
8733          * The error text to display when the alphanumeric validation function returns false
8734          * @type String
8735          */
8736         'alphanumText' : 'This field should only contain letters, numbers and _',
8737         /**
8738          * The keystroke filter mask to be applied on alphanumeric input
8739          * @type RegExp
8740          */
8741         'alphanumMask' : /[a-z0-9_]/i
8742     };
8743 }();/*
8744  * - LGPL
8745  *
8746  * Input
8747  * 
8748  */
8749
8750 /**
8751  * @class Roo.bootstrap.Input
8752  * @extends Roo.bootstrap.Component
8753  * Bootstrap Input class
8754  * @cfg {Boolean} disabled is it disabled
8755  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8756  * @cfg {String} name name of the input
8757  * @cfg {string} fieldLabel - the label associated
8758  * @cfg {string} placeholder - placeholder to put in text.
8759  * @cfg {string}  before - input group add on before
8760  * @cfg {string} after - input group add on after
8761  * @cfg {string} size - (lg|sm) or leave empty..
8762  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8763  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8764  * @cfg {Number} md colspan out of 12 for computer-sized screens
8765  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8766  * @cfg {string} value default value of the input
8767  * @cfg {Number} labelWidth set the width of label 
8768  * @cfg {Number} labellg set the width of label (1-12)
8769  * @cfg {Number} labelmd set the width of label (1-12)
8770  * @cfg {Number} labelsm set the width of label (1-12)
8771  * @cfg {Number} labelxs set the width of label (1-12)
8772  * @cfg {String} labelAlign (top|left)
8773  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8774  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8775  * @cfg {String} indicatorpos (left|right) default left
8776  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8777  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8778
8779  * @cfg {String} align (left|center|right) Default left
8780  * @cfg {Boolean} forceFeedback (true|false) Default false
8781  * 
8782  * @constructor
8783  * Create a new Input
8784  * @param {Object} config The config object
8785  */
8786
8787 Roo.bootstrap.Input = function(config){
8788     
8789     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8790     
8791     this.addEvents({
8792         /**
8793          * @event focus
8794          * Fires when this field receives input focus.
8795          * @param {Roo.form.Field} this
8796          */
8797         focus : true,
8798         /**
8799          * @event blur
8800          * Fires when this field loses input focus.
8801          * @param {Roo.form.Field} this
8802          */
8803         blur : true,
8804         /**
8805          * @event specialkey
8806          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8807          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8808          * @param {Roo.form.Field} this
8809          * @param {Roo.EventObject} e The event object
8810          */
8811         specialkey : true,
8812         /**
8813          * @event change
8814          * Fires just before the field blurs if the field value has changed.
8815          * @param {Roo.form.Field} this
8816          * @param {Mixed} newValue The new value
8817          * @param {Mixed} oldValue The original value
8818          */
8819         change : true,
8820         /**
8821          * @event invalid
8822          * Fires after the field has been marked as invalid.
8823          * @param {Roo.form.Field} this
8824          * @param {String} msg The validation message
8825          */
8826         invalid : true,
8827         /**
8828          * @event valid
8829          * Fires after the field has been validated with no errors.
8830          * @param {Roo.form.Field} this
8831          */
8832         valid : true,
8833          /**
8834          * @event keyup
8835          * Fires after the key up
8836          * @param {Roo.form.Field} this
8837          * @param {Roo.EventObject}  e The event Object
8838          */
8839         keyup : true
8840     });
8841 };
8842
8843 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8844      /**
8845      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8846       automatic validation (defaults to "keyup").
8847      */
8848     validationEvent : "keyup",
8849      /**
8850      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8851      */
8852     validateOnBlur : true,
8853     /**
8854      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8855      */
8856     validationDelay : 250,
8857      /**
8858      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8859      */
8860     focusClass : "x-form-focus",  // not needed???
8861     
8862        
8863     /**
8864      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8865      */
8866     invalidClass : "has-warning",
8867     
8868     /**
8869      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8870      */
8871     validClass : "has-success",
8872     
8873     /**
8874      * @cfg {Boolean} hasFeedback (true|false) default true
8875      */
8876     hasFeedback : true,
8877     
8878     /**
8879      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8880      */
8881     invalidFeedbackClass : "glyphicon-warning-sign",
8882     
8883     /**
8884      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8885      */
8886     validFeedbackClass : "glyphicon-ok",
8887     
8888     /**
8889      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8890      */
8891     selectOnFocus : false,
8892     
8893      /**
8894      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8895      */
8896     maskRe : null,
8897        /**
8898      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8899      */
8900     vtype : null,
8901     
8902       /**
8903      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8904      */
8905     disableKeyFilter : false,
8906     
8907        /**
8908      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8909      */
8910     disabled : false,
8911      /**
8912      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8913      */
8914     allowBlank : true,
8915     /**
8916      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8917      */
8918     blankText : "Please complete this mandatory field",
8919     
8920      /**
8921      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8922      */
8923     minLength : 0,
8924     /**
8925      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8926      */
8927     maxLength : Number.MAX_VALUE,
8928     /**
8929      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8930      */
8931     minLengthText : "The minimum length for this field is {0}",
8932     /**
8933      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8934      */
8935     maxLengthText : "The maximum length for this field is {0}",
8936   
8937     
8938     /**
8939      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8940      * If available, this function will be called only after the basic validators all return true, and will be passed the
8941      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8942      */
8943     validator : null,
8944     /**
8945      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8946      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8947      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8948      */
8949     regex : null,
8950     /**
8951      * @cfg {String} regexText -- Depricated - use Invalid Text
8952      */
8953     regexText : "",
8954     
8955     /**
8956      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8957      */
8958     invalidText : "",
8959     
8960     
8961     
8962     autocomplete: false,
8963     
8964     
8965     fieldLabel : '',
8966     inputType : 'text',
8967     
8968     name : false,
8969     placeholder: false,
8970     before : false,
8971     after : false,
8972     size : false,
8973     hasFocus : false,
8974     preventMark: false,
8975     isFormField : true,
8976     value : '',
8977     labelWidth : 2,
8978     labelAlign : false,
8979     readOnly : false,
8980     align : false,
8981     formatedValue : false,
8982     forceFeedback : false,
8983     
8984     indicatorpos : 'left',
8985     
8986     labellg : 0,
8987     labelmd : 0,
8988     labelsm : 0,
8989     labelxs : 0,
8990     
8991     capture : '',
8992     accept : '',
8993     
8994     parentLabelAlign : function()
8995     {
8996         var parent = this;
8997         while (parent.parent()) {
8998             parent = parent.parent();
8999             if (typeof(parent.labelAlign) !='undefined') {
9000                 return parent.labelAlign;
9001             }
9002         }
9003         return 'left';
9004         
9005     },
9006     
9007     getAutoCreate : function()
9008     {
9009         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9010         
9011         var id = Roo.id();
9012         
9013         var cfg = {};
9014         
9015         if(this.inputType != 'hidden'){
9016             cfg.cls = 'form-group' //input-group
9017         }
9018         
9019         var input =  {
9020             tag: 'input',
9021             id : id,
9022             type : this.inputType,
9023             value : this.value,
9024             cls : 'form-control',
9025             placeholder : this.placeholder || '',
9026             autocomplete : this.autocomplete || 'new-password'
9027         };
9028         
9029         if(this.capture.length){
9030             input.capture = this.capture;
9031         }
9032         
9033         if(this.accept.length){
9034             input.accept = this.accept + "/*";
9035         }
9036         
9037         if(this.align){
9038             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9039         }
9040         
9041         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9042             input.maxLength = this.maxLength;
9043         }
9044         
9045         if (this.disabled) {
9046             input.disabled=true;
9047         }
9048         
9049         if (this.readOnly) {
9050             input.readonly=true;
9051         }
9052         
9053         if (this.name) {
9054             input.name = this.name;
9055         }
9056         
9057         if (this.size) {
9058             input.cls += ' input-' + this.size;
9059         }
9060         
9061         var settings=this;
9062         ['xs','sm','md','lg'].map(function(size){
9063             if (settings[size]) {
9064                 cfg.cls += ' col-' + size + '-' + settings[size];
9065             }
9066         });
9067         
9068         var inputblock = input;
9069         
9070         var feedback = {
9071             tag: 'span',
9072             cls: 'glyphicon form-control-feedback'
9073         };
9074             
9075         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9076             
9077             inputblock = {
9078                 cls : 'has-feedback',
9079                 cn :  [
9080                     input,
9081                     feedback
9082                 ] 
9083             };  
9084         }
9085         
9086         if (this.before || this.after) {
9087             
9088             inputblock = {
9089                 cls : 'input-group',
9090                 cn :  [] 
9091             };
9092             
9093             if (this.before && typeof(this.before) == 'string') {
9094                 
9095                 inputblock.cn.push({
9096                     tag :'span',
9097                     cls : 'roo-input-before input-group-addon',
9098                     html : this.before
9099                 });
9100             }
9101             if (this.before && typeof(this.before) == 'object') {
9102                 this.before = Roo.factory(this.before);
9103                 
9104                 inputblock.cn.push({
9105                     tag :'span',
9106                     cls : 'roo-input-before input-group-' +
9107                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9108                 });
9109             }
9110             
9111             inputblock.cn.push(input);
9112             
9113             if (this.after && typeof(this.after) == 'string') {
9114                 inputblock.cn.push({
9115                     tag :'span',
9116                     cls : 'roo-input-after input-group-addon',
9117                     html : this.after
9118                 });
9119             }
9120             if (this.after && typeof(this.after) == 'object') {
9121                 this.after = Roo.factory(this.after);
9122                 
9123                 inputblock.cn.push({
9124                     tag :'span',
9125                     cls : 'roo-input-after input-group-' +
9126                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9127                 });
9128             }
9129             
9130             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9131                 inputblock.cls += ' has-feedback';
9132                 inputblock.cn.push(feedback);
9133             }
9134         };
9135         
9136         if (align ==='left' && this.fieldLabel.length) {
9137             
9138             cfg.cls += ' roo-form-group-label-left';
9139             
9140             cfg.cn = [
9141                 {
9142                     tag : 'i',
9143                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9144                     tooltip : 'This field is required'
9145                 },
9146                 {
9147                     tag: 'label',
9148                     'for' :  id,
9149                     cls : 'control-label',
9150                     html : this.fieldLabel
9151
9152                 },
9153                 {
9154                     cls : "", 
9155                     cn: [
9156                         inputblock
9157                     ]
9158                 }
9159             ];
9160             
9161             var labelCfg = cfg.cn[1];
9162             var contentCfg = cfg.cn[2];
9163             
9164             if(this.indicatorpos == 'right'){
9165                 cfg.cn = [
9166                     {
9167                         tag: 'label',
9168                         'for' :  id,
9169                         cls : 'control-label',
9170                         cn : [
9171                             {
9172                                 tag : 'span',
9173                                 html : this.fieldLabel
9174                             },
9175                             {
9176                                 tag : 'i',
9177                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9178                                 tooltip : 'This field is required'
9179                             }
9180                         ]
9181                     },
9182                     {
9183                         cls : "",
9184                         cn: [
9185                             inputblock
9186                         ]
9187                     }
9188
9189                 ];
9190                 
9191                 labelCfg = cfg.cn[0];
9192                 contentCfg = cfg.cn[1];
9193             
9194             }
9195             
9196             if(this.labelWidth > 12){
9197                 labelCfg.style = "width: " + this.labelWidth + 'px';
9198             }
9199             
9200             if(this.labelWidth < 13 && this.labelmd == 0){
9201                 this.labelmd = this.labelWidth;
9202             }
9203             
9204             if(this.labellg > 0){
9205                 labelCfg.cls += ' col-lg-' + this.labellg;
9206                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9207             }
9208             
9209             if(this.labelmd > 0){
9210                 labelCfg.cls += ' col-md-' + this.labelmd;
9211                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9212             }
9213             
9214             if(this.labelsm > 0){
9215                 labelCfg.cls += ' col-sm-' + this.labelsm;
9216                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9217             }
9218             
9219             if(this.labelxs > 0){
9220                 labelCfg.cls += ' col-xs-' + this.labelxs;
9221                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9222             }
9223             
9224             
9225         } else if ( this.fieldLabel.length) {
9226                 
9227             cfg.cn = [
9228                 {
9229                     tag : 'i',
9230                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9231                     tooltip : 'This field is required'
9232                 },
9233                 {
9234                     tag: 'label',
9235                    //cls : 'input-group-addon',
9236                     html : this.fieldLabel
9237
9238                 },
9239
9240                inputblock
9241
9242            ];
9243            
9244            if(this.indicatorpos == 'right'){
9245                 
9246                 cfg.cn = [
9247                     {
9248                         tag: 'label',
9249                        //cls : 'input-group-addon',
9250                         html : this.fieldLabel
9251
9252                     },
9253                     {
9254                         tag : 'i',
9255                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9256                         tooltip : 'This field is required'
9257                     },
9258
9259                    inputblock
9260
9261                ];
9262
9263             }
9264
9265         } else {
9266             
9267             cfg.cn = [
9268
9269                     inputblock
9270
9271             ];
9272                 
9273                 
9274         };
9275         
9276         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9277            cfg.cls += ' navbar-form';
9278         }
9279         
9280         if (this.parentType === 'NavGroup') {
9281            cfg.cls += ' navbar-form';
9282            cfg.tag = 'li';
9283         }
9284         
9285         return cfg;
9286         
9287     },
9288     /**
9289      * return the real input element.
9290      */
9291     inputEl: function ()
9292     {
9293         return this.el.select('input.form-control',true).first();
9294     },
9295     
9296     tooltipEl : function()
9297     {
9298         return this.inputEl();
9299     },
9300     
9301     indicatorEl : function()
9302     {
9303         var indicator = this.el.select('i.roo-required-indicator',true).first();
9304         
9305         if(!indicator){
9306             return false;
9307         }
9308         
9309         return indicator;
9310         
9311     },
9312     
9313     setDisabled : function(v)
9314     {
9315         var i  = this.inputEl().dom;
9316         if (!v) {
9317             i.removeAttribute('disabled');
9318             return;
9319             
9320         }
9321         i.setAttribute('disabled','true');
9322     },
9323     initEvents : function()
9324     {
9325           
9326         this.inputEl().on("keydown" , this.fireKey,  this);
9327         this.inputEl().on("focus", this.onFocus,  this);
9328         this.inputEl().on("blur", this.onBlur,  this);
9329         
9330         this.inputEl().relayEvent('keyup', this);
9331         
9332         this.indicator = this.indicatorEl();
9333         
9334         if(this.indicator){
9335             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9336         }
9337  
9338         // reference to original value for reset
9339         this.originalValue = this.getValue();
9340         //Roo.form.TextField.superclass.initEvents.call(this);
9341         if(this.validationEvent == 'keyup'){
9342             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9343             this.inputEl().on('keyup', this.filterValidation, this);
9344         }
9345         else if(this.validationEvent !== false){
9346             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9347         }
9348         
9349         if(this.selectOnFocus){
9350             this.on("focus", this.preFocus, this);
9351             
9352         }
9353         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9354             this.inputEl().on("keypress", this.filterKeys, this);
9355         } else {
9356             this.inputEl().relayEvent('keypress', this);
9357         }
9358        /* if(this.grow){
9359             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9360             this.el.on("click", this.autoSize,  this);
9361         }
9362         */
9363         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9364             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9365         }
9366         
9367         if (typeof(this.before) == 'object') {
9368             this.before.render(this.el.select('.roo-input-before',true).first());
9369         }
9370         if (typeof(this.after) == 'object') {
9371             this.after.render(this.el.select('.roo-input-after',true).first());
9372         }
9373         
9374         this.inputEl().on('change', this.onChange, this);
9375         
9376     },
9377     filterValidation : function(e){
9378         if(!e.isNavKeyPress()){
9379             this.validationTask.delay(this.validationDelay);
9380         }
9381     },
9382      /**
9383      * Validates the field value
9384      * @return {Boolean} True if the value is valid, else false
9385      */
9386     validate : function(){
9387         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9388         if(this.disabled || this.validateValue(this.getRawValue())){
9389             this.markValid();
9390             return true;
9391         }
9392         
9393         this.markInvalid();
9394         return false;
9395     },
9396     
9397     
9398     /**
9399      * Validates a value according to the field's validation rules and marks the field as invalid
9400      * if the validation fails
9401      * @param {Mixed} value The value to validate
9402      * @return {Boolean} True if the value is valid, else false
9403      */
9404     validateValue : function(value)
9405     {
9406         if(this.getVisibilityEl().hasClass('hidden')){
9407             return true;
9408         }
9409         
9410         if(value.length < 1)  { // if it's blank
9411             if(this.allowBlank){
9412                 return true;
9413             }
9414             return false;
9415         }
9416         
9417         if(value.length < this.minLength){
9418             return false;
9419         }
9420         if(value.length > this.maxLength){
9421             return false;
9422         }
9423         if(this.vtype){
9424             var vt = Roo.form.VTypes;
9425             if(!vt[this.vtype](value, this)){
9426                 return false;
9427             }
9428         }
9429         if(typeof this.validator == "function"){
9430             var msg = this.validator(value);
9431             if(msg !== true){
9432                 return false;
9433             }
9434             if (typeof(msg) == 'string') {
9435                 this.invalidText = msg;
9436             }
9437         }
9438         
9439         if(this.regex && !this.regex.test(value)){
9440             return false;
9441         }
9442         
9443         return true;
9444     },
9445     
9446      // private
9447     fireKey : function(e){
9448         //Roo.log('field ' + e.getKey());
9449         if(e.isNavKeyPress()){
9450             this.fireEvent("specialkey", this, e);
9451         }
9452     },
9453     focus : function (selectText){
9454         if(this.rendered){
9455             this.inputEl().focus();
9456             if(selectText === true){
9457                 this.inputEl().dom.select();
9458             }
9459         }
9460         return this;
9461     } ,
9462     
9463     onFocus : function(){
9464         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9465            // this.el.addClass(this.focusClass);
9466         }
9467         if(!this.hasFocus){
9468             this.hasFocus = true;
9469             this.startValue = this.getValue();
9470             this.fireEvent("focus", this);
9471         }
9472     },
9473     
9474     beforeBlur : Roo.emptyFn,
9475
9476     
9477     // private
9478     onBlur : function(){
9479         this.beforeBlur();
9480         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9481             //this.el.removeClass(this.focusClass);
9482         }
9483         this.hasFocus = false;
9484         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9485             this.validate();
9486         }
9487         var v = this.getValue();
9488         if(String(v) !== String(this.startValue)){
9489             this.fireEvent('change', this, v, this.startValue);
9490         }
9491         this.fireEvent("blur", this);
9492     },
9493     
9494     onChange : function(e)
9495     {
9496         var v = this.getValue();
9497         if(String(v) !== String(this.startValue)){
9498             this.fireEvent('change', this, v, this.startValue);
9499         }
9500         
9501     },
9502     
9503     /**
9504      * Resets the current field value to the originally loaded value and clears any validation messages
9505      */
9506     reset : function(){
9507         this.setValue(this.originalValue);
9508         this.validate();
9509     },
9510      /**
9511      * Returns the name of the field
9512      * @return {Mixed} name The name field
9513      */
9514     getName: function(){
9515         return this.name;
9516     },
9517      /**
9518      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9519      * @return {Mixed} value The field value
9520      */
9521     getValue : function(){
9522         
9523         var v = this.inputEl().getValue();
9524         
9525         return v;
9526     },
9527     /**
9528      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9529      * @return {Mixed} value The field value
9530      */
9531     getRawValue : function(){
9532         var v = this.inputEl().getValue();
9533         
9534         return v;
9535     },
9536     
9537     /**
9538      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9539      * @param {Mixed} value The value to set
9540      */
9541     setRawValue : function(v){
9542         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9543     },
9544     
9545     selectText : function(start, end){
9546         var v = this.getRawValue();
9547         if(v.length > 0){
9548             start = start === undefined ? 0 : start;
9549             end = end === undefined ? v.length : end;
9550             var d = this.inputEl().dom;
9551             if(d.setSelectionRange){
9552                 d.setSelectionRange(start, end);
9553             }else if(d.createTextRange){
9554                 var range = d.createTextRange();
9555                 range.moveStart("character", start);
9556                 range.moveEnd("character", v.length-end);
9557                 range.select();
9558             }
9559         }
9560     },
9561     
9562     /**
9563      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9564      * @param {Mixed} value The value to set
9565      */
9566     setValue : function(v){
9567         this.value = v;
9568         if(this.rendered){
9569             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9570             this.validate();
9571         }
9572     },
9573     
9574     /*
9575     processValue : function(value){
9576         if(this.stripCharsRe){
9577             var newValue = value.replace(this.stripCharsRe, '');
9578             if(newValue !== value){
9579                 this.setRawValue(newValue);
9580                 return newValue;
9581             }
9582         }
9583         return value;
9584     },
9585   */
9586     preFocus : function(){
9587         
9588         if(this.selectOnFocus){
9589             this.inputEl().dom.select();
9590         }
9591     },
9592     filterKeys : function(e){
9593         var k = e.getKey();
9594         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9595             return;
9596         }
9597         var c = e.getCharCode(), cc = String.fromCharCode(c);
9598         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9599             return;
9600         }
9601         if(!this.maskRe.test(cc)){
9602             e.stopEvent();
9603         }
9604     },
9605      /**
9606      * Clear any invalid styles/messages for this field
9607      */
9608     clearInvalid : function(){
9609         
9610         if(!this.el || this.preventMark){ // not rendered
9611             return;
9612         }
9613         
9614      
9615         this.el.removeClass(this.invalidClass);
9616         
9617         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9618             
9619             var feedback = this.el.select('.form-control-feedback', true).first();
9620             
9621             if(feedback){
9622                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9623             }
9624             
9625         }
9626         
9627         if(this.indicator){
9628             this.indicator.removeClass('visible');
9629             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9630         }
9631         
9632         this.fireEvent('valid', this);
9633     },
9634     
9635      /**
9636      * Mark this field as valid
9637      */
9638     markValid : function()
9639     {
9640         if(!this.el  || this.preventMark){ // not rendered...
9641             return;
9642         }
9643         
9644         this.el.removeClass([this.invalidClass, this.validClass]);
9645         
9646         var feedback = this.el.select('.form-control-feedback', true).first();
9647             
9648         if(feedback){
9649             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9650         }
9651         
9652         if(this.indicator){
9653             this.indicator.removeClass('visible');
9654             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9655         }
9656         
9657         if(this.disabled){
9658             return;
9659         }
9660         
9661         if(this.allowBlank && !this.getRawValue().length){
9662             return;
9663         }
9664         
9665         this.el.addClass(this.validClass);
9666         
9667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9674             }
9675             
9676         }
9677         
9678         this.fireEvent('valid', this);
9679     },
9680     
9681      /**
9682      * Mark this field as invalid
9683      * @param {String} msg The validation message
9684      */
9685     markInvalid : function(msg)
9686     {
9687         if(!this.el  || this.preventMark){ // not rendered
9688             return;
9689         }
9690         
9691         this.el.removeClass([this.invalidClass, this.validClass]);
9692         
9693         var feedback = this.el.select('.form-control-feedback', true).first();
9694             
9695         if(feedback){
9696             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9697         }
9698
9699         if(this.disabled){
9700             return;
9701         }
9702         
9703         if(this.allowBlank && !this.getRawValue().length){
9704             return;
9705         }
9706         
9707         if(this.indicator){
9708             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9709             this.indicator.addClass('visible');
9710         }
9711         
9712         this.el.addClass(this.invalidClass);
9713         
9714         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9715             
9716             var feedback = this.el.select('.form-control-feedback', true).first();
9717             
9718             if(feedback){
9719                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9720                 
9721                 if(this.getValue().length || this.forceFeedback){
9722                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9723                 }
9724                 
9725             }
9726             
9727         }
9728         
9729         this.fireEvent('invalid', this, msg);
9730     },
9731     // private
9732     SafariOnKeyDown : function(event)
9733     {
9734         // this is a workaround for a password hang bug on chrome/ webkit.
9735         if (this.inputEl().dom.type != 'password') {
9736             return;
9737         }
9738         
9739         var isSelectAll = false;
9740         
9741         if(this.inputEl().dom.selectionEnd > 0){
9742             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9743         }
9744         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9745             event.preventDefault();
9746             this.setValue('');
9747             return;
9748         }
9749         
9750         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9751             
9752             event.preventDefault();
9753             // this is very hacky as keydown always get's upper case.
9754             //
9755             var cc = String.fromCharCode(event.getCharCode());
9756             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9757             
9758         }
9759     },
9760     adjustWidth : function(tag, w){
9761         tag = tag.toLowerCase();
9762         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9763             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9764                 if(tag == 'input'){
9765                     return w + 2;
9766                 }
9767                 if(tag == 'textarea'){
9768                     return w-2;
9769                 }
9770             }else if(Roo.isOpera){
9771                 if(tag == 'input'){
9772                     return w + 2;
9773                 }
9774                 if(tag == 'textarea'){
9775                     return w-2;
9776                 }
9777             }
9778         }
9779         return w;
9780     },
9781     
9782     setFieldLabel : function(v)
9783     {
9784         if(!this.rendered){
9785             return;
9786         }
9787         
9788         if(this.indicator){
9789             var ar = this.el.select('label > span',true);
9790             
9791             if (ar.elements.length) {
9792                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9793                 this.fieldLabel = v;
9794                 return;
9795             }
9796             
9797             var br = this.el.select('label',true);
9798             
9799             if(br.elements.length) {
9800                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9801                 this.fieldLabel = v;
9802                 return;
9803             }
9804             
9805             Roo.log('Cannot Found any of label > span || label in input');
9806             return;
9807         }
9808         
9809         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9810         this.fieldLabel = v;
9811         
9812         
9813     }
9814 });
9815
9816  
9817 /*
9818  * - LGPL
9819  *
9820  * Input
9821  * 
9822  */
9823
9824 /**
9825  * @class Roo.bootstrap.TextArea
9826  * @extends Roo.bootstrap.Input
9827  * Bootstrap TextArea class
9828  * @cfg {Number} cols Specifies the visible width of a text area
9829  * @cfg {Number} rows Specifies the visible number of lines in a text area
9830  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9831  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9832  * @cfg {string} html text
9833  * 
9834  * @constructor
9835  * Create a new TextArea
9836  * @param {Object} config The config object
9837  */
9838
9839 Roo.bootstrap.TextArea = function(config){
9840     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9841    
9842 };
9843
9844 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9845      
9846     cols : false,
9847     rows : 5,
9848     readOnly : false,
9849     warp : 'soft',
9850     resize : false,
9851     value: false,
9852     html: false,
9853     
9854     getAutoCreate : function(){
9855         
9856         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9857         
9858         var id = Roo.id();
9859         
9860         var cfg = {};
9861         
9862         if(this.inputType != 'hidden'){
9863             cfg.cls = 'form-group' //input-group
9864         }
9865         
9866         var input =  {
9867             tag: 'textarea',
9868             id : id,
9869             warp : this.warp,
9870             rows : this.rows,
9871             value : this.value || '',
9872             html: this.html || '',
9873             cls : 'form-control',
9874             placeholder : this.placeholder || '' 
9875             
9876         };
9877         
9878         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9879             input.maxLength = this.maxLength;
9880         }
9881         
9882         if(this.resize){
9883             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9884         }
9885         
9886         if(this.cols){
9887             input.cols = this.cols;
9888         }
9889         
9890         if (this.readOnly) {
9891             input.readonly = true;
9892         }
9893         
9894         if (this.name) {
9895             input.name = this.name;
9896         }
9897         
9898         if (this.size) {
9899             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9900         }
9901         
9902         var settings=this;
9903         ['xs','sm','md','lg'].map(function(size){
9904             if (settings[size]) {
9905                 cfg.cls += ' col-' + size + '-' + settings[size];
9906             }
9907         });
9908         
9909         var inputblock = input;
9910         
9911         if(this.hasFeedback && !this.allowBlank){
9912             
9913             var feedback = {
9914                 tag: 'span',
9915                 cls: 'glyphicon form-control-feedback'
9916             };
9917
9918             inputblock = {
9919                 cls : 'has-feedback',
9920                 cn :  [
9921                     input,
9922                     feedback
9923                 ] 
9924             };  
9925         }
9926         
9927         
9928         if (this.before || this.after) {
9929             
9930             inputblock = {
9931                 cls : 'input-group',
9932                 cn :  [] 
9933             };
9934             if (this.before) {
9935                 inputblock.cn.push({
9936                     tag :'span',
9937                     cls : 'input-group-addon',
9938                     html : this.before
9939                 });
9940             }
9941             
9942             inputblock.cn.push(input);
9943             
9944             if(this.hasFeedback && !this.allowBlank){
9945                 inputblock.cls += ' has-feedback';
9946                 inputblock.cn.push(feedback);
9947             }
9948             
9949             if (this.after) {
9950                 inputblock.cn.push({
9951                     tag :'span',
9952                     cls : 'input-group-addon',
9953                     html : this.after
9954                 });
9955             }
9956             
9957         }
9958         
9959         if (align ==='left' && this.fieldLabel.length) {
9960             cfg.cn = [
9961                 {
9962                     tag: 'label',
9963                     'for' :  id,
9964                     cls : 'control-label',
9965                     html : this.fieldLabel
9966                 },
9967                 {
9968                     cls : "",
9969                     cn: [
9970                         inputblock
9971                     ]
9972                 }
9973
9974             ];
9975             
9976             if(this.labelWidth > 12){
9977                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9978             }
9979
9980             if(this.labelWidth < 13 && this.labelmd == 0){
9981                 this.labelmd = this.labelWidth;
9982             }
9983
9984             if(this.labellg > 0){
9985                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9986                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9987             }
9988
9989             if(this.labelmd > 0){
9990                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9991                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9992             }
9993
9994             if(this.labelsm > 0){
9995                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9996                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9997             }
9998
9999             if(this.labelxs > 0){
10000                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10001                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10002             }
10003             
10004         } else if ( this.fieldLabel.length) {
10005             cfg.cn = [
10006
10007                {
10008                    tag: 'label',
10009                    //cls : 'input-group-addon',
10010                    html : this.fieldLabel
10011
10012                },
10013
10014                inputblock
10015
10016            ];
10017
10018         } else {
10019
10020             cfg.cn = [
10021
10022                 inputblock
10023
10024             ];
10025                 
10026         }
10027         
10028         if (this.disabled) {
10029             input.disabled=true;
10030         }
10031         
10032         return cfg;
10033         
10034     },
10035     /**
10036      * return the real textarea element.
10037      */
10038     inputEl: function ()
10039     {
10040         return this.el.select('textarea.form-control',true).first();
10041     },
10042     
10043     /**
10044      * Clear any invalid styles/messages for this field
10045      */
10046     clearInvalid : function()
10047     {
10048         
10049         if(!this.el || this.preventMark){ // not rendered
10050             return;
10051         }
10052         
10053         var label = this.el.select('label', true).first();
10054         var icon = this.el.select('i.fa-star', true).first();
10055         
10056         if(label && icon){
10057             icon.remove();
10058         }
10059         
10060         this.el.removeClass(this.invalidClass);
10061         
10062         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10063             
10064             var feedback = this.el.select('.form-control-feedback', true).first();
10065             
10066             if(feedback){
10067                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10068             }
10069             
10070         }
10071         
10072         this.fireEvent('valid', this);
10073     },
10074     
10075      /**
10076      * Mark this field as valid
10077      */
10078     markValid : function()
10079     {
10080         if(!this.el  || this.preventMark){ // not rendered
10081             return;
10082         }
10083         
10084         this.el.removeClass([this.invalidClass, this.validClass]);
10085         
10086         var feedback = this.el.select('.form-control-feedback', true).first();
10087             
10088         if(feedback){
10089             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10090         }
10091
10092         if(this.disabled || this.allowBlank){
10093             return;
10094         }
10095         
10096         var label = this.el.select('label', true).first();
10097         var icon = this.el.select('i.fa-star', true).first();
10098         
10099         if(label && icon){
10100             icon.remove();
10101         }
10102         
10103         this.el.addClass(this.validClass);
10104         
10105         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10112             }
10113             
10114         }
10115         
10116         this.fireEvent('valid', this);
10117     },
10118     
10119      /**
10120      * Mark this field as invalid
10121      * @param {String} msg The validation message
10122      */
10123     markInvalid : function(msg)
10124     {
10125         if(!this.el  || this.preventMark){ // not rendered
10126             return;
10127         }
10128         
10129         this.el.removeClass([this.invalidClass, this.validClass]);
10130         
10131         var feedback = this.el.select('.form-control-feedback', true).first();
10132             
10133         if(feedback){
10134             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10135         }
10136
10137         if(this.disabled || this.allowBlank){
10138             return;
10139         }
10140         
10141         var label = this.el.select('label', true).first();
10142         var icon = this.el.select('i.fa-star', true).first();
10143         
10144         if(!this.getValue().length && label && !icon){
10145             this.el.createChild({
10146                 tag : 'i',
10147                 cls : 'text-danger fa fa-lg fa-star',
10148                 tooltip : 'This field is required',
10149                 style : 'margin-right:5px;'
10150             }, label, true);
10151         }
10152
10153         this.el.addClass(this.invalidClass);
10154         
10155         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10156             
10157             var feedback = this.el.select('.form-control-feedback', true).first();
10158             
10159             if(feedback){
10160                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10161                 
10162                 if(this.getValue().length || this.forceFeedback){
10163                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10164                 }
10165                 
10166             }
10167             
10168         }
10169         
10170         this.fireEvent('invalid', this, msg);
10171     }
10172 });
10173
10174  
10175 /*
10176  * - LGPL
10177  *
10178  * trigger field - base class for combo..
10179  * 
10180  */
10181  
10182 /**
10183  * @class Roo.bootstrap.TriggerField
10184  * @extends Roo.bootstrap.Input
10185  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10186  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10187  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10188  * for which you can provide a custom implementation.  For example:
10189  * <pre><code>
10190 var trigger = new Roo.bootstrap.TriggerField();
10191 trigger.onTriggerClick = myTriggerFn;
10192 trigger.applyTo('my-field');
10193 </code></pre>
10194  *
10195  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10196  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10197  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10198  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10199  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10200
10201  * @constructor
10202  * Create a new TriggerField.
10203  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10204  * to the base TextField)
10205  */
10206 Roo.bootstrap.TriggerField = function(config){
10207     this.mimicing = false;
10208     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10209 };
10210
10211 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10212     /**
10213      * @cfg {String} triggerClass A CSS class to apply to the trigger
10214      */
10215      /**
10216      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10217      */
10218     hideTrigger:false,
10219
10220     /**
10221      * @cfg {Boolean} removable (true|false) special filter default false
10222      */
10223     removable : false,
10224     
10225     /** @cfg {Boolean} grow @hide */
10226     /** @cfg {Number} growMin @hide */
10227     /** @cfg {Number} growMax @hide */
10228
10229     /**
10230      * @hide 
10231      * @method
10232      */
10233     autoSize: Roo.emptyFn,
10234     // private
10235     monitorTab : true,
10236     // private
10237     deferHeight : true,
10238
10239     
10240     actionMode : 'wrap',
10241     
10242     caret : false,
10243     
10244     
10245     getAutoCreate : function(){
10246        
10247         var align = this.labelAlign || this.parentLabelAlign();
10248         
10249         var id = Roo.id();
10250         
10251         var cfg = {
10252             cls: 'form-group' //input-group
10253         };
10254         
10255         
10256         var input =  {
10257             tag: 'input',
10258             id : id,
10259             type : this.inputType,
10260             cls : 'form-control',
10261             autocomplete: 'new-password',
10262             placeholder : this.placeholder || '' 
10263             
10264         };
10265         if (this.name) {
10266             input.name = this.name;
10267         }
10268         if (this.size) {
10269             input.cls += ' input-' + this.size;
10270         }
10271         
10272         if (this.disabled) {
10273             input.disabled=true;
10274         }
10275         
10276         var inputblock = input;
10277         
10278         if(this.hasFeedback && !this.allowBlank){
10279             
10280             var feedback = {
10281                 tag: 'span',
10282                 cls: 'glyphicon form-control-feedback'
10283             };
10284             
10285             if(this.removable && !this.editable && !this.tickable){
10286                 inputblock = {
10287                     cls : 'has-feedback',
10288                     cn :  [
10289                         inputblock,
10290                         {
10291                             tag: 'button',
10292                             html : 'x',
10293                             cls : 'roo-combo-removable-btn close'
10294                         },
10295                         feedback
10296                     ] 
10297                 };
10298             } else {
10299                 inputblock = {
10300                     cls : 'has-feedback',
10301                     cn :  [
10302                         inputblock,
10303                         feedback
10304                     ] 
10305                 };
10306             }
10307
10308         } else {
10309             if(this.removable && !this.editable && !this.tickable){
10310                 inputblock = {
10311                     cls : 'roo-removable',
10312                     cn :  [
10313                         inputblock,
10314                         {
10315                             tag: 'button',
10316                             html : 'x',
10317                             cls : 'roo-combo-removable-btn close'
10318                         }
10319                     ] 
10320                 };
10321             }
10322         }
10323         
10324         if (this.before || this.after) {
10325             
10326             inputblock = {
10327                 cls : 'input-group',
10328                 cn :  [] 
10329             };
10330             if (this.before) {
10331                 inputblock.cn.push({
10332                     tag :'span',
10333                     cls : 'input-group-addon',
10334                     html : this.before
10335                 });
10336             }
10337             
10338             inputblock.cn.push(input);
10339             
10340             if(this.hasFeedback && !this.allowBlank){
10341                 inputblock.cls += ' has-feedback';
10342                 inputblock.cn.push(feedback);
10343             }
10344             
10345             if (this.after) {
10346                 inputblock.cn.push({
10347                     tag :'span',
10348                     cls : 'input-group-addon',
10349                     html : this.after
10350                 });
10351             }
10352             
10353         };
10354         
10355         var box = {
10356             tag: 'div',
10357             cn: [
10358                 {
10359                     tag: 'input',
10360                     type : 'hidden',
10361                     cls: 'form-hidden-field'
10362                 },
10363                 inputblock
10364             ]
10365             
10366         };
10367         
10368         if(this.multiple){
10369             box = {
10370                 tag: 'div',
10371                 cn: [
10372                     {
10373                         tag: 'input',
10374                         type : 'hidden',
10375                         cls: 'form-hidden-field'
10376                     },
10377                     {
10378                         tag: 'ul',
10379                         cls: 'roo-select2-choices',
10380                         cn:[
10381                             {
10382                                 tag: 'li',
10383                                 cls: 'roo-select2-search-field',
10384                                 cn: [
10385
10386                                     inputblock
10387                                 ]
10388                             }
10389                         ]
10390                     }
10391                 ]
10392             }
10393         };
10394         
10395         var combobox = {
10396             cls: 'roo-select2-container input-group',
10397             cn: [
10398                 box
10399 //                {
10400 //                    tag: 'ul',
10401 //                    cls: 'typeahead typeahead-long dropdown-menu',
10402 //                    style: 'display:none'
10403 //                }
10404             ]
10405         };
10406         
10407         if(!this.multiple && this.showToggleBtn){
10408             
10409             var caret = {
10410                         tag: 'span',
10411                         cls: 'caret'
10412              };
10413             if (this.caret != false) {
10414                 caret = {
10415                      tag: 'i',
10416                      cls: 'fa fa-' + this.caret
10417                 };
10418                 
10419             }
10420             
10421             combobox.cn.push({
10422                 tag :'span',
10423                 cls : 'input-group-addon btn dropdown-toggle',
10424                 cn : [
10425                     caret,
10426                     {
10427                         tag: 'span',
10428                         cls: 'combobox-clear',
10429                         cn  : [
10430                             {
10431                                 tag : 'i',
10432                                 cls: 'icon-remove'
10433                             }
10434                         ]
10435                     }
10436                 ]
10437
10438             })
10439         }
10440         
10441         if(this.multiple){
10442             combobox.cls += ' roo-select2-container-multi';
10443         }
10444         
10445         if (align ==='left' && this.fieldLabel.length) {
10446             
10447             cfg.cls += ' roo-form-group-label-left';
10448
10449             cfg.cn = [
10450                 {
10451                     tag : 'i',
10452                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10453                     tooltip : 'This field is required'
10454                 },
10455                 {
10456                     tag: 'label',
10457                     'for' :  id,
10458                     cls : 'control-label',
10459                     html : this.fieldLabel
10460
10461                 },
10462                 {
10463                     cls : "", 
10464                     cn: [
10465                         combobox
10466                     ]
10467                 }
10468
10469             ];
10470             
10471             var labelCfg = cfg.cn[1];
10472             var contentCfg = cfg.cn[2];
10473             
10474             if(this.indicatorpos == 'right'){
10475                 cfg.cn = [
10476                     {
10477                         tag: 'label',
10478                         'for' :  id,
10479                         cls : 'control-label',
10480                         cn : [
10481                             {
10482                                 tag : 'span',
10483                                 html : this.fieldLabel
10484                             },
10485                             {
10486                                 tag : 'i',
10487                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10488                                 tooltip : 'This field is required'
10489                             }
10490                         ]
10491                     },
10492                     {
10493                         cls : "", 
10494                         cn: [
10495                             combobox
10496                         ]
10497                     }
10498
10499                 ];
10500                 
10501                 labelCfg = cfg.cn[0];
10502                 contentCfg = cfg.cn[1];
10503             }
10504             
10505             if(this.labelWidth > 12){
10506                 labelCfg.style = "width: " + this.labelWidth + 'px';
10507             }
10508             
10509             if(this.labelWidth < 13 && this.labelmd == 0){
10510                 this.labelmd = this.labelWidth;
10511             }
10512             
10513             if(this.labellg > 0){
10514                 labelCfg.cls += ' col-lg-' + this.labellg;
10515                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10516             }
10517             
10518             if(this.labelmd > 0){
10519                 labelCfg.cls += ' col-md-' + this.labelmd;
10520                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10521             }
10522             
10523             if(this.labelsm > 0){
10524                 labelCfg.cls += ' col-sm-' + this.labelsm;
10525                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10526             }
10527             
10528             if(this.labelxs > 0){
10529                 labelCfg.cls += ' col-xs-' + this.labelxs;
10530                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10531             }
10532             
10533         } else if ( this.fieldLabel.length) {
10534 //                Roo.log(" label");
10535             cfg.cn = [
10536                 {
10537                    tag : 'i',
10538                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10539                    tooltip : 'This field is required'
10540                },
10541                {
10542                    tag: 'label',
10543                    //cls : 'input-group-addon',
10544                    html : this.fieldLabel
10545
10546                },
10547
10548                combobox
10549
10550             ];
10551             
10552             if(this.indicatorpos == 'right'){
10553                 
10554                 cfg.cn = [
10555                     {
10556                        tag: 'label',
10557                        cn : [
10558                            {
10559                                tag : 'span',
10560                                html : this.fieldLabel
10561                            },
10562                            {
10563                               tag : 'i',
10564                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10565                               tooltip : 'This field is required'
10566                            }
10567                        ]
10568
10569                     },
10570                     combobox
10571
10572                 ];
10573
10574             }
10575
10576         } else {
10577             
10578 //                Roo.log(" no label && no align");
10579                 cfg = combobox
10580                      
10581                 
10582         }
10583         
10584         var settings=this;
10585         ['xs','sm','md','lg'].map(function(size){
10586             if (settings[size]) {
10587                 cfg.cls += ' col-' + size + '-' + settings[size];
10588             }
10589         });
10590         
10591         return cfg;
10592         
10593     },
10594     
10595     
10596     
10597     // private
10598     onResize : function(w, h){
10599 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10600 //        if(typeof w == 'number'){
10601 //            var x = w - this.trigger.getWidth();
10602 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10603 //            this.trigger.setStyle('left', x+'px');
10604 //        }
10605     },
10606
10607     // private
10608     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10609
10610     // private
10611     getResizeEl : function(){
10612         return this.inputEl();
10613     },
10614
10615     // private
10616     getPositionEl : function(){
10617         return this.inputEl();
10618     },
10619
10620     // private
10621     alignErrorIcon : function(){
10622         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10623     },
10624
10625     // private
10626     initEvents : function(){
10627         
10628         this.createList();
10629         
10630         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10631         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10632         if(!this.multiple && this.showToggleBtn){
10633             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10634             if(this.hideTrigger){
10635                 this.trigger.setDisplayed(false);
10636             }
10637             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10638         }
10639         
10640         if(this.multiple){
10641             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10642         }
10643         
10644         if(this.removable && !this.editable && !this.tickable){
10645             var close = this.closeTriggerEl();
10646             
10647             if(close){
10648                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10649                 close.on('click', this.removeBtnClick, this, close);
10650             }
10651         }
10652         
10653         //this.trigger.addClassOnOver('x-form-trigger-over');
10654         //this.trigger.addClassOnClick('x-form-trigger-click');
10655         
10656         //if(!this.width){
10657         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10658         //}
10659     },
10660     
10661     closeTriggerEl : function()
10662     {
10663         var close = this.el.select('.roo-combo-removable-btn', true).first();
10664         return close ? close : false;
10665     },
10666     
10667     removeBtnClick : function(e, h, el)
10668     {
10669         e.preventDefault();
10670         
10671         if(this.fireEvent("remove", this) !== false){
10672             this.reset();
10673             this.fireEvent("afterremove", this)
10674         }
10675     },
10676     
10677     createList : function()
10678     {
10679         this.list = Roo.get(document.body).createChild({
10680             tag: 'ul',
10681             cls: 'typeahead typeahead-long dropdown-menu',
10682             style: 'display:none'
10683         });
10684         
10685         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10686         
10687     },
10688
10689     // private
10690     initTrigger : function(){
10691        
10692     },
10693
10694     // private
10695     onDestroy : function(){
10696         if(this.trigger){
10697             this.trigger.removeAllListeners();
10698           //  this.trigger.remove();
10699         }
10700         //if(this.wrap){
10701         //    this.wrap.remove();
10702         //}
10703         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10704     },
10705
10706     // private
10707     onFocus : function(){
10708         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10709         /*
10710         if(!this.mimicing){
10711             this.wrap.addClass('x-trigger-wrap-focus');
10712             this.mimicing = true;
10713             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10714             if(this.monitorTab){
10715                 this.el.on("keydown", this.checkTab, this);
10716             }
10717         }
10718         */
10719     },
10720
10721     // private
10722     checkTab : function(e){
10723         if(e.getKey() == e.TAB){
10724             this.triggerBlur();
10725         }
10726     },
10727
10728     // private
10729     onBlur : function(){
10730         // do nothing
10731     },
10732
10733     // private
10734     mimicBlur : function(e, t){
10735         /*
10736         if(!this.wrap.contains(t) && this.validateBlur()){
10737             this.triggerBlur();
10738         }
10739         */
10740     },
10741
10742     // private
10743     triggerBlur : function(){
10744         this.mimicing = false;
10745         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10746         if(this.monitorTab){
10747             this.el.un("keydown", this.checkTab, this);
10748         }
10749         //this.wrap.removeClass('x-trigger-wrap-focus');
10750         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10751     },
10752
10753     // private
10754     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10755     validateBlur : function(e, t){
10756         return true;
10757     },
10758
10759     // private
10760     onDisable : function(){
10761         this.inputEl().dom.disabled = true;
10762         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10763         //if(this.wrap){
10764         //    this.wrap.addClass('x-item-disabled');
10765         //}
10766     },
10767
10768     // private
10769     onEnable : function(){
10770         this.inputEl().dom.disabled = false;
10771         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10772         //if(this.wrap){
10773         //    this.el.removeClass('x-item-disabled');
10774         //}
10775     },
10776
10777     // private
10778     onShow : function(){
10779         var ae = this.getActionEl();
10780         
10781         if(ae){
10782             ae.dom.style.display = '';
10783             ae.dom.style.visibility = 'visible';
10784         }
10785     },
10786
10787     // private
10788     
10789     onHide : function(){
10790         var ae = this.getActionEl();
10791         ae.dom.style.display = 'none';
10792     },
10793
10794     /**
10795      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10796      * by an implementing function.
10797      * @method
10798      * @param {EventObject} e
10799      */
10800     onTriggerClick : Roo.emptyFn
10801 });
10802  /*
10803  * Based on:
10804  * Ext JS Library 1.1.1
10805  * Copyright(c) 2006-2007, Ext JS, LLC.
10806  *
10807  * Originally Released Under LGPL - original licence link has changed is not relivant.
10808  *
10809  * Fork - LGPL
10810  * <script type="text/javascript">
10811  */
10812
10813
10814 /**
10815  * @class Roo.data.SortTypes
10816  * @singleton
10817  * Defines the default sorting (casting?) comparison functions used when sorting data.
10818  */
10819 Roo.data.SortTypes = {
10820     /**
10821      * Default sort that does nothing
10822      * @param {Mixed} s The value being converted
10823      * @return {Mixed} The comparison value
10824      */
10825     none : function(s){
10826         return s;
10827     },
10828     
10829     /**
10830      * The regular expression used to strip tags
10831      * @type {RegExp}
10832      * @property
10833      */
10834     stripTagsRE : /<\/?[^>]+>/gi,
10835     
10836     /**
10837      * Strips all HTML tags to sort on text only
10838      * @param {Mixed} s The value being converted
10839      * @return {String} The comparison value
10840      */
10841     asText : function(s){
10842         return String(s).replace(this.stripTagsRE, "");
10843     },
10844     
10845     /**
10846      * Strips all HTML tags to sort on text only - Case insensitive
10847      * @param {Mixed} s The value being converted
10848      * @return {String} The comparison value
10849      */
10850     asUCText : function(s){
10851         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10852     },
10853     
10854     /**
10855      * Case insensitive string
10856      * @param {Mixed} s The value being converted
10857      * @return {String} The comparison value
10858      */
10859     asUCString : function(s) {
10860         return String(s).toUpperCase();
10861     },
10862     
10863     /**
10864      * Date sorting
10865      * @param {Mixed} s The value being converted
10866      * @return {Number} The comparison value
10867      */
10868     asDate : function(s) {
10869         if(!s){
10870             return 0;
10871         }
10872         if(s instanceof Date){
10873             return s.getTime();
10874         }
10875         return Date.parse(String(s));
10876     },
10877     
10878     /**
10879      * Float sorting
10880      * @param {Mixed} s The value being converted
10881      * @return {Float} The comparison value
10882      */
10883     asFloat : function(s) {
10884         var val = parseFloat(String(s).replace(/,/g, ""));
10885         if(isNaN(val)) {
10886             val = 0;
10887         }
10888         return val;
10889     },
10890     
10891     /**
10892      * Integer sorting
10893      * @param {Mixed} s The value being converted
10894      * @return {Number} The comparison value
10895      */
10896     asInt : function(s) {
10897         var val = parseInt(String(s).replace(/,/g, ""));
10898         if(isNaN(val)) {
10899             val = 0;
10900         }
10901         return val;
10902     }
10903 };/*
10904  * Based on:
10905  * Ext JS Library 1.1.1
10906  * Copyright(c) 2006-2007, Ext JS, LLC.
10907  *
10908  * Originally Released Under LGPL - original licence link has changed is not relivant.
10909  *
10910  * Fork - LGPL
10911  * <script type="text/javascript">
10912  */
10913
10914 /**
10915 * @class Roo.data.Record
10916  * Instances of this class encapsulate both record <em>definition</em> information, and record
10917  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10918  * to access Records cached in an {@link Roo.data.Store} object.<br>
10919  * <p>
10920  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10921  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10922  * objects.<br>
10923  * <p>
10924  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10925  * @constructor
10926  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10927  * {@link #create}. The parameters are the same.
10928  * @param {Array} data An associative Array of data values keyed by the field name.
10929  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10930  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10931  * not specified an integer id is generated.
10932  */
10933 Roo.data.Record = function(data, id){
10934     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10935     this.data = data;
10936 };
10937
10938 /**
10939  * Generate a constructor for a specific record layout.
10940  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10941  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10942  * Each field definition object may contain the following properties: <ul>
10943  * <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,
10944  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10945  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10946  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10947  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10948  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10949  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10950  * this may be omitted.</p></li>
10951  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10952  * <ul><li>auto (Default, implies no conversion)</li>
10953  * <li>string</li>
10954  * <li>int</li>
10955  * <li>float</li>
10956  * <li>boolean</li>
10957  * <li>date</li></ul></p></li>
10958  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10959  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10960  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10961  * by the Reader into an object that will be stored in the Record. It is passed the
10962  * following parameters:<ul>
10963  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10964  * </ul></p></li>
10965  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10966  * </ul>
10967  * <br>usage:<br><pre><code>
10968 var TopicRecord = Roo.data.Record.create(
10969     {name: 'title', mapping: 'topic_title'},
10970     {name: 'author', mapping: 'username'},
10971     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10972     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10973     {name: 'lastPoster', mapping: 'user2'},
10974     {name: 'excerpt', mapping: 'post_text'}
10975 );
10976
10977 var myNewRecord = new TopicRecord({
10978     title: 'Do my job please',
10979     author: 'noobie',
10980     totalPosts: 1,
10981     lastPost: new Date(),
10982     lastPoster: 'Animal',
10983     excerpt: 'No way dude!'
10984 });
10985 myStore.add(myNewRecord);
10986 </code></pre>
10987  * @method create
10988  * @static
10989  */
10990 Roo.data.Record.create = function(o){
10991     var f = function(){
10992         f.superclass.constructor.apply(this, arguments);
10993     };
10994     Roo.extend(f, Roo.data.Record);
10995     var p = f.prototype;
10996     p.fields = new Roo.util.MixedCollection(false, function(field){
10997         return field.name;
10998     });
10999     for(var i = 0, len = o.length; i < len; i++){
11000         p.fields.add(new Roo.data.Field(o[i]));
11001     }
11002     f.getField = function(name){
11003         return p.fields.get(name);  
11004     };
11005     return f;
11006 };
11007
11008 Roo.data.Record.AUTO_ID = 1000;
11009 Roo.data.Record.EDIT = 'edit';
11010 Roo.data.Record.REJECT = 'reject';
11011 Roo.data.Record.COMMIT = 'commit';
11012
11013 Roo.data.Record.prototype = {
11014     /**
11015      * Readonly flag - true if this record has been modified.
11016      * @type Boolean
11017      */
11018     dirty : false,
11019     editing : false,
11020     error: null,
11021     modified: null,
11022
11023     // private
11024     join : function(store){
11025         this.store = store;
11026     },
11027
11028     /**
11029      * Set the named field to the specified value.
11030      * @param {String} name The name of the field to set.
11031      * @param {Object} value The value to set the field to.
11032      */
11033     set : function(name, value){
11034         if(this.data[name] == value){
11035             return;
11036         }
11037         this.dirty = true;
11038         if(!this.modified){
11039             this.modified = {};
11040         }
11041         if(typeof this.modified[name] == 'undefined'){
11042             this.modified[name] = this.data[name];
11043         }
11044         this.data[name] = value;
11045         if(!this.editing && this.store){
11046             this.store.afterEdit(this);
11047         }       
11048     },
11049
11050     /**
11051      * Get the value of the named field.
11052      * @param {String} name The name of the field to get the value of.
11053      * @return {Object} The value of the field.
11054      */
11055     get : function(name){
11056         return this.data[name]; 
11057     },
11058
11059     // private
11060     beginEdit : function(){
11061         this.editing = true;
11062         this.modified = {}; 
11063     },
11064
11065     // private
11066     cancelEdit : function(){
11067         this.editing = false;
11068         delete this.modified;
11069     },
11070
11071     // private
11072     endEdit : function(){
11073         this.editing = false;
11074         if(this.dirty && this.store){
11075             this.store.afterEdit(this);
11076         }
11077     },
11078
11079     /**
11080      * Usually called by the {@link Roo.data.Store} which owns the Record.
11081      * Rejects all changes made to the Record since either creation, or the last commit operation.
11082      * Modified fields are reverted to their original values.
11083      * <p>
11084      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11085      * of reject operations.
11086      */
11087     reject : function(){
11088         var m = this.modified;
11089         for(var n in m){
11090             if(typeof m[n] != "function"){
11091                 this.data[n] = m[n];
11092             }
11093         }
11094         this.dirty = false;
11095         delete this.modified;
11096         this.editing = false;
11097         if(this.store){
11098             this.store.afterReject(this);
11099         }
11100     },
11101
11102     /**
11103      * Usually called by the {@link Roo.data.Store} which owns the Record.
11104      * Commits all changes made to the Record since either creation, or the last commit operation.
11105      * <p>
11106      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11107      * of commit operations.
11108      */
11109     commit : function(){
11110         this.dirty = false;
11111         delete this.modified;
11112         this.editing = false;
11113         if(this.store){
11114             this.store.afterCommit(this);
11115         }
11116     },
11117
11118     // private
11119     hasError : function(){
11120         return this.error != null;
11121     },
11122
11123     // private
11124     clearError : function(){
11125         this.error = null;
11126     },
11127
11128     /**
11129      * Creates a copy of this record.
11130      * @param {String} id (optional) A new record id if you don't want to use this record's id
11131      * @return {Record}
11132      */
11133     copy : function(newId) {
11134         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11135     }
11136 };/*
11137  * Based on:
11138  * Ext JS Library 1.1.1
11139  * Copyright(c) 2006-2007, Ext JS, LLC.
11140  *
11141  * Originally Released Under LGPL - original licence link has changed is not relivant.
11142  *
11143  * Fork - LGPL
11144  * <script type="text/javascript">
11145  */
11146
11147
11148
11149 /**
11150  * @class Roo.data.Store
11151  * @extends Roo.util.Observable
11152  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11153  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11154  * <p>
11155  * 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
11156  * has no knowledge of the format of the data returned by the Proxy.<br>
11157  * <p>
11158  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11159  * instances from the data object. These records are cached and made available through accessor functions.
11160  * @constructor
11161  * Creates a new Store.
11162  * @param {Object} config A config object containing the objects needed for the Store to access data,
11163  * and read the data into Records.
11164  */
11165 Roo.data.Store = function(config){
11166     this.data = new Roo.util.MixedCollection(false);
11167     this.data.getKey = function(o){
11168         return o.id;
11169     };
11170     this.baseParams = {};
11171     // private
11172     this.paramNames = {
11173         "start" : "start",
11174         "limit" : "limit",
11175         "sort" : "sort",
11176         "dir" : "dir",
11177         "multisort" : "_multisort"
11178     };
11179
11180     if(config && config.data){
11181         this.inlineData = config.data;
11182         delete config.data;
11183     }
11184
11185     Roo.apply(this, config);
11186     
11187     if(this.reader){ // reader passed
11188         this.reader = Roo.factory(this.reader, Roo.data);
11189         this.reader.xmodule = this.xmodule || false;
11190         if(!this.recordType){
11191             this.recordType = this.reader.recordType;
11192         }
11193         if(this.reader.onMetaChange){
11194             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11195         }
11196     }
11197
11198     if(this.recordType){
11199         this.fields = this.recordType.prototype.fields;
11200     }
11201     this.modified = [];
11202
11203     this.addEvents({
11204         /**
11205          * @event datachanged
11206          * Fires when the data cache has changed, and a widget which is using this Store
11207          * as a Record cache should refresh its view.
11208          * @param {Store} this
11209          */
11210         datachanged : true,
11211         /**
11212          * @event metachange
11213          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11214          * @param {Store} this
11215          * @param {Object} meta The JSON metadata
11216          */
11217         metachange : true,
11218         /**
11219          * @event add
11220          * Fires when Records have been added to the Store
11221          * @param {Store} this
11222          * @param {Roo.data.Record[]} records The array of Records added
11223          * @param {Number} index The index at which the record(s) were added
11224          */
11225         add : true,
11226         /**
11227          * @event remove
11228          * Fires when a Record has been removed from the Store
11229          * @param {Store} this
11230          * @param {Roo.data.Record} record The Record that was removed
11231          * @param {Number} index The index at which the record was removed
11232          */
11233         remove : true,
11234         /**
11235          * @event update
11236          * Fires when a Record has been updated
11237          * @param {Store} this
11238          * @param {Roo.data.Record} record The Record that was updated
11239          * @param {String} operation The update operation being performed.  Value may be one of:
11240          * <pre><code>
11241  Roo.data.Record.EDIT
11242  Roo.data.Record.REJECT
11243  Roo.data.Record.COMMIT
11244          * </code></pre>
11245          */
11246         update : true,
11247         /**
11248          * @event clear
11249          * Fires when the data cache has been cleared.
11250          * @param {Store} this
11251          */
11252         clear : true,
11253         /**
11254          * @event beforeload
11255          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11256          * the load action will be canceled.
11257          * @param {Store} this
11258          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11259          */
11260         beforeload : true,
11261         /**
11262          * @event beforeloadadd
11263          * Fires after a new set of Records has been loaded.
11264          * @param {Store} this
11265          * @param {Roo.data.Record[]} records The Records that were loaded
11266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11267          */
11268         beforeloadadd : true,
11269         /**
11270          * @event load
11271          * Fires after a new set of Records has been loaded, before they are added to the store.
11272          * @param {Store} this
11273          * @param {Roo.data.Record[]} records The Records that were loaded
11274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11275          * @params {Object} return from reader
11276          */
11277         load : true,
11278         /**
11279          * @event loadexception
11280          * Fires if an exception occurs in the Proxy during loading.
11281          * Called with the signature of the Proxy's "loadexception" event.
11282          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11283          * 
11284          * @param {Proxy} 
11285          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11286          * @param {Object} load options 
11287          * @param {Object} jsonData from your request (normally this contains the Exception)
11288          */
11289         loadexception : true
11290     });
11291     
11292     if(this.proxy){
11293         this.proxy = Roo.factory(this.proxy, Roo.data);
11294         this.proxy.xmodule = this.xmodule || false;
11295         this.relayEvents(this.proxy,  ["loadexception"]);
11296     }
11297     this.sortToggle = {};
11298     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11299
11300     Roo.data.Store.superclass.constructor.call(this);
11301
11302     if(this.inlineData){
11303         this.loadData(this.inlineData);
11304         delete this.inlineData;
11305     }
11306 };
11307
11308 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11309      /**
11310     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11311     * without a remote query - used by combo/forms at present.
11312     */
11313     
11314     /**
11315     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11316     */
11317     /**
11318     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11319     */
11320     /**
11321     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11322     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11323     */
11324     /**
11325     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11326     * on any HTTP request
11327     */
11328     /**
11329     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11330     */
11331     /**
11332     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11333     */
11334     multiSort: false,
11335     /**
11336     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11337     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11338     */
11339     remoteSort : false,
11340
11341     /**
11342     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11343      * loaded or when a record is removed. (defaults to false).
11344     */
11345     pruneModifiedRecords : false,
11346
11347     // private
11348     lastOptions : null,
11349
11350     /**
11351      * Add Records to the Store and fires the add event.
11352      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11353      */
11354     add : function(records){
11355         records = [].concat(records);
11356         for(var i = 0, len = records.length; i < len; i++){
11357             records[i].join(this);
11358         }
11359         var index = this.data.length;
11360         this.data.addAll(records);
11361         this.fireEvent("add", this, records, index);
11362     },
11363
11364     /**
11365      * Remove a Record from the Store and fires the remove event.
11366      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11367      */
11368     remove : function(record){
11369         var index = this.data.indexOf(record);
11370         this.data.removeAt(index);
11371  
11372         if(this.pruneModifiedRecords){
11373             this.modified.remove(record);
11374         }
11375         this.fireEvent("remove", this, record, index);
11376     },
11377
11378     /**
11379      * Remove all Records from the Store and fires the clear event.
11380      */
11381     removeAll : function(){
11382         this.data.clear();
11383         if(this.pruneModifiedRecords){
11384             this.modified = [];
11385         }
11386         this.fireEvent("clear", this);
11387     },
11388
11389     /**
11390      * Inserts Records to the Store at the given index and fires the add event.
11391      * @param {Number} index The start index at which to insert the passed Records.
11392      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11393      */
11394     insert : function(index, records){
11395         records = [].concat(records);
11396         for(var i = 0, len = records.length; i < len; i++){
11397             this.data.insert(index, records[i]);
11398             records[i].join(this);
11399         }
11400         this.fireEvent("add", this, records, index);
11401     },
11402
11403     /**
11404      * Get the index within the cache of the passed Record.
11405      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11406      * @return {Number} The index of the passed Record. Returns -1 if not found.
11407      */
11408     indexOf : function(record){
11409         return this.data.indexOf(record);
11410     },
11411
11412     /**
11413      * Get the index within the cache of the Record with the passed id.
11414      * @param {String} id The id of the Record to find.
11415      * @return {Number} The index of the Record. Returns -1 if not found.
11416      */
11417     indexOfId : function(id){
11418         return this.data.indexOfKey(id);
11419     },
11420
11421     /**
11422      * Get the Record with the specified id.
11423      * @param {String} id The id of the Record to find.
11424      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11425      */
11426     getById : function(id){
11427         return this.data.key(id);
11428     },
11429
11430     /**
11431      * Get the Record at the specified index.
11432      * @param {Number} index The index of the Record to find.
11433      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11434      */
11435     getAt : function(index){
11436         return this.data.itemAt(index);
11437     },
11438
11439     /**
11440      * Returns a range of Records between specified indices.
11441      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11442      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11443      * @return {Roo.data.Record[]} An array of Records
11444      */
11445     getRange : function(start, end){
11446         return this.data.getRange(start, end);
11447     },
11448
11449     // private
11450     storeOptions : function(o){
11451         o = Roo.apply({}, o);
11452         delete o.callback;
11453         delete o.scope;
11454         this.lastOptions = o;
11455     },
11456
11457     /**
11458      * Loads the Record cache from the configured Proxy using the configured Reader.
11459      * <p>
11460      * If using remote paging, then the first load call must specify the <em>start</em>
11461      * and <em>limit</em> properties in the options.params property to establish the initial
11462      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11463      * <p>
11464      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11465      * and this call will return before the new data has been loaded. Perform any post-processing
11466      * in a callback function, or in a "load" event handler.</strong>
11467      * <p>
11468      * @param {Object} options An object containing properties which control loading options:<ul>
11469      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11470      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11471      * passed the following arguments:<ul>
11472      * <li>r : Roo.data.Record[]</li>
11473      * <li>options: Options object from the load call</li>
11474      * <li>success: Boolean success indicator</li></ul></li>
11475      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11476      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11477      * </ul>
11478      */
11479     load : function(options){
11480         options = options || {};
11481         if(this.fireEvent("beforeload", this, options) !== false){
11482             this.storeOptions(options);
11483             var p = Roo.apply(options.params || {}, this.baseParams);
11484             // if meta was not loaded from remote source.. try requesting it.
11485             if (!this.reader.metaFromRemote) {
11486                 p._requestMeta = 1;
11487             }
11488             if(this.sortInfo && this.remoteSort){
11489                 var pn = this.paramNames;
11490                 p[pn["sort"]] = this.sortInfo.field;
11491                 p[pn["dir"]] = this.sortInfo.direction;
11492             }
11493             if (this.multiSort) {
11494                 var pn = this.paramNames;
11495                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11496             }
11497             
11498             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11499         }
11500     },
11501
11502     /**
11503      * Reloads the Record cache from the configured Proxy using the configured Reader and
11504      * the options from the last load operation performed.
11505      * @param {Object} options (optional) An object containing properties which may override the options
11506      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11507      * the most recently used options are reused).
11508      */
11509     reload : function(options){
11510         this.load(Roo.applyIf(options||{}, this.lastOptions));
11511     },
11512
11513     // private
11514     // Called as a callback by the Reader during a load operation.
11515     loadRecords : function(o, options, success){
11516         if(!o || success === false){
11517             if(success !== false){
11518                 this.fireEvent("load", this, [], options, o);
11519             }
11520             if(options.callback){
11521                 options.callback.call(options.scope || this, [], options, false);
11522             }
11523             return;
11524         }
11525         // if data returned failure - throw an exception.
11526         if (o.success === false) {
11527             // show a message if no listener is registered.
11528             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11529                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11530             }
11531             // loadmask wil be hooked into this..
11532             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11533             return;
11534         }
11535         var r = o.records, t = o.totalRecords || r.length;
11536         
11537         this.fireEvent("beforeloadadd", this, r, options, o);
11538         
11539         if(!options || options.add !== true){
11540             if(this.pruneModifiedRecords){
11541                 this.modified = [];
11542             }
11543             for(var i = 0, len = r.length; i < len; i++){
11544                 r[i].join(this);
11545             }
11546             if(this.snapshot){
11547                 this.data = this.snapshot;
11548                 delete this.snapshot;
11549             }
11550             this.data.clear();
11551             this.data.addAll(r);
11552             this.totalLength = t;
11553             this.applySort();
11554             this.fireEvent("datachanged", this);
11555         }else{
11556             this.totalLength = Math.max(t, this.data.length+r.length);
11557             this.add(r);
11558         }
11559         
11560         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11561                 
11562             var e = new Roo.data.Record({});
11563
11564             e.set(this.parent.displayField, this.parent.emptyTitle);
11565             e.set(this.parent.valueField, '');
11566
11567             this.insert(0, e);
11568         }
11569             
11570         this.fireEvent("load", this, r, options, o);
11571         if(options.callback){
11572             options.callback.call(options.scope || this, r, options, true);
11573         }
11574     },
11575
11576
11577     /**
11578      * Loads data from a passed data block. A Reader which understands the format of the data
11579      * must have been configured in the constructor.
11580      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11581      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11582      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11583      */
11584     loadData : function(o, append){
11585         var r = this.reader.readRecords(o);
11586         this.loadRecords(r, {add: append}, true);
11587     },
11588
11589     /**
11590      * Gets the number of cached records.
11591      * <p>
11592      * <em>If using paging, this may not be the total size of the dataset. If the data object
11593      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11594      * the data set size</em>
11595      */
11596     getCount : function(){
11597         return this.data.length || 0;
11598     },
11599
11600     /**
11601      * Gets the total number of records in the dataset as returned by the server.
11602      * <p>
11603      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11604      * the dataset size</em>
11605      */
11606     getTotalCount : function(){
11607         return this.totalLength || 0;
11608     },
11609
11610     /**
11611      * Returns the sort state of the Store as an object with two properties:
11612      * <pre><code>
11613  field {String} The name of the field by which the Records are sorted
11614  direction {String} The sort order, "ASC" or "DESC"
11615      * </code></pre>
11616      */
11617     getSortState : function(){
11618         return this.sortInfo;
11619     },
11620
11621     // private
11622     applySort : function(){
11623         if(this.sortInfo && !this.remoteSort){
11624             var s = this.sortInfo, f = s.field;
11625             var st = this.fields.get(f).sortType;
11626             var fn = function(r1, r2){
11627                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11628                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11629             };
11630             this.data.sort(s.direction, fn);
11631             if(this.snapshot && this.snapshot != this.data){
11632                 this.snapshot.sort(s.direction, fn);
11633             }
11634         }
11635     },
11636
11637     /**
11638      * Sets the default sort column and order to be used by the next load operation.
11639      * @param {String} fieldName The name of the field to sort by.
11640      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11641      */
11642     setDefaultSort : function(field, dir){
11643         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11644     },
11645
11646     /**
11647      * Sort the Records.
11648      * If remote sorting is used, the sort is performed on the server, and the cache is
11649      * reloaded. If local sorting is used, the cache is sorted internally.
11650      * @param {String} fieldName The name of the field to sort by.
11651      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11652      */
11653     sort : function(fieldName, dir){
11654         var f = this.fields.get(fieldName);
11655         if(!dir){
11656             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11657             
11658             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11659                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11660             }else{
11661                 dir = f.sortDir;
11662             }
11663         }
11664         this.sortToggle[f.name] = dir;
11665         this.sortInfo = {field: f.name, direction: dir};
11666         if(!this.remoteSort){
11667             this.applySort();
11668             this.fireEvent("datachanged", this);
11669         }else{
11670             this.load(this.lastOptions);
11671         }
11672     },
11673
11674     /**
11675      * Calls the specified function for each of the Records in the cache.
11676      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11677      * Returning <em>false</em> aborts and exits the iteration.
11678      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11679      */
11680     each : function(fn, scope){
11681         this.data.each(fn, scope);
11682     },
11683
11684     /**
11685      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11686      * (e.g., during paging).
11687      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11688      */
11689     getModifiedRecords : function(){
11690         return this.modified;
11691     },
11692
11693     // private
11694     createFilterFn : function(property, value, anyMatch){
11695         if(!value.exec){ // not a regex
11696             value = String(value);
11697             if(value.length == 0){
11698                 return false;
11699             }
11700             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11701         }
11702         return function(r){
11703             return value.test(r.data[property]);
11704         };
11705     },
11706
11707     /**
11708      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11709      * @param {String} property A field on your records
11710      * @param {Number} start The record index to start at (defaults to 0)
11711      * @param {Number} end The last record index to include (defaults to length - 1)
11712      * @return {Number} The sum
11713      */
11714     sum : function(property, start, end){
11715         var rs = this.data.items, v = 0;
11716         start = start || 0;
11717         end = (end || end === 0) ? end : rs.length-1;
11718
11719         for(var i = start; i <= end; i++){
11720             v += (rs[i].data[property] || 0);
11721         }
11722         return v;
11723     },
11724
11725     /**
11726      * Filter the records by a specified property.
11727      * @param {String} field A field on your records
11728      * @param {String/RegExp} value Either a string that the field
11729      * should start with or a RegExp to test against the field
11730      * @param {Boolean} anyMatch True to match any part not just the beginning
11731      */
11732     filter : function(property, value, anyMatch){
11733         var fn = this.createFilterFn(property, value, anyMatch);
11734         return fn ? this.filterBy(fn) : this.clearFilter();
11735     },
11736
11737     /**
11738      * Filter by a function. The specified function will be called with each
11739      * record in this data source. If the function returns true the record is included,
11740      * otherwise it is filtered.
11741      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11742      * @param {Object} scope (optional) The scope of the function (defaults to this)
11743      */
11744     filterBy : function(fn, scope){
11745         this.snapshot = this.snapshot || this.data;
11746         this.data = this.queryBy(fn, scope||this);
11747         this.fireEvent("datachanged", this);
11748     },
11749
11750     /**
11751      * Query the records by a specified property.
11752      * @param {String} field A field on your records
11753      * @param {String/RegExp} value Either a string that the field
11754      * should start with or a RegExp to test against the field
11755      * @param {Boolean} anyMatch True to match any part not just the beginning
11756      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11757      */
11758     query : function(property, value, anyMatch){
11759         var fn = this.createFilterFn(property, value, anyMatch);
11760         return fn ? this.queryBy(fn) : this.data.clone();
11761     },
11762
11763     /**
11764      * Query by a function. The specified function will be called with each
11765      * record in this data source. If the function returns true the record is included
11766      * in the results.
11767      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11768      * @param {Object} scope (optional) The scope of the function (defaults to this)
11769       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11770      **/
11771     queryBy : function(fn, scope){
11772         var data = this.snapshot || this.data;
11773         return data.filterBy(fn, scope||this);
11774     },
11775
11776     /**
11777      * Collects unique values for a particular dataIndex from this store.
11778      * @param {String} dataIndex The property to collect
11779      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11780      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11781      * @return {Array} An array of the unique values
11782      **/
11783     collect : function(dataIndex, allowNull, bypassFilter){
11784         var d = (bypassFilter === true && this.snapshot) ?
11785                 this.snapshot.items : this.data.items;
11786         var v, sv, r = [], l = {};
11787         for(var i = 0, len = d.length; i < len; i++){
11788             v = d[i].data[dataIndex];
11789             sv = String(v);
11790             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11791                 l[sv] = true;
11792                 r[r.length] = v;
11793             }
11794         }
11795         return r;
11796     },
11797
11798     /**
11799      * Revert to a view of the Record cache with no filtering applied.
11800      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11801      */
11802     clearFilter : function(suppressEvent){
11803         if(this.snapshot && this.snapshot != this.data){
11804             this.data = this.snapshot;
11805             delete this.snapshot;
11806             if(suppressEvent !== true){
11807                 this.fireEvent("datachanged", this);
11808             }
11809         }
11810     },
11811
11812     // private
11813     afterEdit : function(record){
11814         if(this.modified.indexOf(record) == -1){
11815             this.modified.push(record);
11816         }
11817         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11818     },
11819     
11820     // private
11821     afterReject : function(record){
11822         this.modified.remove(record);
11823         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11824     },
11825
11826     // private
11827     afterCommit : function(record){
11828         this.modified.remove(record);
11829         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11830     },
11831
11832     /**
11833      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11834      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11835      */
11836     commitChanges : function(){
11837         var m = this.modified.slice(0);
11838         this.modified = [];
11839         for(var i = 0, len = m.length; i < len; i++){
11840             m[i].commit();
11841         }
11842     },
11843
11844     /**
11845      * Cancel outstanding changes on all changed records.
11846      */
11847     rejectChanges : function(){
11848         var m = this.modified.slice(0);
11849         this.modified = [];
11850         for(var i = 0, len = m.length; i < len; i++){
11851             m[i].reject();
11852         }
11853     },
11854
11855     onMetaChange : function(meta, rtype, o){
11856         this.recordType = rtype;
11857         this.fields = rtype.prototype.fields;
11858         delete this.snapshot;
11859         this.sortInfo = meta.sortInfo || this.sortInfo;
11860         this.modified = [];
11861         this.fireEvent('metachange', this, this.reader.meta);
11862     },
11863     
11864     moveIndex : function(data, type)
11865     {
11866         var index = this.indexOf(data);
11867         
11868         var newIndex = index + type;
11869         
11870         this.remove(data);
11871         
11872         this.insert(newIndex, data);
11873         
11874     }
11875 });/*
11876  * Based on:
11877  * Ext JS Library 1.1.1
11878  * Copyright(c) 2006-2007, Ext JS, LLC.
11879  *
11880  * Originally Released Under LGPL - original licence link has changed is not relivant.
11881  *
11882  * Fork - LGPL
11883  * <script type="text/javascript">
11884  */
11885
11886 /**
11887  * @class Roo.data.SimpleStore
11888  * @extends Roo.data.Store
11889  * Small helper class to make creating Stores from Array data easier.
11890  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11891  * @cfg {Array} fields An array of field definition objects, or field name strings.
11892  * @cfg {Array} data The multi-dimensional array of data
11893  * @constructor
11894  * @param {Object} config
11895  */
11896 Roo.data.SimpleStore = function(config){
11897     Roo.data.SimpleStore.superclass.constructor.call(this, {
11898         isLocal : true,
11899         reader: new Roo.data.ArrayReader({
11900                 id: config.id
11901             },
11902             Roo.data.Record.create(config.fields)
11903         ),
11904         proxy : new Roo.data.MemoryProxy(config.data)
11905     });
11906     this.load();
11907 };
11908 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11909  * Based on:
11910  * Ext JS Library 1.1.1
11911  * Copyright(c) 2006-2007, Ext JS, LLC.
11912  *
11913  * Originally Released Under LGPL - original licence link has changed is not relivant.
11914  *
11915  * Fork - LGPL
11916  * <script type="text/javascript">
11917  */
11918
11919 /**
11920 /**
11921  * @extends Roo.data.Store
11922  * @class Roo.data.JsonStore
11923  * Small helper class to make creating Stores for JSON data easier. <br/>
11924 <pre><code>
11925 var store = new Roo.data.JsonStore({
11926     url: 'get-images.php',
11927     root: 'images',
11928     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11929 });
11930 </code></pre>
11931  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11932  * JsonReader and HttpProxy (unless inline data is provided).</b>
11933  * @cfg {Array} fields An array of field definition objects, or field name strings.
11934  * @constructor
11935  * @param {Object} config
11936  */
11937 Roo.data.JsonStore = function(c){
11938     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11939         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11940         reader: new Roo.data.JsonReader(c, c.fields)
11941     }));
11942 };
11943 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11944  * Based on:
11945  * Ext JS Library 1.1.1
11946  * Copyright(c) 2006-2007, Ext JS, LLC.
11947  *
11948  * Originally Released Under LGPL - original licence link has changed is not relivant.
11949  *
11950  * Fork - LGPL
11951  * <script type="text/javascript">
11952  */
11953
11954  
11955 Roo.data.Field = function(config){
11956     if(typeof config == "string"){
11957         config = {name: config};
11958     }
11959     Roo.apply(this, config);
11960     
11961     if(!this.type){
11962         this.type = "auto";
11963     }
11964     
11965     var st = Roo.data.SortTypes;
11966     // named sortTypes are supported, here we look them up
11967     if(typeof this.sortType == "string"){
11968         this.sortType = st[this.sortType];
11969     }
11970     
11971     // set default sortType for strings and dates
11972     if(!this.sortType){
11973         switch(this.type){
11974             case "string":
11975                 this.sortType = st.asUCString;
11976                 break;
11977             case "date":
11978                 this.sortType = st.asDate;
11979                 break;
11980             default:
11981                 this.sortType = st.none;
11982         }
11983     }
11984
11985     // define once
11986     var stripRe = /[\$,%]/g;
11987
11988     // prebuilt conversion function for this field, instead of
11989     // switching every time we're reading a value
11990     if(!this.convert){
11991         var cv, dateFormat = this.dateFormat;
11992         switch(this.type){
11993             case "":
11994             case "auto":
11995             case undefined:
11996                 cv = function(v){ return v; };
11997                 break;
11998             case "string":
11999                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12000                 break;
12001             case "int":
12002                 cv = function(v){
12003                     return v !== undefined && v !== null && v !== '' ?
12004                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12005                     };
12006                 break;
12007             case "float":
12008                 cv = function(v){
12009                     return v !== undefined && v !== null && v !== '' ?
12010                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12011                     };
12012                 break;
12013             case "bool":
12014             case "boolean":
12015                 cv = function(v){ return v === true || v === "true" || v == 1; };
12016                 break;
12017             case "date":
12018                 cv = function(v){
12019                     if(!v){
12020                         return '';
12021                     }
12022                     if(v instanceof Date){
12023                         return v;
12024                     }
12025                     if(dateFormat){
12026                         if(dateFormat == "timestamp"){
12027                             return new Date(v*1000);
12028                         }
12029                         return Date.parseDate(v, dateFormat);
12030                     }
12031                     var parsed = Date.parse(v);
12032                     return parsed ? new Date(parsed) : null;
12033                 };
12034              break;
12035             
12036         }
12037         this.convert = cv;
12038     }
12039 };
12040
12041 Roo.data.Field.prototype = {
12042     dateFormat: null,
12043     defaultValue: "",
12044     mapping: null,
12045     sortType : null,
12046     sortDir : "ASC"
12047 };/*
12048  * Based on:
12049  * Ext JS Library 1.1.1
12050  * Copyright(c) 2006-2007, Ext JS, LLC.
12051  *
12052  * Originally Released Under LGPL - original licence link has changed is not relivant.
12053  *
12054  * Fork - LGPL
12055  * <script type="text/javascript">
12056  */
12057  
12058 // Base class for reading structured data from a data source.  This class is intended to be
12059 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12060
12061 /**
12062  * @class Roo.data.DataReader
12063  * Base class for reading structured data from a data source.  This class is intended to be
12064  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12065  */
12066
12067 Roo.data.DataReader = function(meta, recordType){
12068     
12069     this.meta = meta;
12070     
12071     this.recordType = recordType instanceof Array ? 
12072         Roo.data.Record.create(recordType) : recordType;
12073 };
12074
12075 Roo.data.DataReader.prototype = {
12076      /**
12077      * Create an empty record
12078      * @param {Object} data (optional) - overlay some values
12079      * @return {Roo.data.Record} record created.
12080      */
12081     newRow :  function(d) {
12082         var da =  {};
12083         this.recordType.prototype.fields.each(function(c) {
12084             switch( c.type) {
12085                 case 'int' : da[c.name] = 0; break;
12086                 case 'date' : da[c.name] = new Date(); break;
12087                 case 'float' : da[c.name] = 0.0; break;
12088                 case 'boolean' : da[c.name] = false; break;
12089                 default : da[c.name] = ""; break;
12090             }
12091             
12092         });
12093         return new this.recordType(Roo.apply(da, d));
12094     }
12095     
12096 };/*
12097  * Based on:
12098  * Ext JS Library 1.1.1
12099  * Copyright(c) 2006-2007, Ext JS, LLC.
12100  *
12101  * Originally Released Under LGPL - original licence link has changed is not relivant.
12102  *
12103  * Fork - LGPL
12104  * <script type="text/javascript">
12105  */
12106
12107 /**
12108  * @class Roo.data.DataProxy
12109  * @extends Roo.data.Observable
12110  * This class is an abstract base class for implementations which provide retrieval of
12111  * unformatted data objects.<br>
12112  * <p>
12113  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12114  * (of the appropriate type which knows how to parse the data object) to provide a block of
12115  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12116  * <p>
12117  * Custom implementations must implement the load method as described in
12118  * {@link Roo.data.HttpProxy#load}.
12119  */
12120 Roo.data.DataProxy = function(){
12121     this.addEvents({
12122         /**
12123          * @event beforeload
12124          * Fires before a network request is made to retrieve a data object.
12125          * @param {Object} This DataProxy object.
12126          * @param {Object} params The params parameter to the load function.
12127          */
12128         beforeload : true,
12129         /**
12130          * @event load
12131          * Fires before the load method's callback is called.
12132          * @param {Object} This DataProxy object.
12133          * @param {Object} o The data object.
12134          * @param {Object} arg The callback argument object passed to the load function.
12135          */
12136         load : true,
12137         /**
12138          * @event loadexception
12139          * Fires if an Exception occurs during data retrieval.
12140          * @param {Object} This DataProxy object.
12141          * @param {Object} o The data object.
12142          * @param {Object} arg The callback argument object passed to the load function.
12143          * @param {Object} e The Exception.
12144          */
12145         loadexception : true
12146     });
12147     Roo.data.DataProxy.superclass.constructor.call(this);
12148 };
12149
12150 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12151
12152     /**
12153      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12154      */
12155 /*
12156  * Based on:
12157  * Ext JS Library 1.1.1
12158  * Copyright(c) 2006-2007, Ext JS, LLC.
12159  *
12160  * Originally Released Under LGPL - original licence link has changed is not relivant.
12161  *
12162  * Fork - LGPL
12163  * <script type="text/javascript">
12164  */
12165 /**
12166  * @class Roo.data.MemoryProxy
12167  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12168  * to the Reader when its load method is called.
12169  * @constructor
12170  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12171  */
12172 Roo.data.MemoryProxy = function(data){
12173     if (data.data) {
12174         data = data.data;
12175     }
12176     Roo.data.MemoryProxy.superclass.constructor.call(this);
12177     this.data = data;
12178 };
12179
12180 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12181     
12182     /**
12183      * Load data from the requested source (in this case an in-memory
12184      * data object passed to the constructor), read the data object into
12185      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12186      * process that block using the passed callback.
12187      * @param {Object} params This parameter is not used by the MemoryProxy class.
12188      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12189      * object into a block of Roo.data.Records.
12190      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12191      * The function must be passed <ul>
12192      * <li>The Record block object</li>
12193      * <li>The "arg" argument from the load function</li>
12194      * <li>A boolean success indicator</li>
12195      * </ul>
12196      * @param {Object} scope The scope in which to call the callback
12197      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12198      */
12199     load : function(params, reader, callback, scope, arg){
12200         params = params || {};
12201         var result;
12202         try {
12203             result = reader.readRecords(this.data);
12204         }catch(e){
12205             this.fireEvent("loadexception", this, arg, null, e);
12206             callback.call(scope, null, arg, false);
12207             return;
12208         }
12209         callback.call(scope, result, arg, true);
12210     },
12211     
12212     // private
12213     update : function(params, records){
12214         
12215     }
12216 });/*
12217  * Based on:
12218  * Ext JS Library 1.1.1
12219  * Copyright(c) 2006-2007, Ext JS, LLC.
12220  *
12221  * Originally Released Under LGPL - original licence link has changed is not relivant.
12222  *
12223  * Fork - LGPL
12224  * <script type="text/javascript">
12225  */
12226 /**
12227  * @class Roo.data.HttpProxy
12228  * @extends Roo.data.DataProxy
12229  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12230  * configured to reference a certain URL.<br><br>
12231  * <p>
12232  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12233  * from which the running page was served.<br><br>
12234  * <p>
12235  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12236  * <p>
12237  * Be aware that to enable the browser to parse an XML document, the server must set
12238  * the Content-Type header in the HTTP response to "text/xml".
12239  * @constructor
12240  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12241  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12242  * will be used to make the request.
12243  */
12244 Roo.data.HttpProxy = function(conn){
12245     Roo.data.HttpProxy.superclass.constructor.call(this);
12246     // is conn a conn config or a real conn?
12247     this.conn = conn;
12248     this.useAjax = !conn || !conn.events;
12249   
12250 };
12251
12252 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12253     // thse are take from connection...
12254     
12255     /**
12256      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12257      */
12258     /**
12259      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12260      * extra parameters to each request made by this object. (defaults to undefined)
12261      */
12262     /**
12263      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12264      *  to each request made by this object. (defaults to undefined)
12265      */
12266     /**
12267      * @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)
12268      */
12269     /**
12270      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12271      */
12272      /**
12273      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12274      * @type Boolean
12275      */
12276   
12277
12278     /**
12279      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12280      * @type Boolean
12281      */
12282     /**
12283      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12284      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12285      * a finer-grained basis than the DataProxy events.
12286      */
12287     getConnection : function(){
12288         return this.useAjax ? Roo.Ajax : this.conn;
12289     },
12290
12291     /**
12292      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12293      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12294      * process that block using the passed callback.
12295      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12296      * for the request to the remote server.
12297      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12298      * object into a block of Roo.data.Records.
12299      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12300      * The function must be passed <ul>
12301      * <li>The Record block object</li>
12302      * <li>The "arg" argument from the load function</li>
12303      * <li>A boolean success indicator</li>
12304      * </ul>
12305      * @param {Object} scope The scope in which to call the callback
12306      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12307      */
12308     load : function(params, reader, callback, scope, arg){
12309         if(this.fireEvent("beforeload", this, params) !== false){
12310             var  o = {
12311                 params : params || {},
12312                 request: {
12313                     callback : callback,
12314                     scope : scope,
12315                     arg : arg
12316                 },
12317                 reader: reader,
12318                 callback : this.loadResponse,
12319                 scope: this
12320             };
12321             if(this.useAjax){
12322                 Roo.applyIf(o, this.conn);
12323                 if(this.activeRequest){
12324                     Roo.Ajax.abort(this.activeRequest);
12325                 }
12326                 this.activeRequest = Roo.Ajax.request(o);
12327             }else{
12328                 this.conn.request(o);
12329             }
12330         }else{
12331             callback.call(scope||this, null, arg, false);
12332         }
12333     },
12334
12335     // private
12336     loadResponse : function(o, success, response){
12337         delete this.activeRequest;
12338         if(!success){
12339             this.fireEvent("loadexception", this, o, response);
12340             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12341             return;
12342         }
12343         var result;
12344         try {
12345             result = o.reader.read(response);
12346         }catch(e){
12347             this.fireEvent("loadexception", this, o, response, e);
12348             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12349             return;
12350         }
12351         
12352         this.fireEvent("load", this, o, o.request.arg);
12353         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12354     },
12355
12356     // private
12357     update : function(dataSet){
12358
12359     },
12360
12361     // private
12362     updateResponse : function(dataSet){
12363
12364     }
12365 });/*
12366  * Based on:
12367  * Ext JS Library 1.1.1
12368  * Copyright(c) 2006-2007, Ext JS, LLC.
12369  *
12370  * Originally Released Under LGPL - original licence link has changed is not relivant.
12371  *
12372  * Fork - LGPL
12373  * <script type="text/javascript">
12374  */
12375
12376 /**
12377  * @class Roo.data.ScriptTagProxy
12378  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12379  * other than the originating domain of the running page.<br><br>
12380  * <p>
12381  * <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
12382  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12383  * <p>
12384  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12385  * source code that is used as the source inside a &lt;script> tag.<br><br>
12386  * <p>
12387  * In order for the browser to process the returned data, the server must wrap the data object
12388  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12389  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12390  * depending on whether the callback name was passed:
12391  * <p>
12392  * <pre><code>
12393 boolean scriptTag = false;
12394 String cb = request.getParameter("callback");
12395 if (cb != null) {
12396     scriptTag = true;
12397     response.setContentType("text/javascript");
12398 } else {
12399     response.setContentType("application/x-json");
12400 }
12401 Writer out = response.getWriter();
12402 if (scriptTag) {
12403     out.write(cb + "(");
12404 }
12405 out.print(dataBlock.toJsonString());
12406 if (scriptTag) {
12407     out.write(");");
12408 }
12409 </pre></code>
12410  *
12411  * @constructor
12412  * @param {Object} config A configuration object.
12413  */
12414 Roo.data.ScriptTagProxy = function(config){
12415     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12416     Roo.apply(this, config);
12417     this.head = document.getElementsByTagName("head")[0];
12418 };
12419
12420 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12421
12422 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12423     /**
12424      * @cfg {String} url The URL from which to request the data object.
12425      */
12426     /**
12427      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12428      */
12429     timeout : 30000,
12430     /**
12431      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12432      * the server the name of the callback function set up by the load call to process the returned data object.
12433      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12434      * javascript output which calls this named function passing the data object as its only parameter.
12435      */
12436     callbackParam : "callback",
12437     /**
12438      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12439      * name to the request.
12440      */
12441     nocache : true,
12442
12443     /**
12444      * Load data from the configured URL, read the data object into
12445      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12446      * process that block using the passed callback.
12447      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12448      * for the request to the remote server.
12449      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12450      * object into a block of Roo.data.Records.
12451      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12452      * The function must be passed <ul>
12453      * <li>The Record block object</li>
12454      * <li>The "arg" argument from the load function</li>
12455      * <li>A boolean success indicator</li>
12456      * </ul>
12457      * @param {Object} scope The scope in which to call the callback
12458      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12459      */
12460     load : function(params, reader, callback, scope, arg){
12461         if(this.fireEvent("beforeload", this, params) !== false){
12462
12463             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12464
12465             var url = this.url;
12466             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12467             if(this.nocache){
12468                 url += "&_dc=" + (new Date().getTime());
12469             }
12470             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12471             var trans = {
12472                 id : transId,
12473                 cb : "stcCallback"+transId,
12474                 scriptId : "stcScript"+transId,
12475                 params : params,
12476                 arg : arg,
12477                 url : url,
12478                 callback : callback,
12479                 scope : scope,
12480                 reader : reader
12481             };
12482             var conn = this;
12483
12484             window[trans.cb] = function(o){
12485                 conn.handleResponse(o, trans);
12486             };
12487
12488             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12489
12490             if(this.autoAbort !== false){
12491                 this.abort();
12492             }
12493
12494             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12495
12496             var script = document.createElement("script");
12497             script.setAttribute("src", url);
12498             script.setAttribute("type", "text/javascript");
12499             script.setAttribute("id", trans.scriptId);
12500             this.head.appendChild(script);
12501
12502             this.trans = trans;
12503         }else{
12504             callback.call(scope||this, null, arg, false);
12505         }
12506     },
12507
12508     // private
12509     isLoading : function(){
12510         return this.trans ? true : false;
12511     },
12512
12513     /**
12514      * Abort the current server request.
12515      */
12516     abort : function(){
12517         if(this.isLoading()){
12518             this.destroyTrans(this.trans);
12519         }
12520     },
12521
12522     // private
12523     destroyTrans : function(trans, isLoaded){
12524         this.head.removeChild(document.getElementById(trans.scriptId));
12525         clearTimeout(trans.timeoutId);
12526         if(isLoaded){
12527             window[trans.cb] = undefined;
12528             try{
12529                 delete window[trans.cb];
12530             }catch(e){}
12531         }else{
12532             // if hasn't been loaded, wait for load to remove it to prevent script error
12533             window[trans.cb] = function(){
12534                 window[trans.cb] = undefined;
12535                 try{
12536                     delete window[trans.cb];
12537                 }catch(e){}
12538             };
12539         }
12540     },
12541
12542     // private
12543     handleResponse : function(o, trans){
12544         this.trans = false;
12545         this.destroyTrans(trans, true);
12546         var result;
12547         try {
12548             result = trans.reader.readRecords(o);
12549         }catch(e){
12550             this.fireEvent("loadexception", this, o, trans.arg, e);
12551             trans.callback.call(trans.scope||window, null, trans.arg, false);
12552             return;
12553         }
12554         this.fireEvent("load", this, o, trans.arg);
12555         trans.callback.call(trans.scope||window, result, trans.arg, true);
12556     },
12557
12558     // private
12559     handleFailure : function(trans){
12560         this.trans = false;
12561         this.destroyTrans(trans, false);
12562         this.fireEvent("loadexception", this, null, trans.arg);
12563         trans.callback.call(trans.scope||window, null, trans.arg, false);
12564     }
12565 });/*
12566  * Based on:
12567  * Ext JS Library 1.1.1
12568  * Copyright(c) 2006-2007, Ext JS, LLC.
12569  *
12570  * Originally Released Under LGPL - original licence link has changed is not relivant.
12571  *
12572  * Fork - LGPL
12573  * <script type="text/javascript">
12574  */
12575
12576 /**
12577  * @class Roo.data.JsonReader
12578  * @extends Roo.data.DataReader
12579  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12580  * based on mappings in a provided Roo.data.Record constructor.
12581  * 
12582  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12583  * in the reply previously. 
12584  * 
12585  * <p>
12586  * Example code:
12587  * <pre><code>
12588 var RecordDef = Roo.data.Record.create([
12589     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12590     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12591 ]);
12592 var myReader = new Roo.data.JsonReader({
12593     totalProperty: "results",    // The property which contains the total dataset size (optional)
12594     root: "rows",                // The property which contains an Array of row objects
12595     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12596 }, RecordDef);
12597 </code></pre>
12598  * <p>
12599  * This would consume a JSON file like this:
12600  * <pre><code>
12601 { 'results': 2, 'rows': [
12602     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12603     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12604 }
12605 </code></pre>
12606  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12607  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12608  * paged from the remote server.
12609  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12610  * @cfg {String} root name of the property which contains the Array of row objects.
12611  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12612  * @cfg {Array} fields Array of field definition objects
12613  * @constructor
12614  * Create a new JsonReader
12615  * @param {Object} meta Metadata configuration options
12616  * @param {Object} recordType Either an Array of field definition objects,
12617  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12618  */
12619 Roo.data.JsonReader = function(meta, recordType){
12620     
12621     meta = meta || {};
12622     // set some defaults:
12623     Roo.applyIf(meta, {
12624         totalProperty: 'total',
12625         successProperty : 'success',
12626         root : 'data',
12627         id : 'id'
12628     });
12629     
12630     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12631 };
12632 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12633     
12634     /**
12635      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12636      * Used by Store query builder to append _requestMeta to params.
12637      * 
12638      */
12639     metaFromRemote : false,
12640     /**
12641      * This method is only used by a DataProxy which has retrieved data from a remote server.
12642      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12643      * @return {Object} data A data block which is used by an Roo.data.Store object as
12644      * a cache of Roo.data.Records.
12645      */
12646     read : function(response){
12647         var json = response.responseText;
12648        
12649         var o = /* eval:var:o */ eval("("+json+")");
12650         if(!o) {
12651             throw {message: "JsonReader.read: Json object not found"};
12652         }
12653         
12654         if(o.metaData){
12655             
12656             delete this.ef;
12657             this.metaFromRemote = true;
12658             this.meta = o.metaData;
12659             this.recordType = Roo.data.Record.create(o.metaData.fields);
12660             this.onMetaChange(this.meta, this.recordType, o);
12661         }
12662         return this.readRecords(o);
12663     },
12664
12665     // private function a store will implement
12666     onMetaChange : function(meta, recordType, o){
12667
12668     },
12669
12670     /**
12671          * @ignore
12672          */
12673     simpleAccess: function(obj, subsc) {
12674         return obj[subsc];
12675     },
12676
12677         /**
12678          * @ignore
12679          */
12680     getJsonAccessor: function(){
12681         var re = /[\[\.]/;
12682         return function(expr) {
12683             try {
12684                 return(re.test(expr))
12685                     ? new Function("obj", "return obj." + expr)
12686                     : function(obj){
12687                         return obj[expr];
12688                     };
12689             } catch(e){}
12690             return Roo.emptyFn;
12691         };
12692     }(),
12693
12694     /**
12695      * Create a data block containing Roo.data.Records from an XML document.
12696      * @param {Object} o An object which contains an Array of row objects in the property specified
12697      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12698      * which contains the total size of the dataset.
12699      * @return {Object} data A data block which is used by an Roo.data.Store object as
12700      * a cache of Roo.data.Records.
12701      */
12702     readRecords : function(o){
12703         /**
12704          * After any data loads, the raw JSON data is available for further custom processing.
12705          * @type Object
12706          */
12707         this.o = o;
12708         var s = this.meta, Record = this.recordType,
12709             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12710
12711 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12712         if (!this.ef) {
12713             if(s.totalProperty) {
12714                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12715                 }
12716                 if(s.successProperty) {
12717                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12718                 }
12719                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12720                 if (s.id) {
12721                         var g = this.getJsonAccessor(s.id);
12722                         this.getId = function(rec) {
12723                                 var r = g(rec);  
12724                                 return (r === undefined || r === "") ? null : r;
12725                         };
12726                 } else {
12727                         this.getId = function(){return null;};
12728                 }
12729             this.ef = [];
12730             for(var jj = 0; jj < fl; jj++){
12731                 f = fi[jj];
12732                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12733                 this.ef[jj] = this.getJsonAccessor(map);
12734             }
12735         }
12736
12737         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12738         if(s.totalProperty){
12739             var vt = parseInt(this.getTotal(o), 10);
12740             if(!isNaN(vt)){
12741                 totalRecords = vt;
12742             }
12743         }
12744         if(s.successProperty){
12745             var vs = this.getSuccess(o);
12746             if(vs === false || vs === 'false'){
12747                 success = false;
12748             }
12749         }
12750         var records = [];
12751         for(var i = 0; i < c; i++){
12752                 var n = root[i];
12753             var values = {};
12754             var id = this.getId(n);
12755             for(var j = 0; j < fl; j++){
12756                 f = fi[j];
12757             var v = this.ef[j](n);
12758             if (!f.convert) {
12759                 Roo.log('missing convert for ' + f.name);
12760                 Roo.log(f);
12761                 continue;
12762             }
12763             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12764             }
12765             var record = new Record(values, id);
12766             record.json = n;
12767             records[i] = record;
12768         }
12769         return {
12770             raw : o,
12771             success : success,
12772             records : records,
12773             totalRecords : totalRecords
12774         };
12775     }
12776 });/*
12777  * Based on:
12778  * Ext JS Library 1.1.1
12779  * Copyright(c) 2006-2007, Ext JS, LLC.
12780  *
12781  * Originally Released Under LGPL - original licence link has changed is not relivant.
12782  *
12783  * Fork - LGPL
12784  * <script type="text/javascript">
12785  */
12786
12787 /**
12788  * @class Roo.data.ArrayReader
12789  * @extends Roo.data.DataReader
12790  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12791  * Each element of that Array represents a row of data fields. The
12792  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12793  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12794  * <p>
12795  * Example code:.
12796  * <pre><code>
12797 var RecordDef = Roo.data.Record.create([
12798     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12799     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12800 ]);
12801 var myReader = new Roo.data.ArrayReader({
12802     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12803 }, RecordDef);
12804 </code></pre>
12805  * <p>
12806  * This would consume an Array like this:
12807  * <pre><code>
12808 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12809   </code></pre>
12810  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12811  * @constructor
12812  * Create a new JsonReader
12813  * @param {Object} meta Metadata configuration options.
12814  * @param {Object} recordType Either an Array of field definition objects
12815  * as specified to {@link Roo.data.Record#create},
12816  * or an {@link Roo.data.Record} object
12817  * created using {@link Roo.data.Record#create}.
12818  */
12819 Roo.data.ArrayReader = function(meta, recordType){
12820     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12821 };
12822
12823 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12824     /**
12825      * Create a data block containing Roo.data.Records from an XML document.
12826      * @param {Object} o An Array of row objects which represents the dataset.
12827      * @return {Object} data A data block which is used by an Roo.data.Store object as
12828      * a cache of Roo.data.Records.
12829      */
12830     readRecords : function(o){
12831         var sid = this.meta ? this.meta.id : null;
12832         var recordType = this.recordType, fields = recordType.prototype.fields;
12833         var records = [];
12834         var root = o;
12835             for(var i = 0; i < root.length; i++){
12836                     var n = root[i];
12837                 var values = {};
12838                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12839                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12840                 var f = fields.items[j];
12841                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12842                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12843                 v = f.convert(v);
12844                 values[f.name] = v;
12845             }
12846                 var record = new recordType(values, id);
12847                 record.json = n;
12848                 records[records.length] = record;
12849             }
12850             return {
12851                 records : records,
12852                 totalRecords : records.length
12853             };
12854     }
12855 });/*
12856  * - LGPL
12857  * * 
12858  */
12859
12860 /**
12861  * @class Roo.bootstrap.ComboBox
12862  * @extends Roo.bootstrap.TriggerField
12863  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12864  * @cfg {Boolean} append (true|false) default false
12865  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12866  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12867  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12868  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12869  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12870  * @cfg {Boolean} animate default true
12871  * @cfg {Boolean} emptyResultText only for touch device
12872  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12873  * @cfg {String} emptyTitle default ''
12874  * @constructor
12875  * Create a new ComboBox.
12876  * @param {Object} config Configuration options
12877  */
12878 Roo.bootstrap.ComboBox = function(config){
12879     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12880     this.addEvents({
12881         /**
12882          * @event expand
12883          * Fires when the dropdown list is expanded
12884         * @param {Roo.bootstrap.ComboBox} combo This combo box
12885         */
12886         'expand' : true,
12887         /**
12888          * @event collapse
12889          * Fires when the dropdown list is collapsed
12890         * @param {Roo.bootstrap.ComboBox} combo This combo box
12891         */
12892         'collapse' : true,
12893         /**
12894          * @event beforeselect
12895          * Fires before a list item is selected. Return false to cancel the selection.
12896         * @param {Roo.bootstrap.ComboBox} combo This combo box
12897         * @param {Roo.data.Record} record The data record returned from the underlying store
12898         * @param {Number} index The index of the selected item in the dropdown list
12899         */
12900         'beforeselect' : true,
12901         /**
12902          * @event select
12903          * Fires when a list item is selected
12904         * @param {Roo.bootstrap.ComboBox} combo This combo box
12905         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12906         * @param {Number} index The index of the selected item in the dropdown list
12907         */
12908         'select' : true,
12909         /**
12910          * @event beforequery
12911          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12912          * The event object passed has these properties:
12913         * @param {Roo.bootstrap.ComboBox} combo This combo box
12914         * @param {String} query The query
12915         * @param {Boolean} forceAll true to force "all" query
12916         * @param {Boolean} cancel true to cancel the query
12917         * @param {Object} e The query event object
12918         */
12919         'beforequery': true,
12920          /**
12921          * @event add
12922          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12923         * @param {Roo.bootstrap.ComboBox} combo This combo box
12924         */
12925         'add' : true,
12926         /**
12927          * @event edit
12928          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12929         * @param {Roo.bootstrap.ComboBox} combo This combo box
12930         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12931         */
12932         'edit' : true,
12933         /**
12934          * @event remove
12935          * Fires when the remove value from the combobox array
12936         * @param {Roo.bootstrap.ComboBox} combo This combo box
12937         */
12938         'remove' : true,
12939         /**
12940          * @event afterremove
12941          * Fires when the remove value from the combobox array
12942         * @param {Roo.bootstrap.ComboBox} combo This combo box
12943         */
12944         'afterremove' : true,
12945         /**
12946          * @event specialfilter
12947          * Fires when specialfilter
12948             * @param {Roo.bootstrap.ComboBox} combo This combo box
12949             */
12950         'specialfilter' : true,
12951         /**
12952          * @event tick
12953          * Fires when tick the element
12954             * @param {Roo.bootstrap.ComboBox} combo This combo box
12955             */
12956         'tick' : true,
12957         /**
12958          * @event touchviewdisplay
12959          * Fires when touch view require special display (default is using displayField)
12960             * @param {Roo.bootstrap.ComboBox} combo This combo box
12961             * @param {Object} cfg set html .
12962             */
12963         'touchviewdisplay' : true
12964         
12965     });
12966     
12967     this.item = [];
12968     this.tickItems = [];
12969     
12970     this.selectedIndex = -1;
12971     if(this.mode == 'local'){
12972         if(config.queryDelay === undefined){
12973             this.queryDelay = 10;
12974         }
12975         if(config.minChars === undefined){
12976             this.minChars = 0;
12977         }
12978     }
12979 };
12980
12981 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12982      
12983     /**
12984      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12985      * rendering into an Roo.Editor, defaults to false)
12986      */
12987     /**
12988      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12989      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12990      */
12991     /**
12992      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12993      */
12994     /**
12995      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12996      * the dropdown list (defaults to undefined, with no header element)
12997      */
12998
12999      /**
13000      * @cfg {String/Roo.Template} tpl The template to use to render the output
13001      */
13002      
13003      /**
13004      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13005      */
13006     listWidth: undefined,
13007     /**
13008      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13009      * mode = 'remote' or 'text' if mode = 'local')
13010      */
13011     displayField: undefined,
13012     
13013     /**
13014      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13015      * mode = 'remote' or 'value' if mode = 'local'). 
13016      * Note: use of a valueField requires the user make a selection
13017      * in order for a value to be mapped.
13018      */
13019     valueField: undefined,
13020     /**
13021      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13022      */
13023     modalTitle : '',
13024     
13025     /**
13026      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13027      * field's data value (defaults to the underlying DOM element's name)
13028      */
13029     hiddenName: undefined,
13030     /**
13031      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13032      */
13033     listClass: '',
13034     /**
13035      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13036      */
13037     selectedClass: 'active',
13038     
13039     /**
13040      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13041      */
13042     shadow:'sides',
13043     /**
13044      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13045      * anchor positions (defaults to 'tl-bl')
13046      */
13047     listAlign: 'tl-bl?',
13048     /**
13049      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13050      */
13051     maxHeight: 300,
13052     /**
13053      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13054      * query specified by the allQuery config option (defaults to 'query')
13055      */
13056     triggerAction: 'query',
13057     /**
13058      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13059      * (defaults to 4, does not apply if editable = false)
13060      */
13061     minChars : 4,
13062     /**
13063      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13064      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13065      */
13066     typeAhead: false,
13067     /**
13068      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13069      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13070      */
13071     queryDelay: 500,
13072     /**
13073      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13074      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13075      */
13076     pageSize: 0,
13077     /**
13078      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13079      * when editable = true (defaults to false)
13080      */
13081     selectOnFocus:false,
13082     /**
13083      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13084      */
13085     queryParam: 'query',
13086     /**
13087      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13088      * when mode = 'remote' (defaults to 'Loading...')
13089      */
13090     loadingText: 'Loading...',
13091     /**
13092      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13093      */
13094     resizable: false,
13095     /**
13096      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13097      */
13098     handleHeight : 8,
13099     /**
13100      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13101      * traditional select (defaults to true)
13102      */
13103     editable: true,
13104     /**
13105      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13106      */
13107     allQuery: '',
13108     /**
13109      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13110      */
13111     mode: 'remote',
13112     /**
13113      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13114      * listWidth has a higher value)
13115      */
13116     minListWidth : 70,
13117     /**
13118      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13119      * allow the user to set arbitrary text into the field (defaults to false)
13120      */
13121     forceSelection:false,
13122     /**
13123      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13124      * if typeAhead = true (defaults to 250)
13125      */
13126     typeAheadDelay : 250,
13127     /**
13128      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13129      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13130      */
13131     valueNotFoundText : undefined,
13132     /**
13133      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13134      */
13135     blockFocus : false,
13136     
13137     /**
13138      * @cfg {Boolean} disableClear Disable showing of clear button.
13139      */
13140     disableClear : false,
13141     /**
13142      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13143      */
13144     alwaysQuery : false,
13145     
13146     /**
13147      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13148      */
13149     multiple : false,
13150     
13151     /**
13152      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13153      */
13154     invalidClass : "has-warning",
13155     
13156     /**
13157      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13158      */
13159     validClass : "has-success",
13160     
13161     /**
13162      * @cfg {Boolean} specialFilter (true|false) special filter default false
13163      */
13164     specialFilter : false,
13165     
13166     /**
13167      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13168      */
13169     mobileTouchView : true,
13170     
13171     /**
13172      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13173      */
13174     useNativeIOS : false,
13175     
13176     /**
13177      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13178      */
13179     mobile_restrict_height : false,
13180     
13181     ios_options : false,
13182     
13183     //private
13184     addicon : false,
13185     editicon: false,
13186     
13187     page: 0,
13188     hasQuery: false,
13189     append: false,
13190     loadNext: false,
13191     autoFocus : true,
13192     tickable : false,
13193     btnPosition : 'right',
13194     triggerList : true,
13195     showToggleBtn : true,
13196     animate : true,
13197     emptyResultText: 'Empty',
13198     triggerText : 'Select',
13199     emptyTitle : '',
13200     
13201     // element that contains real text value.. (when hidden is used..)
13202     
13203     getAutoCreate : function()
13204     {   
13205         var cfg = false;
13206         //render
13207         /*
13208          * Render classic select for iso
13209          */
13210         
13211         if(Roo.isIOS && this.useNativeIOS){
13212             cfg = this.getAutoCreateNativeIOS();
13213             return cfg;
13214         }
13215         
13216         /*
13217          * Touch Devices
13218          */
13219         
13220         if(Roo.isTouch && this.mobileTouchView){
13221             cfg = this.getAutoCreateTouchView();
13222             return cfg;;
13223         }
13224         
13225         /*
13226          *  Normal ComboBox
13227          */
13228         if(!this.tickable){
13229             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13230             return cfg;
13231         }
13232         
13233         /*
13234          *  ComboBox with tickable selections
13235          */
13236              
13237         var align = this.labelAlign || this.parentLabelAlign();
13238         
13239         cfg = {
13240             cls : 'form-group roo-combobox-tickable' //input-group
13241         };
13242         
13243         var btn_text_select = '';
13244         var btn_text_done = '';
13245         var btn_text_cancel = '';
13246         
13247         if (this.btn_text_show) {
13248             btn_text_select = 'Select';
13249             btn_text_done = 'Done';
13250             btn_text_cancel = 'Cancel'; 
13251         }
13252         
13253         var buttons = {
13254             tag : 'div',
13255             cls : 'tickable-buttons',
13256             cn : [
13257                 {
13258                     tag : 'button',
13259                     type : 'button',
13260                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13261                     //html : this.triggerText
13262                     html: btn_text_select
13263                 },
13264                 {
13265                     tag : 'button',
13266                     type : 'button',
13267                     name : 'ok',
13268                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13269                     //html : 'Done'
13270                     html: btn_text_done
13271                 },
13272                 {
13273                     tag : 'button',
13274                     type : 'button',
13275                     name : 'cancel',
13276                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13277                     //html : 'Cancel'
13278                     html: btn_text_cancel
13279                 }
13280             ]
13281         };
13282         
13283         if(this.editable){
13284             buttons.cn.unshift({
13285                 tag: 'input',
13286                 cls: 'roo-select2-search-field-input'
13287             });
13288         }
13289         
13290         var _this = this;
13291         
13292         Roo.each(buttons.cn, function(c){
13293             if (_this.size) {
13294                 c.cls += ' btn-' + _this.size;
13295             }
13296
13297             if (_this.disabled) {
13298                 c.disabled = true;
13299             }
13300         });
13301         
13302         var box = {
13303             tag: 'div',
13304             cn: [
13305                 {
13306                     tag: 'input',
13307                     type : 'hidden',
13308                     cls: 'form-hidden-field'
13309                 },
13310                 {
13311                     tag: 'ul',
13312                     cls: 'roo-select2-choices',
13313                     cn:[
13314                         {
13315                             tag: 'li',
13316                             cls: 'roo-select2-search-field',
13317                             cn: [
13318                                 buttons
13319                             ]
13320                         }
13321                     ]
13322                 }
13323             ]
13324         };
13325         
13326         var combobox = {
13327             cls: 'roo-select2-container input-group roo-select2-container-multi',
13328             cn: [
13329                 box
13330 //                {
13331 //                    tag: 'ul',
13332 //                    cls: 'typeahead typeahead-long dropdown-menu',
13333 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13334 //                }
13335             ]
13336         };
13337         
13338         if(this.hasFeedback && !this.allowBlank){
13339             
13340             var feedback = {
13341                 tag: 'span',
13342                 cls: 'glyphicon form-control-feedback'
13343             };
13344
13345             combobox.cn.push(feedback);
13346         }
13347         
13348         
13349         if (align ==='left' && this.fieldLabel.length) {
13350             
13351             cfg.cls += ' roo-form-group-label-left';
13352             
13353             cfg.cn = [
13354                 {
13355                     tag : 'i',
13356                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13357                     tooltip : 'This field is required'
13358                 },
13359                 {
13360                     tag: 'label',
13361                     'for' :  id,
13362                     cls : 'control-label',
13363                     html : this.fieldLabel
13364
13365                 },
13366                 {
13367                     cls : "", 
13368                     cn: [
13369                         combobox
13370                     ]
13371                 }
13372
13373             ];
13374             
13375             var labelCfg = cfg.cn[1];
13376             var contentCfg = cfg.cn[2];
13377             
13378
13379             if(this.indicatorpos == 'right'){
13380                 
13381                 cfg.cn = [
13382                     {
13383                         tag: 'label',
13384                         'for' :  id,
13385                         cls : 'control-label',
13386                         cn : [
13387                             {
13388                                 tag : 'span',
13389                                 html : this.fieldLabel
13390                             },
13391                             {
13392                                 tag : 'i',
13393                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13394                                 tooltip : 'This field is required'
13395                             }
13396                         ]
13397                     },
13398                     {
13399                         cls : "",
13400                         cn: [
13401                             combobox
13402                         ]
13403                     }
13404
13405                 ];
13406                 
13407                 
13408                 
13409                 labelCfg = cfg.cn[0];
13410                 contentCfg = cfg.cn[1];
13411             
13412             }
13413             
13414             if(this.labelWidth > 12){
13415                 labelCfg.style = "width: " + this.labelWidth + 'px';
13416             }
13417             
13418             if(this.labelWidth < 13 && this.labelmd == 0){
13419                 this.labelmd = this.labelWidth;
13420             }
13421             
13422             if(this.labellg > 0){
13423                 labelCfg.cls += ' col-lg-' + this.labellg;
13424                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13425             }
13426             
13427             if(this.labelmd > 0){
13428                 labelCfg.cls += ' col-md-' + this.labelmd;
13429                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13430             }
13431             
13432             if(this.labelsm > 0){
13433                 labelCfg.cls += ' col-sm-' + this.labelsm;
13434                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13435             }
13436             
13437             if(this.labelxs > 0){
13438                 labelCfg.cls += ' col-xs-' + this.labelxs;
13439                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13440             }
13441                 
13442                 
13443         } else if ( this.fieldLabel.length) {
13444 //                Roo.log(" label");
13445                  cfg.cn = [
13446                     {
13447                         tag : 'i',
13448                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13449                         tooltip : 'This field is required'
13450                     },
13451                     {
13452                         tag: 'label',
13453                         //cls : 'input-group-addon',
13454                         html : this.fieldLabel
13455                     },
13456                     combobox
13457                 ];
13458                 
13459                 if(this.indicatorpos == 'right'){
13460                     cfg.cn = [
13461                         {
13462                             tag: 'label',
13463                             //cls : 'input-group-addon',
13464                             html : this.fieldLabel
13465                         },
13466                         {
13467                             tag : 'i',
13468                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13469                             tooltip : 'This field is required'
13470                         },
13471                         combobox
13472                     ];
13473                     
13474                 }
13475
13476         } else {
13477             
13478 //                Roo.log(" no label && no align");
13479                 cfg = combobox
13480                      
13481                 
13482         }
13483          
13484         var settings=this;
13485         ['xs','sm','md','lg'].map(function(size){
13486             if (settings[size]) {
13487                 cfg.cls += ' col-' + size + '-' + settings[size];
13488             }
13489         });
13490         
13491         return cfg;
13492         
13493     },
13494     
13495     _initEventsCalled : false,
13496     
13497     // private
13498     initEvents: function()
13499     {   
13500         if (this._initEventsCalled) { // as we call render... prevent looping...
13501             return;
13502         }
13503         this._initEventsCalled = true;
13504         
13505         if (!this.store) {
13506             throw "can not find store for combo";
13507         }
13508         
13509         this.indicator = this.indicatorEl();
13510         
13511         this.store = Roo.factory(this.store, Roo.data);
13512         this.store.parent = this;
13513         
13514         // if we are building from html. then this element is so complex, that we can not really
13515         // use the rendered HTML.
13516         // so we have to trash and replace the previous code.
13517         if (Roo.XComponent.build_from_html) {
13518             // remove this element....
13519             var e = this.el.dom, k=0;
13520             while (e ) { e = e.previousSibling;  ++k;}
13521
13522             this.el.remove();
13523             
13524             this.el=false;
13525             this.rendered = false;
13526             
13527             this.render(this.parent().getChildContainer(true), k);
13528         }
13529         
13530         if(Roo.isIOS && this.useNativeIOS){
13531             this.initIOSView();
13532             return;
13533         }
13534         
13535         /*
13536          * Touch Devices
13537          */
13538         
13539         if(Roo.isTouch && this.mobileTouchView){
13540             this.initTouchView();
13541             return;
13542         }
13543         
13544         if(this.tickable){
13545             this.initTickableEvents();
13546             return;
13547         }
13548         
13549         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13550         
13551         if(this.hiddenName){
13552             
13553             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13554             
13555             this.hiddenField.dom.value =
13556                 this.hiddenValue !== undefined ? this.hiddenValue :
13557                 this.value !== undefined ? this.value : '';
13558
13559             // prevent input submission
13560             this.el.dom.removeAttribute('name');
13561             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13562              
13563              
13564         }
13565         //if(Roo.isGecko){
13566         //    this.el.dom.setAttribute('autocomplete', 'off');
13567         //}
13568         
13569         var cls = 'x-combo-list';
13570         
13571         //this.list = new Roo.Layer({
13572         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13573         //});
13574         
13575         var _this = this;
13576         
13577         (function(){
13578             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13579             _this.list.setWidth(lw);
13580         }).defer(100);
13581         
13582         this.list.on('mouseover', this.onViewOver, this);
13583         this.list.on('mousemove', this.onViewMove, this);
13584         this.list.on('scroll', this.onViewScroll, this);
13585         
13586         /*
13587         this.list.swallowEvent('mousewheel');
13588         this.assetHeight = 0;
13589
13590         if(this.title){
13591             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13592             this.assetHeight += this.header.getHeight();
13593         }
13594
13595         this.innerList = this.list.createChild({cls:cls+'-inner'});
13596         this.innerList.on('mouseover', this.onViewOver, this);
13597         this.innerList.on('mousemove', this.onViewMove, this);
13598         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13599         
13600         if(this.allowBlank && !this.pageSize && !this.disableClear){
13601             this.footer = this.list.createChild({cls:cls+'-ft'});
13602             this.pageTb = new Roo.Toolbar(this.footer);
13603            
13604         }
13605         if(this.pageSize){
13606             this.footer = this.list.createChild({cls:cls+'-ft'});
13607             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13608                     {pageSize: this.pageSize});
13609             
13610         }
13611         
13612         if (this.pageTb && this.allowBlank && !this.disableClear) {
13613             var _this = this;
13614             this.pageTb.add(new Roo.Toolbar.Fill(), {
13615                 cls: 'x-btn-icon x-btn-clear',
13616                 text: '&#160;',
13617                 handler: function()
13618                 {
13619                     _this.collapse();
13620                     _this.clearValue();
13621                     _this.onSelect(false, -1);
13622                 }
13623             });
13624         }
13625         if (this.footer) {
13626             this.assetHeight += this.footer.getHeight();
13627         }
13628         */
13629             
13630         if(!this.tpl){
13631             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13632         }
13633
13634         this.view = new Roo.View(this.list, this.tpl, {
13635             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13636         });
13637         //this.view.wrapEl.setDisplayed(false);
13638         this.view.on('click', this.onViewClick, this);
13639         
13640         
13641         this.store.on('beforeload', this.onBeforeLoad, this);
13642         this.store.on('load', this.onLoad, this);
13643         this.store.on('loadexception', this.onLoadException, this);
13644         /*
13645         if(this.resizable){
13646             this.resizer = new Roo.Resizable(this.list,  {
13647                pinned:true, handles:'se'
13648             });
13649             this.resizer.on('resize', function(r, w, h){
13650                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13651                 this.listWidth = w;
13652                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13653                 this.restrictHeight();
13654             }, this);
13655             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13656         }
13657         */
13658         if(!this.editable){
13659             this.editable = true;
13660             this.setEditable(false);
13661         }
13662         
13663         /*
13664         
13665         if (typeof(this.events.add.listeners) != 'undefined') {
13666             
13667             this.addicon = this.wrap.createChild(
13668                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13669        
13670             this.addicon.on('click', function(e) {
13671                 this.fireEvent('add', this);
13672             }, this);
13673         }
13674         if (typeof(this.events.edit.listeners) != 'undefined') {
13675             
13676             this.editicon = this.wrap.createChild(
13677                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13678             if (this.addicon) {
13679                 this.editicon.setStyle('margin-left', '40px');
13680             }
13681             this.editicon.on('click', function(e) {
13682                 
13683                 // we fire even  if inothing is selected..
13684                 this.fireEvent('edit', this, this.lastData );
13685                 
13686             }, this);
13687         }
13688         */
13689         
13690         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13691             "up" : function(e){
13692                 this.inKeyMode = true;
13693                 this.selectPrev();
13694             },
13695
13696             "down" : function(e){
13697                 if(!this.isExpanded()){
13698                     this.onTriggerClick();
13699                 }else{
13700                     this.inKeyMode = true;
13701                     this.selectNext();
13702                 }
13703             },
13704
13705             "enter" : function(e){
13706 //                this.onViewClick();
13707                 //return true;
13708                 this.collapse();
13709                 
13710                 if(this.fireEvent("specialkey", this, e)){
13711                     this.onViewClick(false);
13712                 }
13713                 
13714                 return true;
13715             },
13716
13717             "esc" : function(e){
13718                 this.collapse();
13719             },
13720
13721             "tab" : function(e){
13722                 this.collapse();
13723                 
13724                 if(this.fireEvent("specialkey", this, e)){
13725                     this.onViewClick(false);
13726                 }
13727                 
13728                 return true;
13729             },
13730
13731             scope : this,
13732
13733             doRelay : function(foo, bar, hname){
13734                 if(hname == 'down' || this.scope.isExpanded()){
13735                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13736                 }
13737                 return true;
13738             },
13739
13740             forceKeyDown: true
13741         });
13742         
13743         
13744         this.queryDelay = Math.max(this.queryDelay || 10,
13745                 this.mode == 'local' ? 10 : 250);
13746         
13747         
13748         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13749         
13750         if(this.typeAhead){
13751             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13752         }
13753         if(this.editable !== false){
13754             this.inputEl().on("keyup", this.onKeyUp, this);
13755         }
13756         if(this.forceSelection){
13757             this.inputEl().on('blur', this.doForce, this);
13758         }
13759         
13760         if(this.multiple){
13761             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13762             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13763         }
13764     },
13765     
13766     initTickableEvents: function()
13767     {   
13768         this.createList();
13769         
13770         if(this.hiddenName){
13771             
13772             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13773             
13774             this.hiddenField.dom.value =
13775                 this.hiddenValue !== undefined ? this.hiddenValue :
13776                 this.value !== undefined ? this.value : '';
13777
13778             // prevent input submission
13779             this.el.dom.removeAttribute('name');
13780             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13781              
13782              
13783         }
13784         
13785 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13786         
13787         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13788         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13789         if(this.triggerList){
13790             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13791         }
13792          
13793         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13794         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13795         
13796         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13797         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13798         
13799         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13800         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13801         
13802         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13803         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13804         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13805         
13806         this.okBtn.hide();
13807         this.cancelBtn.hide();
13808         
13809         var _this = this;
13810         
13811         (function(){
13812             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13813             _this.list.setWidth(lw);
13814         }).defer(100);
13815         
13816         this.list.on('mouseover', this.onViewOver, this);
13817         this.list.on('mousemove', this.onViewMove, this);
13818         
13819         this.list.on('scroll', this.onViewScroll, this);
13820         
13821         if(!this.tpl){
13822             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13823                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13824         }
13825
13826         this.view = new Roo.View(this.list, this.tpl, {
13827             singleSelect:true,
13828             tickable:true,
13829             parent:this,
13830             store: this.store,
13831             selectedClass: this.selectedClass
13832         });
13833         
13834         //this.view.wrapEl.setDisplayed(false);
13835         this.view.on('click', this.onViewClick, this);
13836         
13837         
13838         
13839         this.store.on('beforeload', this.onBeforeLoad, this);
13840         this.store.on('load', this.onLoad, this);
13841         this.store.on('loadexception', this.onLoadException, this);
13842         
13843         if(this.editable){
13844             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13845                 "up" : function(e){
13846                     this.inKeyMode = true;
13847                     this.selectPrev();
13848                 },
13849
13850                 "down" : function(e){
13851                     this.inKeyMode = true;
13852                     this.selectNext();
13853                 },
13854
13855                 "enter" : function(e){
13856                     if(this.fireEvent("specialkey", this, e)){
13857                         this.onViewClick(false);
13858                     }
13859                     
13860                     return true;
13861                 },
13862
13863                 "esc" : function(e){
13864                     this.onTickableFooterButtonClick(e, false, false);
13865                 },
13866
13867                 "tab" : function(e){
13868                     this.fireEvent("specialkey", this, e);
13869                     
13870                     this.onTickableFooterButtonClick(e, false, false);
13871                     
13872                     return true;
13873                 },
13874
13875                 scope : this,
13876
13877                 doRelay : function(e, fn, key){
13878                     if(this.scope.isExpanded()){
13879                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13880                     }
13881                     return true;
13882                 },
13883
13884                 forceKeyDown: true
13885             });
13886         }
13887         
13888         this.queryDelay = Math.max(this.queryDelay || 10,
13889                 this.mode == 'local' ? 10 : 250);
13890         
13891         
13892         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13893         
13894         if(this.typeAhead){
13895             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13896         }
13897         
13898         if(this.editable !== false){
13899             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13900         }
13901         
13902         this.indicator = this.indicatorEl();
13903         
13904         if(this.indicator){
13905             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13906             this.indicator.hide();
13907         }
13908         
13909     },
13910
13911     onDestroy : function(){
13912         if(this.view){
13913             this.view.setStore(null);
13914             this.view.el.removeAllListeners();
13915             this.view.el.remove();
13916             this.view.purgeListeners();
13917         }
13918         if(this.list){
13919             this.list.dom.innerHTML  = '';
13920         }
13921         
13922         if(this.store){
13923             this.store.un('beforeload', this.onBeforeLoad, this);
13924             this.store.un('load', this.onLoad, this);
13925             this.store.un('loadexception', this.onLoadException, this);
13926         }
13927         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13928     },
13929
13930     // private
13931     fireKey : function(e){
13932         if(e.isNavKeyPress() && !this.list.isVisible()){
13933             this.fireEvent("specialkey", this, e);
13934         }
13935     },
13936
13937     // private
13938     onResize: function(w, h){
13939 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13940 //        
13941 //        if(typeof w != 'number'){
13942 //            // we do not handle it!?!?
13943 //            return;
13944 //        }
13945 //        var tw = this.trigger.getWidth();
13946 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13947 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13948 //        var x = w - tw;
13949 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13950 //            
13951 //        //this.trigger.setStyle('left', x+'px');
13952 //        
13953 //        if(this.list && this.listWidth === undefined){
13954 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13955 //            this.list.setWidth(lw);
13956 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13957 //        }
13958         
13959     
13960         
13961     },
13962
13963     /**
13964      * Allow or prevent the user from directly editing the field text.  If false is passed,
13965      * the user will only be able to select from the items defined in the dropdown list.  This method
13966      * is the runtime equivalent of setting the 'editable' config option at config time.
13967      * @param {Boolean} value True to allow the user to directly edit the field text
13968      */
13969     setEditable : function(value){
13970         if(value == this.editable){
13971             return;
13972         }
13973         this.editable = value;
13974         if(!value){
13975             this.inputEl().dom.setAttribute('readOnly', true);
13976             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13977             this.inputEl().addClass('x-combo-noedit');
13978         }else{
13979             this.inputEl().dom.setAttribute('readOnly', false);
13980             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13981             this.inputEl().removeClass('x-combo-noedit');
13982         }
13983     },
13984
13985     // private
13986     
13987     onBeforeLoad : function(combo,opts){
13988         if(!this.hasFocus){
13989             return;
13990         }
13991          if (!opts.add) {
13992             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13993          }
13994         this.restrictHeight();
13995         this.selectedIndex = -1;
13996     },
13997
13998     // private
13999     onLoad : function(){
14000         
14001         this.hasQuery = false;
14002         
14003         if(!this.hasFocus){
14004             return;
14005         }
14006         
14007         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14008             this.loading.hide();
14009         }
14010         
14011         if(this.store.getCount() > 0){
14012             
14013             this.expand();
14014             this.restrictHeight();
14015             if(this.lastQuery == this.allQuery){
14016                 if(this.editable && !this.tickable){
14017                     this.inputEl().dom.select();
14018                 }
14019                 
14020                 if(
14021                     !this.selectByValue(this.value, true) &&
14022                     this.autoFocus && 
14023                     (
14024                         !this.store.lastOptions ||
14025                         typeof(this.store.lastOptions.add) == 'undefined' || 
14026                         this.store.lastOptions.add != true
14027                     )
14028                 ){
14029                     this.select(0, true);
14030                 }
14031             }else{
14032                 if(this.autoFocus){
14033                     this.selectNext();
14034                 }
14035                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14036                     this.taTask.delay(this.typeAheadDelay);
14037                 }
14038             }
14039         }else{
14040             this.onEmptyResults();
14041         }
14042         
14043         //this.el.focus();
14044     },
14045     // private
14046     onLoadException : function()
14047     {
14048         this.hasQuery = false;
14049         
14050         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14051             this.loading.hide();
14052         }
14053         
14054         if(this.tickable && this.editable){
14055             return;
14056         }
14057         
14058         this.collapse();
14059         // only causes errors at present
14060         //Roo.log(this.store.reader.jsonData);
14061         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14062             // fixme
14063             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14064         //}
14065         
14066         
14067     },
14068     // private
14069     onTypeAhead : function(){
14070         if(this.store.getCount() > 0){
14071             var r = this.store.getAt(0);
14072             var newValue = r.data[this.displayField];
14073             var len = newValue.length;
14074             var selStart = this.getRawValue().length;
14075             
14076             if(selStart != len){
14077                 this.setRawValue(newValue);
14078                 this.selectText(selStart, newValue.length);
14079             }
14080         }
14081     },
14082
14083     // private
14084     onSelect : function(record, index){
14085         
14086         if(this.fireEvent('beforeselect', this, record, index) !== false){
14087         
14088             this.setFromData(index > -1 ? record.data : false);
14089             
14090             this.collapse();
14091             this.fireEvent('select', this, record, index);
14092         }
14093     },
14094
14095     /**
14096      * Returns the currently selected field value or empty string if no value is set.
14097      * @return {String} value The selected value
14098      */
14099     getValue : function()
14100     {
14101         if(Roo.isIOS && this.useNativeIOS){
14102             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14103         }
14104         
14105         if(this.multiple){
14106             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14107         }
14108         
14109         if(this.valueField){
14110             return typeof this.value != 'undefined' ? this.value : '';
14111         }else{
14112             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14113         }
14114     },
14115     
14116     getRawValue : function()
14117     {
14118         if(Roo.isIOS && this.useNativeIOS){
14119             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14120         }
14121         
14122         var v = this.inputEl().getValue();
14123         
14124         return v;
14125     },
14126
14127     /**
14128      * Clears any text/value currently set in the field
14129      */
14130     clearValue : function(){
14131         
14132         if(this.hiddenField){
14133             this.hiddenField.dom.value = '';
14134         }
14135         this.value = '';
14136         this.setRawValue('');
14137         this.lastSelectionText = '';
14138         this.lastData = false;
14139         
14140         var close = this.closeTriggerEl();
14141         
14142         if(close){
14143             close.hide();
14144         }
14145         
14146         this.validate();
14147         
14148     },
14149
14150     /**
14151      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14152      * will be displayed in the field.  If the value does not match the data value of an existing item,
14153      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14154      * Otherwise the field will be blank (although the value will still be set).
14155      * @param {String} value The value to match
14156      */
14157     setValue : function(v)
14158     {
14159         if(Roo.isIOS && this.useNativeIOS){
14160             this.setIOSValue(v);
14161             return;
14162         }
14163         
14164         if(this.multiple){
14165             this.syncValue();
14166             return;
14167         }
14168         
14169         var text = v;
14170         if(this.valueField){
14171             var r = this.findRecord(this.valueField, v);
14172             if(r){
14173                 text = r.data[this.displayField];
14174             }else if(this.valueNotFoundText !== undefined){
14175                 text = this.valueNotFoundText;
14176             }
14177         }
14178         this.lastSelectionText = text;
14179         if(this.hiddenField){
14180             this.hiddenField.dom.value = v;
14181         }
14182         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14183         this.value = v;
14184         
14185         var close = this.closeTriggerEl();
14186         
14187         if(close){
14188             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14189         }
14190         
14191         this.validate();
14192     },
14193     /**
14194      * @property {Object} the last set data for the element
14195      */
14196     
14197     lastData : false,
14198     /**
14199      * Sets the value of the field based on a object which is related to the record format for the store.
14200      * @param {Object} value the value to set as. or false on reset?
14201      */
14202     setFromData : function(o){
14203         
14204         if(this.multiple){
14205             this.addItem(o);
14206             return;
14207         }
14208             
14209         var dv = ''; // display value
14210         var vv = ''; // value value..
14211         this.lastData = o;
14212         if (this.displayField) {
14213             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14214         } else {
14215             // this is an error condition!!!
14216             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14217         }
14218         
14219         if(this.valueField){
14220             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14221         }
14222         
14223         var close = this.closeTriggerEl();
14224         
14225         if(close){
14226             if(dv.length || vv * 1 > 0){
14227                 close.show() ;
14228                 this.blockFocus=true;
14229             } else {
14230                 close.hide();
14231             }             
14232         }
14233         
14234         if(this.hiddenField){
14235             this.hiddenField.dom.value = vv;
14236             
14237             this.lastSelectionText = dv;
14238             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14239             this.value = vv;
14240             return;
14241         }
14242         // no hidden field.. - we store the value in 'value', but still display
14243         // display field!!!!
14244         this.lastSelectionText = dv;
14245         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14246         this.value = vv;
14247         
14248         
14249         
14250     },
14251     // private
14252     reset : function(){
14253         // overridden so that last data is reset..
14254         
14255         if(this.multiple){
14256             this.clearItem();
14257             return;
14258         }
14259         
14260         this.setValue(this.originalValue);
14261         //this.clearInvalid();
14262         this.lastData = false;
14263         if (this.view) {
14264             this.view.clearSelections();
14265         }
14266         
14267         this.validate();
14268     },
14269     // private
14270     findRecord : function(prop, value){
14271         var record;
14272         if(this.store.getCount() > 0){
14273             this.store.each(function(r){
14274                 if(r.data[prop] == value){
14275                     record = r;
14276                     return false;
14277                 }
14278                 return true;
14279             });
14280         }
14281         return record;
14282     },
14283     
14284     getName: function()
14285     {
14286         // returns hidden if it's set..
14287         if (!this.rendered) {return ''};
14288         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14289         
14290     },
14291     // private
14292     onViewMove : function(e, t){
14293         this.inKeyMode = false;
14294     },
14295
14296     // private
14297     onViewOver : function(e, t){
14298         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14299             return;
14300         }
14301         var item = this.view.findItemFromChild(t);
14302         
14303         if(item){
14304             var index = this.view.indexOf(item);
14305             this.select(index, false);
14306         }
14307     },
14308
14309     // private
14310     onViewClick : function(view, doFocus, el, e)
14311     {
14312         var index = this.view.getSelectedIndexes()[0];
14313         
14314         var r = this.store.getAt(index);
14315         
14316         if(this.tickable){
14317             
14318             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14319                 return;
14320             }
14321             
14322             var rm = false;
14323             var _this = this;
14324             
14325             Roo.each(this.tickItems, function(v,k){
14326                 
14327                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14328                     Roo.log(v);
14329                     _this.tickItems.splice(k, 1);
14330                     
14331                     if(typeof(e) == 'undefined' && view == false){
14332                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14333                     }
14334                     
14335                     rm = true;
14336                     return;
14337                 }
14338             });
14339             
14340             if(rm){
14341                 return;
14342             }
14343             
14344             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14345                 this.tickItems.push(r.data);
14346             }
14347             
14348             if(typeof(e) == 'undefined' && view == false){
14349                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14350             }
14351                     
14352             return;
14353         }
14354         
14355         if(r){
14356             this.onSelect(r, index);
14357         }
14358         if(doFocus !== false && !this.blockFocus){
14359             this.inputEl().focus();
14360         }
14361     },
14362
14363     // private
14364     restrictHeight : function(){
14365         //this.innerList.dom.style.height = '';
14366         //var inner = this.innerList.dom;
14367         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14368         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14369         //this.list.beginUpdate();
14370         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14371         this.list.alignTo(this.inputEl(), this.listAlign);
14372         this.list.alignTo(this.inputEl(), this.listAlign);
14373         //this.list.endUpdate();
14374     },
14375
14376     // private
14377     onEmptyResults : function(){
14378         
14379         if(this.tickable && this.editable){
14380             this.hasFocus = false;
14381             this.restrictHeight();
14382             return;
14383         }
14384         
14385         this.collapse();
14386     },
14387
14388     /**
14389      * Returns true if the dropdown list is expanded, else false.
14390      */
14391     isExpanded : function(){
14392         return this.list.isVisible();
14393     },
14394
14395     /**
14396      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14397      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14398      * @param {String} value The data value of the item to select
14399      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14400      * selected item if it is not currently in view (defaults to true)
14401      * @return {Boolean} True if the value matched an item in the list, else false
14402      */
14403     selectByValue : function(v, scrollIntoView){
14404         if(v !== undefined && v !== null){
14405             var r = this.findRecord(this.valueField || this.displayField, v);
14406             if(r){
14407                 this.select(this.store.indexOf(r), scrollIntoView);
14408                 return true;
14409             }
14410         }
14411         return false;
14412     },
14413
14414     /**
14415      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14416      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14417      * @param {Number} index The zero-based index of the list item to select
14418      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14419      * selected item if it is not currently in view (defaults to true)
14420      */
14421     select : function(index, scrollIntoView){
14422         this.selectedIndex = index;
14423         this.view.select(index);
14424         if(scrollIntoView !== false){
14425             var el = this.view.getNode(index);
14426             /*
14427              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14428              */
14429             if(el){
14430                 this.list.scrollChildIntoView(el, false);
14431             }
14432         }
14433     },
14434
14435     // private
14436     selectNext : function(){
14437         var ct = this.store.getCount();
14438         if(ct > 0){
14439             if(this.selectedIndex == -1){
14440                 this.select(0);
14441             }else if(this.selectedIndex < ct-1){
14442                 this.select(this.selectedIndex+1);
14443             }
14444         }
14445     },
14446
14447     // private
14448     selectPrev : function(){
14449         var ct = this.store.getCount();
14450         if(ct > 0){
14451             if(this.selectedIndex == -1){
14452                 this.select(0);
14453             }else if(this.selectedIndex != 0){
14454                 this.select(this.selectedIndex-1);
14455             }
14456         }
14457     },
14458
14459     // private
14460     onKeyUp : function(e){
14461         if(this.editable !== false && !e.isSpecialKey()){
14462             this.lastKey = e.getKey();
14463             this.dqTask.delay(this.queryDelay);
14464         }
14465     },
14466
14467     // private
14468     validateBlur : function(){
14469         return !this.list || !this.list.isVisible();   
14470     },
14471
14472     // private
14473     initQuery : function(){
14474         
14475         var v = this.getRawValue();
14476         
14477         if(this.tickable && this.editable){
14478             v = this.tickableInputEl().getValue();
14479         }
14480         
14481         this.doQuery(v);
14482     },
14483
14484     // private
14485     doForce : function(){
14486         if(this.inputEl().dom.value.length > 0){
14487             this.inputEl().dom.value =
14488                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14489              
14490         }
14491     },
14492
14493     /**
14494      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14495      * query allowing the query action to be canceled if needed.
14496      * @param {String} query The SQL query to execute
14497      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14498      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14499      * saved in the current store (defaults to false)
14500      */
14501     doQuery : function(q, forceAll){
14502         
14503         if(q === undefined || q === null){
14504             q = '';
14505         }
14506         var qe = {
14507             query: q,
14508             forceAll: forceAll,
14509             combo: this,
14510             cancel:false
14511         };
14512         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14513             return false;
14514         }
14515         q = qe.query;
14516         
14517         forceAll = qe.forceAll;
14518         if(forceAll === true || (q.length >= this.minChars)){
14519             
14520             this.hasQuery = true;
14521             
14522             if(this.lastQuery != q || this.alwaysQuery){
14523                 this.lastQuery = q;
14524                 if(this.mode == 'local'){
14525                     this.selectedIndex = -1;
14526                     if(forceAll){
14527                         this.store.clearFilter();
14528                     }else{
14529                         
14530                         if(this.specialFilter){
14531                             this.fireEvent('specialfilter', this);
14532                             this.onLoad();
14533                             return;
14534                         }
14535                         
14536                         this.store.filter(this.displayField, q);
14537                     }
14538                     
14539                     this.store.fireEvent("datachanged", this.store);
14540                     
14541                     this.onLoad();
14542                     
14543                     
14544                 }else{
14545                     
14546                     this.store.baseParams[this.queryParam] = q;
14547                     
14548                     var options = {params : this.getParams(q)};
14549                     
14550                     if(this.loadNext){
14551                         options.add = true;
14552                         options.params.start = this.page * this.pageSize;
14553                     }
14554                     
14555                     this.store.load(options);
14556                     
14557                     /*
14558                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14559                      *  we should expand the list on onLoad
14560                      *  so command out it
14561                      */
14562 //                    this.expand();
14563                 }
14564             }else{
14565                 this.selectedIndex = -1;
14566                 this.onLoad();   
14567             }
14568         }
14569         
14570         this.loadNext = false;
14571     },
14572     
14573     // private
14574     getParams : function(q){
14575         var p = {};
14576         //p[this.queryParam] = q;
14577         
14578         if(this.pageSize){
14579             p.start = 0;
14580             p.limit = this.pageSize;
14581         }
14582         return p;
14583     },
14584
14585     /**
14586      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14587      */
14588     collapse : function(){
14589         if(!this.isExpanded()){
14590             return;
14591         }
14592         
14593         this.list.hide();
14594         
14595         this.hasFocus = false;
14596         
14597         if(this.tickable){
14598             this.okBtn.hide();
14599             this.cancelBtn.hide();
14600             this.trigger.show();
14601             
14602             if(this.editable){
14603                 this.tickableInputEl().dom.value = '';
14604                 this.tickableInputEl().blur();
14605             }
14606             
14607         }
14608         
14609         Roo.get(document).un('mousedown', this.collapseIf, this);
14610         Roo.get(document).un('mousewheel', this.collapseIf, this);
14611         if (!this.editable) {
14612             Roo.get(document).un('keydown', this.listKeyPress, this);
14613         }
14614         this.fireEvent('collapse', this);
14615         
14616         this.validate();
14617     },
14618
14619     // private
14620     collapseIf : function(e){
14621         var in_combo  = e.within(this.el);
14622         var in_list =  e.within(this.list);
14623         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14624         
14625         if (in_combo || in_list || is_list) {
14626             //e.stopPropagation();
14627             return;
14628         }
14629         
14630         if(this.tickable){
14631             this.onTickableFooterButtonClick(e, false, false);
14632         }
14633
14634         this.collapse();
14635         
14636     },
14637
14638     /**
14639      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14640      */
14641     expand : function(){
14642        
14643         if(this.isExpanded() || !this.hasFocus){
14644             return;
14645         }
14646         
14647         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14648         this.list.setWidth(lw);
14649         
14650         Roo.log('expand');
14651         
14652         this.list.show();
14653         
14654         this.restrictHeight();
14655         
14656         if(this.tickable){
14657             
14658             this.tickItems = Roo.apply([], this.item);
14659             
14660             this.okBtn.show();
14661             this.cancelBtn.show();
14662             this.trigger.hide();
14663             
14664             if(this.editable){
14665                 this.tickableInputEl().focus();
14666             }
14667             
14668         }
14669         
14670         Roo.get(document).on('mousedown', this.collapseIf, this);
14671         Roo.get(document).on('mousewheel', this.collapseIf, this);
14672         if (!this.editable) {
14673             Roo.get(document).on('keydown', this.listKeyPress, this);
14674         }
14675         
14676         this.fireEvent('expand', this);
14677     },
14678
14679     // private
14680     // Implements the default empty TriggerField.onTriggerClick function
14681     onTriggerClick : function(e)
14682     {
14683         Roo.log('trigger click');
14684         
14685         if(this.disabled || !this.triggerList){
14686             return;
14687         }
14688         
14689         this.page = 0;
14690         this.loadNext = false;
14691         
14692         if(this.isExpanded()){
14693             this.collapse();
14694             if (!this.blockFocus) {
14695                 this.inputEl().focus();
14696             }
14697             
14698         }else {
14699             this.hasFocus = true;
14700             if(this.triggerAction == 'all') {
14701                 this.doQuery(this.allQuery, true);
14702             } else {
14703                 this.doQuery(this.getRawValue());
14704             }
14705             if (!this.blockFocus) {
14706                 this.inputEl().focus();
14707             }
14708         }
14709     },
14710     
14711     onTickableTriggerClick : function(e)
14712     {
14713         if(this.disabled){
14714             return;
14715         }
14716         
14717         this.page = 0;
14718         this.loadNext = false;
14719         this.hasFocus = true;
14720         
14721         if(this.triggerAction == 'all') {
14722             this.doQuery(this.allQuery, true);
14723         } else {
14724             this.doQuery(this.getRawValue());
14725         }
14726     },
14727     
14728     onSearchFieldClick : function(e)
14729     {
14730         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14731             this.onTickableFooterButtonClick(e, false, false);
14732             return;
14733         }
14734         
14735         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14736             return;
14737         }
14738         
14739         this.page = 0;
14740         this.loadNext = false;
14741         this.hasFocus = true;
14742         
14743         if(this.triggerAction == 'all') {
14744             this.doQuery(this.allQuery, true);
14745         } else {
14746             this.doQuery(this.getRawValue());
14747         }
14748     },
14749     
14750     listKeyPress : function(e)
14751     {
14752         //Roo.log('listkeypress');
14753         // scroll to first matching element based on key pres..
14754         if (e.isSpecialKey()) {
14755             return false;
14756         }
14757         var k = String.fromCharCode(e.getKey()).toUpperCase();
14758         //Roo.log(k);
14759         var match  = false;
14760         var csel = this.view.getSelectedNodes();
14761         var cselitem = false;
14762         if (csel.length) {
14763             var ix = this.view.indexOf(csel[0]);
14764             cselitem  = this.store.getAt(ix);
14765             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14766                 cselitem = false;
14767             }
14768             
14769         }
14770         
14771         this.store.each(function(v) { 
14772             if (cselitem) {
14773                 // start at existing selection.
14774                 if (cselitem.id == v.id) {
14775                     cselitem = false;
14776                 }
14777                 return true;
14778             }
14779                 
14780             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14781                 match = this.store.indexOf(v);
14782                 return false;
14783             }
14784             return true;
14785         }, this);
14786         
14787         if (match === false) {
14788             return true; // no more action?
14789         }
14790         // scroll to?
14791         this.view.select(match);
14792         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14793         sn.scrollIntoView(sn.dom.parentNode, false);
14794     },
14795     
14796     onViewScroll : function(e, t){
14797         
14798         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){
14799             return;
14800         }
14801         
14802         this.hasQuery = true;
14803         
14804         this.loading = this.list.select('.loading', true).first();
14805         
14806         if(this.loading === null){
14807             this.list.createChild({
14808                 tag: 'div',
14809                 cls: 'loading roo-select2-more-results roo-select2-active',
14810                 html: 'Loading more results...'
14811             });
14812             
14813             this.loading = this.list.select('.loading', true).first();
14814             
14815             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14816             
14817             this.loading.hide();
14818         }
14819         
14820         this.loading.show();
14821         
14822         var _combo = this;
14823         
14824         this.page++;
14825         this.loadNext = true;
14826         
14827         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14828         
14829         return;
14830     },
14831     
14832     addItem : function(o)
14833     {   
14834         var dv = ''; // display value
14835         
14836         if (this.displayField) {
14837             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14838         } else {
14839             // this is an error condition!!!
14840             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14841         }
14842         
14843         if(!dv.length){
14844             return;
14845         }
14846         
14847         var choice = this.choices.createChild({
14848             tag: 'li',
14849             cls: 'roo-select2-search-choice',
14850             cn: [
14851                 {
14852                     tag: 'div',
14853                     html: dv
14854                 },
14855                 {
14856                     tag: 'a',
14857                     href: '#',
14858                     cls: 'roo-select2-search-choice-close fa fa-times',
14859                     tabindex: '-1'
14860                 }
14861             ]
14862             
14863         }, this.searchField);
14864         
14865         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14866         
14867         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14868         
14869         this.item.push(o);
14870         
14871         this.lastData = o;
14872         
14873         this.syncValue();
14874         
14875         this.inputEl().dom.value = '';
14876         
14877         this.validate();
14878     },
14879     
14880     onRemoveItem : function(e, _self, o)
14881     {
14882         e.preventDefault();
14883         
14884         this.lastItem = Roo.apply([], this.item);
14885         
14886         var index = this.item.indexOf(o.data) * 1;
14887         
14888         if( index < 0){
14889             Roo.log('not this item?!');
14890             return;
14891         }
14892         
14893         this.item.splice(index, 1);
14894         o.item.remove();
14895         
14896         this.syncValue();
14897         
14898         this.fireEvent('remove', this, e);
14899         
14900         this.validate();
14901         
14902     },
14903     
14904     syncValue : function()
14905     {
14906         if(!this.item.length){
14907             this.clearValue();
14908             return;
14909         }
14910             
14911         var value = [];
14912         var _this = this;
14913         Roo.each(this.item, function(i){
14914             if(_this.valueField){
14915                 value.push(i[_this.valueField]);
14916                 return;
14917             }
14918
14919             value.push(i);
14920         });
14921
14922         this.value = value.join(',');
14923
14924         if(this.hiddenField){
14925             this.hiddenField.dom.value = this.value;
14926         }
14927         
14928         this.store.fireEvent("datachanged", this.store);
14929         
14930         this.validate();
14931     },
14932     
14933     clearItem : function()
14934     {
14935         if(!this.multiple){
14936             return;
14937         }
14938         
14939         this.item = [];
14940         
14941         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14942            c.remove();
14943         });
14944         
14945         this.syncValue();
14946         
14947         this.validate();
14948         
14949         if(this.tickable && !Roo.isTouch){
14950             this.view.refresh();
14951         }
14952     },
14953     
14954     inputEl: function ()
14955     {
14956         if(Roo.isIOS && this.useNativeIOS){
14957             return this.el.select('select.roo-ios-select', true).first();
14958         }
14959         
14960         if(Roo.isTouch && this.mobileTouchView){
14961             return this.el.select('input.form-control',true).first();
14962         }
14963         
14964         if(this.tickable){
14965             return this.searchField;
14966         }
14967         
14968         return this.el.select('input.form-control',true).first();
14969     },
14970     
14971     onTickableFooterButtonClick : function(e, btn, el)
14972     {
14973         e.preventDefault();
14974         
14975         this.lastItem = Roo.apply([], this.item);
14976         
14977         if(btn && btn.name == 'cancel'){
14978             this.tickItems = Roo.apply([], this.item);
14979             this.collapse();
14980             return;
14981         }
14982         
14983         this.clearItem();
14984         
14985         var _this = this;
14986         
14987         Roo.each(this.tickItems, function(o){
14988             _this.addItem(o);
14989         });
14990         
14991         this.collapse();
14992         
14993     },
14994     
14995     validate : function()
14996     {
14997         if(this.getVisibilityEl().hasClass('hidden')){
14998             return true;
14999         }
15000         
15001         var v = this.getRawValue();
15002         
15003         if(this.multiple){
15004             v = this.getValue();
15005         }
15006         
15007         if(this.disabled || this.allowBlank || v.length){
15008             this.markValid();
15009             return true;
15010         }
15011         
15012         this.markInvalid();
15013         return false;
15014     },
15015     
15016     tickableInputEl : function()
15017     {
15018         if(!this.tickable || !this.editable){
15019             return this.inputEl();
15020         }
15021         
15022         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15023     },
15024     
15025     
15026     getAutoCreateTouchView : function()
15027     {
15028         var id = Roo.id();
15029         
15030         var cfg = {
15031             cls: 'form-group' //input-group
15032         };
15033         
15034         var input =  {
15035             tag: 'input',
15036             id : id,
15037             type : this.inputType,
15038             cls : 'form-control x-combo-noedit',
15039             autocomplete: 'new-password',
15040             placeholder : this.placeholder || '',
15041             readonly : true
15042         };
15043         
15044         if (this.name) {
15045             input.name = this.name;
15046         }
15047         
15048         if (this.size) {
15049             input.cls += ' input-' + this.size;
15050         }
15051         
15052         if (this.disabled) {
15053             input.disabled = true;
15054         }
15055         
15056         var inputblock = {
15057             cls : '',
15058             cn : [
15059                 input
15060             ]
15061         };
15062         
15063         if(this.before){
15064             inputblock.cls += ' input-group';
15065             
15066             inputblock.cn.unshift({
15067                 tag :'span',
15068                 cls : 'input-group-addon',
15069                 html : this.before
15070             });
15071         }
15072         
15073         if(this.removable && !this.multiple){
15074             inputblock.cls += ' roo-removable';
15075             
15076             inputblock.cn.push({
15077                 tag: 'button',
15078                 html : 'x',
15079                 cls : 'roo-combo-removable-btn close'
15080             });
15081         }
15082
15083         if(this.hasFeedback && !this.allowBlank){
15084             
15085             inputblock.cls += ' has-feedback';
15086             
15087             inputblock.cn.push({
15088                 tag: 'span',
15089                 cls: 'glyphicon form-control-feedback'
15090             });
15091             
15092         }
15093         
15094         if (this.after) {
15095             
15096             inputblock.cls += (this.before) ? '' : ' input-group';
15097             
15098             inputblock.cn.push({
15099                 tag :'span',
15100                 cls : 'input-group-addon',
15101                 html : this.after
15102             });
15103         }
15104
15105         var box = {
15106             tag: 'div',
15107             cn: [
15108                 {
15109                     tag: 'input',
15110                     type : 'hidden',
15111                     cls: 'form-hidden-field'
15112                 },
15113                 inputblock
15114             ]
15115             
15116         };
15117         
15118         if(this.multiple){
15119             box = {
15120                 tag: 'div',
15121                 cn: [
15122                     {
15123                         tag: 'input',
15124                         type : 'hidden',
15125                         cls: 'form-hidden-field'
15126                     },
15127                     {
15128                         tag: 'ul',
15129                         cls: 'roo-select2-choices',
15130                         cn:[
15131                             {
15132                                 tag: 'li',
15133                                 cls: 'roo-select2-search-field',
15134                                 cn: [
15135
15136                                     inputblock
15137                                 ]
15138                             }
15139                         ]
15140                     }
15141                 ]
15142             }
15143         };
15144         
15145         var combobox = {
15146             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15147             cn: [
15148                 box
15149             ]
15150         };
15151         
15152         if(!this.multiple && this.showToggleBtn){
15153             
15154             var caret = {
15155                         tag: 'span',
15156                         cls: 'caret'
15157             };
15158             
15159             if (this.caret != false) {
15160                 caret = {
15161                      tag: 'i',
15162                      cls: 'fa fa-' + this.caret
15163                 };
15164                 
15165             }
15166             
15167             combobox.cn.push({
15168                 tag :'span',
15169                 cls : 'input-group-addon btn dropdown-toggle',
15170                 cn : [
15171                     caret,
15172                     {
15173                         tag: 'span',
15174                         cls: 'combobox-clear',
15175                         cn  : [
15176                             {
15177                                 tag : 'i',
15178                                 cls: 'icon-remove'
15179                             }
15180                         ]
15181                     }
15182                 ]
15183
15184             })
15185         }
15186         
15187         if(this.multiple){
15188             combobox.cls += ' roo-select2-container-multi';
15189         }
15190         
15191         var align = this.labelAlign || this.parentLabelAlign();
15192         
15193         if (align ==='left' && this.fieldLabel.length) {
15194
15195             cfg.cn = [
15196                 {
15197                    tag : 'i',
15198                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15199                    tooltip : 'This field is required'
15200                 },
15201                 {
15202                     tag: 'label',
15203                     cls : 'control-label',
15204                     html : this.fieldLabel
15205
15206                 },
15207                 {
15208                     cls : '', 
15209                     cn: [
15210                         combobox
15211                     ]
15212                 }
15213             ];
15214             
15215             var labelCfg = cfg.cn[1];
15216             var contentCfg = cfg.cn[2];
15217             
15218
15219             if(this.indicatorpos == 'right'){
15220                 cfg.cn = [
15221                     {
15222                         tag: 'label',
15223                         'for' :  id,
15224                         cls : 'control-label',
15225                         cn : [
15226                             {
15227                                 tag : 'span',
15228                                 html : this.fieldLabel
15229                             },
15230                             {
15231                                 tag : 'i',
15232                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15233                                 tooltip : 'This field is required'
15234                             }
15235                         ]
15236                     },
15237                     {
15238                         cls : "",
15239                         cn: [
15240                             combobox
15241                         ]
15242                     }
15243
15244                 ];
15245                 
15246                 labelCfg = cfg.cn[0];
15247                 contentCfg = cfg.cn[1];
15248             }
15249             
15250            
15251             
15252             if(this.labelWidth > 12){
15253                 labelCfg.style = "width: " + this.labelWidth + 'px';
15254             }
15255             
15256             if(this.labelWidth < 13 && this.labelmd == 0){
15257                 this.labelmd = this.labelWidth;
15258             }
15259             
15260             if(this.labellg > 0){
15261                 labelCfg.cls += ' col-lg-' + this.labellg;
15262                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15263             }
15264             
15265             if(this.labelmd > 0){
15266                 labelCfg.cls += ' col-md-' + this.labelmd;
15267                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15268             }
15269             
15270             if(this.labelsm > 0){
15271                 labelCfg.cls += ' col-sm-' + this.labelsm;
15272                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15273             }
15274             
15275             if(this.labelxs > 0){
15276                 labelCfg.cls += ' col-xs-' + this.labelxs;
15277                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15278             }
15279                 
15280                 
15281         } else if ( this.fieldLabel.length) {
15282             cfg.cn = [
15283                 {
15284                    tag : 'i',
15285                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15286                    tooltip : 'This field is required'
15287                 },
15288                 {
15289                     tag: 'label',
15290                     cls : 'control-label',
15291                     html : this.fieldLabel
15292
15293                 },
15294                 {
15295                     cls : '', 
15296                     cn: [
15297                         combobox
15298                     ]
15299                 }
15300             ];
15301             
15302             if(this.indicatorpos == 'right'){
15303                 cfg.cn = [
15304                     {
15305                         tag: 'label',
15306                         cls : 'control-label',
15307                         html : this.fieldLabel,
15308                         cn : [
15309                             {
15310                                tag : 'i',
15311                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15312                                tooltip : 'This field is required'
15313                             }
15314                         ]
15315                     },
15316                     {
15317                         cls : '', 
15318                         cn: [
15319                             combobox
15320                         ]
15321                     }
15322                 ];
15323             }
15324         } else {
15325             cfg.cn = combobox;    
15326         }
15327         
15328         
15329         var settings = this;
15330         
15331         ['xs','sm','md','lg'].map(function(size){
15332             if (settings[size]) {
15333                 cfg.cls += ' col-' + size + '-' + settings[size];
15334             }
15335         });
15336         
15337         return cfg;
15338     },
15339     
15340     initTouchView : function()
15341     {
15342         this.renderTouchView();
15343         
15344         this.touchViewEl.on('scroll', function(){
15345             this.el.dom.scrollTop = 0;
15346         }, this);
15347         
15348         this.originalValue = this.getValue();
15349         
15350         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15351         
15352         this.inputEl().on("click", this.showTouchView, this);
15353         if (this.triggerEl) {
15354             this.triggerEl.on("click", this.showTouchView, this);
15355         }
15356         
15357         
15358         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15359         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15360         
15361         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15362         
15363         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15364         this.store.on('load', this.onTouchViewLoad, this);
15365         this.store.on('loadexception', this.onTouchViewLoadException, this);
15366         
15367         if(this.hiddenName){
15368             
15369             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15370             
15371             this.hiddenField.dom.value =
15372                 this.hiddenValue !== undefined ? this.hiddenValue :
15373                 this.value !== undefined ? this.value : '';
15374         
15375             this.el.dom.removeAttribute('name');
15376             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15377         }
15378         
15379         if(this.multiple){
15380             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15381             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15382         }
15383         
15384         if(this.removable && !this.multiple){
15385             var close = this.closeTriggerEl();
15386             if(close){
15387                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15388                 close.on('click', this.removeBtnClick, this, close);
15389             }
15390         }
15391         /*
15392          * fix the bug in Safari iOS8
15393          */
15394         this.inputEl().on("focus", function(e){
15395             document.activeElement.blur();
15396         }, this);
15397         
15398         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15399         
15400         return;
15401         
15402         
15403     },
15404     
15405     renderTouchView : function()
15406     {
15407         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15408         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15409         
15410         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15411         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15412         
15413         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15414         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15415         this.touchViewBodyEl.setStyle('overflow', 'auto');
15416         
15417         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15418         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15419         
15420         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15421         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15422         
15423     },
15424     
15425     showTouchView : function()
15426     {
15427         if(this.disabled){
15428             return;
15429         }
15430         
15431         this.touchViewHeaderEl.hide();
15432
15433         if(this.modalTitle.length){
15434             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15435             this.touchViewHeaderEl.show();
15436         }
15437
15438         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15439         this.touchViewEl.show();
15440
15441         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15442         
15443         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15444         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15445
15446         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15447
15448         if(this.modalTitle.length){
15449             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15450         }
15451         
15452         this.touchViewBodyEl.setHeight(bodyHeight);
15453
15454         if(this.animate){
15455             var _this = this;
15456             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15457         }else{
15458             this.touchViewEl.addClass('in');
15459         }
15460         
15461         if(this._touchViewMask){
15462             Roo.get(document.body).addClass("x-body-masked");
15463             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15464             this._touchViewMask.setStyle('z-index', 10000);
15465             this._touchViewMask.addClass('show');
15466         }
15467         
15468         this.doTouchViewQuery();
15469         
15470     },
15471     
15472     hideTouchView : function()
15473     {
15474         this.touchViewEl.removeClass('in');
15475
15476         if(this.animate){
15477             var _this = this;
15478             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15479         }else{
15480             this.touchViewEl.setStyle('display', 'none');
15481         }
15482         
15483         if(this._touchViewMask){
15484             this._touchViewMask.removeClass('show');
15485             Roo.get(document.body).removeClass("x-body-masked");
15486         }
15487     },
15488     
15489     setTouchViewValue : function()
15490     {
15491         if(this.multiple){
15492             this.clearItem();
15493         
15494             var _this = this;
15495
15496             Roo.each(this.tickItems, function(o){
15497                 this.addItem(o);
15498             }, this);
15499         }
15500         
15501         this.hideTouchView();
15502     },
15503     
15504     doTouchViewQuery : function()
15505     {
15506         var qe = {
15507             query: '',
15508             forceAll: true,
15509             combo: this,
15510             cancel:false
15511         };
15512         
15513         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15514             return false;
15515         }
15516         
15517         if(!this.alwaysQuery || this.mode == 'local'){
15518             this.onTouchViewLoad();
15519             return;
15520         }
15521         
15522         this.store.load();
15523     },
15524     
15525     onTouchViewBeforeLoad : function(combo,opts)
15526     {
15527         return;
15528     },
15529
15530     // private
15531     onTouchViewLoad : function()
15532     {
15533         if(this.store.getCount() < 1){
15534             this.onTouchViewEmptyResults();
15535             return;
15536         }
15537         
15538         this.clearTouchView();
15539         
15540         var rawValue = this.getRawValue();
15541         
15542         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15543         
15544         this.tickItems = [];
15545         
15546         this.store.data.each(function(d, rowIndex){
15547             var row = this.touchViewListGroup.createChild(template);
15548             
15549             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15550                 row.addClass(d.data.cls);
15551             }
15552             
15553             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15554                 var cfg = {
15555                     data : d.data,
15556                     html : d.data[this.displayField]
15557                 };
15558                 
15559                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15560                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15561                 }
15562             }
15563             row.removeClass('selected');
15564             if(!this.multiple && this.valueField &&
15565                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15566             {
15567                 // radio buttons..
15568                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15569                 row.addClass('selected');
15570             }
15571             
15572             if(this.multiple && this.valueField &&
15573                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15574             {
15575                 
15576                 // checkboxes...
15577                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15578                 this.tickItems.push(d.data);
15579             }
15580             
15581             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15582             
15583         }, this);
15584         
15585         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15586         
15587         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15588
15589         if(this.modalTitle.length){
15590             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15591         }
15592
15593         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15594         
15595         if(this.mobile_restrict_height && listHeight < bodyHeight){
15596             this.touchViewBodyEl.setHeight(listHeight);
15597         }
15598         
15599         var _this = this;
15600         
15601         if(firstChecked && listHeight > bodyHeight){
15602             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15603         }
15604         
15605     },
15606     
15607     onTouchViewLoadException : function()
15608     {
15609         this.hideTouchView();
15610     },
15611     
15612     onTouchViewEmptyResults : function()
15613     {
15614         this.clearTouchView();
15615         
15616         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15617         
15618         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15619         
15620     },
15621     
15622     clearTouchView : function()
15623     {
15624         this.touchViewListGroup.dom.innerHTML = '';
15625     },
15626     
15627     onTouchViewClick : function(e, el, o)
15628     {
15629         e.preventDefault();
15630         
15631         var row = o.row;
15632         var rowIndex = o.rowIndex;
15633         
15634         var r = this.store.getAt(rowIndex);
15635         
15636         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15637             
15638             if(!this.multiple){
15639                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15640                     c.dom.removeAttribute('checked');
15641                 }, this);
15642
15643                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15644
15645                 this.setFromData(r.data);
15646
15647                 var close = this.closeTriggerEl();
15648
15649                 if(close){
15650                     close.show();
15651                 }
15652
15653                 this.hideTouchView();
15654
15655                 this.fireEvent('select', this, r, rowIndex);
15656
15657                 return;
15658             }
15659
15660             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15661                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15662                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15663                 return;
15664             }
15665
15666             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15667             this.addItem(r.data);
15668             this.tickItems.push(r.data);
15669         }
15670     },
15671     
15672     getAutoCreateNativeIOS : function()
15673     {
15674         var cfg = {
15675             cls: 'form-group' //input-group,
15676         };
15677         
15678         var combobox =  {
15679             tag: 'select',
15680             cls : 'roo-ios-select'
15681         };
15682         
15683         if (this.name) {
15684             combobox.name = this.name;
15685         }
15686         
15687         if (this.disabled) {
15688             combobox.disabled = true;
15689         }
15690         
15691         var settings = this;
15692         
15693         ['xs','sm','md','lg'].map(function(size){
15694             if (settings[size]) {
15695                 cfg.cls += ' col-' + size + '-' + settings[size];
15696             }
15697         });
15698         
15699         cfg.cn = combobox;
15700         
15701         return cfg;
15702         
15703     },
15704     
15705     initIOSView : function()
15706     {
15707         this.store.on('load', this.onIOSViewLoad, this);
15708         
15709         return;
15710     },
15711     
15712     onIOSViewLoad : function()
15713     {
15714         if(this.store.getCount() < 1){
15715             return;
15716         }
15717         
15718         this.clearIOSView();
15719         
15720         if(this.allowBlank) {
15721             
15722             var default_text = '-- SELECT --';
15723             
15724             if(this.placeholder.length){
15725                 default_text = this.placeholder;
15726             }
15727             
15728             if(this.emptyTitle.length){
15729                 default_text += ' - ' + this.emptyTitle + ' -';
15730             }
15731             
15732             var opt = this.inputEl().createChild({
15733                 tag: 'option',
15734                 value : 0,
15735                 html : default_text
15736             });
15737             
15738             var o = {};
15739             o[this.valueField] = 0;
15740             o[this.displayField] = default_text;
15741             
15742             this.ios_options.push({
15743                 data : o,
15744                 el : opt
15745             });
15746             
15747         }
15748         
15749         this.store.data.each(function(d, rowIndex){
15750             
15751             var html = '';
15752             
15753             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15754                 html = d.data[this.displayField];
15755             }
15756             
15757             var value = '';
15758             
15759             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15760                 value = d.data[this.valueField];
15761             }
15762             
15763             var option = {
15764                 tag: 'option',
15765                 value : value,
15766                 html : html
15767             };
15768             
15769             if(this.value == d.data[this.valueField]){
15770                 option['selected'] = true;
15771             }
15772             
15773             var opt = this.inputEl().createChild(option);
15774             
15775             this.ios_options.push({
15776                 data : d.data,
15777                 el : opt
15778             });
15779             
15780         }, this);
15781         
15782         this.inputEl().on('change', function(){
15783            this.fireEvent('select', this);
15784         }, this);
15785         
15786     },
15787     
15788     clearIOSView: function()
15789     {
15790         this.inputEl().dom.innerHTML = '';
15791         
15792         this.ios_options = [];
15793     },
15794     
15795     setIOSValue: function(v)
15796     {
15797         this.value = v;
15798         
15799         if(!this.ios_options){
15800             return;
15801         }
15802         
15803         Roo.each(this.ios_options, function(opts){
15804            
15805            opts.el.dom.removeAttribute('selected');
15806            
15807            if(opts.data[this.valueField] != v){
15808                return;
15809            }
15810            
15811            opts.el.dom.setAttribute('selected', true);
15812            
15813         }, this);
15814     }
15815
15816     /** 
15817     * @cfg {Boolean} grow 
15818     * @hide 
15819     */
15820     /** 
15821     * @cfg {Number} growMin 
15822     * @hide 
15823     */
15824     /** 
15825     * @cfg {Number} growMax 
15826     * @hide 
15827     */
15828     /**
15829      * @hide
15830      * @method autoSize
15831      */
15832 });
15833
15834 Roo.apply(Roo.bootstrap.ComboBox,  {
15835     
15836     header : {
15837         tag: 'div',
15838         cls: 'modal-header',
15839         cn: [
15840             {
15841                 tag: 'h4',
15842                 cls: 'modal-title'
15843             }
15844         ]
15845     },
15846     
15847     body : {
15848         tag: 'div',
15849         cls: 'modal-body',
15850         cn: [
15851             {
15852                 tag: 'ul',
15853                 cls: 'list-group'
15854             }
15855         ]
15856     },
15857     
15858     listItemRadio : {
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 radio-inline radio radio-info',
15869                 cn: [
15870                     {
15871                         tag: 'input',
15872                         type: 'radio'
15873                     },
15874                     {
15875                         tag: 'label'
15876                     }
15877                 ]
15878             }
15879         ]
15880     },
15881     
15882     listItemCheckbox : {
15883         tag: 'li',
15884         cls: 'list-group-item',
15885         cn: [
15886             {
15887                 tag: 'span',
15888                 cls: 'roo-combobox-list-group-item-value'
15889             },
15890             {
15891                 tag: 'div',
15892                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15893                 cn: [
15894                     {
15895                         tag: 'input',
15896                         type: 'checkbox'
15897                     },
15898                     {
15899                         tag: 'label'
15900                     }
15901                 ]
15902             }
15903         ]
15904     },
15905     
15906     emptyResult : {
15907         tag: 'div',
15908         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15909     },
15910     
15911     footer : {
15912         tag: 'div',
15913         cls: 'modal-footer',
15914         cn: [
15915             {
15916                 tag: 'div',
15917                 cls: 'row',
15918                 cn: [
15919                     {
15920                         tag: 'div',
15921                         cls: 'col-xs-6 text-left',
15922                         cn: {
15923                             tag: 'button',
15924                             cls: 'btn btn-danger roo-touch-view-cancel',
15925                             html: 'Cancel'
15926                         }
15927                     },
15928                     {
15929                         tag: 'div',
15930                         cls: 'col-xs-6 text-right',
15931                         cn: {
15932                             tag: 'button',
15933                             cls: 'btn btn-success roo-touch-view-ok',
15934                             html: 'OK'
15935                         }
15936                     }
15937                 ]
15938             }
15939         ]
15940         
15941     }
15942 });
15943
15944 Roo.apply(Roo.bootstrap.ComboBox,  {
15945     
15946     touchViewTemplate : {
15947         tag: 'div',
15948         cls: 'modal fade roo-combobox-touch-view',
15949         cn: [
15950             {
15951                 tag: 'div',
15952                 cls: 'modal-dialog',
15953                 style : 'position:fixed', // we have to fix position....
15954                 cn: [
15955                     {
15956                         tag: 'div',
15957                         cls: 'modal-content',
15958                         cn: [
15959                             Roo.bootstrap.ComboBox.header,
15960                             Roo.bootstrap.ComboBox.body,
15961                             Roo.bootstrap.ComboBox.footer
15962                         ]
15963                     }
15964                 ]
15965             }
15966         ]
15967     }
15968 });/*
15969  * Based on:
15970  * Ext JS Library 1.1.1
15971  * Copyright(c) 2006-2007, Ext JS, LLC.
15972  *
15973  * Originally Released Under LGPL - original licence link has changed is not relivant.
15974  *
15975  * Fork - LGPL
15976  * <script type="text/javascript">
15977  */
15978
15979 /**
15980  * @class Roo.View
15981  * @extends Roo.util.Observable
15982  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15983  * This class also supports single and multi selection modes. <br>
15984  * Create a data model bound view:
15985  <pre><code>
15986  var store = new Roo.data.Store(...);
15987
15988  var view = new Roo.View({
15989     el : "my-element",
15990     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15991  
15992     singleSelect: true,
15993     selectedClass: "ydataview-selected",
15994     store: store
15995  });
15996
15997  // listen for node click?
15998  view.on("click", function(vw, index, node, e){
15999  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16000  });
16001
16002  // load XML data
16003  dataModel.load("foobar.xml");
16004  </code></pre>
16005  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16006  * <br><br>
16007  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16008  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16009  * 
16010  * Note: old style constructor is still suported (container, template, config)
16011  * 
16012  * @constructor
16013  * Create a new View
16014  * @param {Object} config The config object
16015  * 
16016  */
16017 Roo.View = function(config, depreciated_tpl, depreciated_config){
16018     
16019     this.parent = false;
16020     
16021     if (typeof(depreciated_tpl) == 'undefined') {
16022         // new way.. - universal constructor.
16023         Roo.apply(this, config);
16024         this.el  = Roo.get(this.el);
16025     } else {
16026         // old format..
16027         this.el  = Roo.get(config);
16028         this.tpl = depreciated_tpl;
16029         Roo.apply(this, depreciated_config);
16030     }
16031     this.wrapEl  = this.el.wrap().wrap();
16032     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16033     
16034     
16035     if(typeof(this.tpl) == "string"){
16036         this.tpl = new Roo.Template(this.tpl);
16037     } else {
16038         // support xtype ctors..
16039         this.tpl = new Roo.factory(this.tpl, Roo);
16040     }
16041     
16042     
16043     this.tpl.compile();
16044     
16045     /** @private */
16046     this.addEvents({
16047         /**
16048          * @event beforeclick
16049          * Fires before a click is processed. Returns false to cancel the default action.
16050          * @param {Roo.View} this
16051          * @param {Number} index The index of the target node
16052          * @param {HTMLElement} node The target node
16053          * @param {Roo.EventObject} e The raw event object
16054          */
16055             "beforeclick" : true,
16056         /**
16057          * @event click
16058          * Fires when a template node is clicked.
16059          * @param {Roo.View} this
16060          * @param {Number} index The index of the target node
16061          * @param {HTMLElement} node The target node
16062          * @param {Roo.EventObject} e The raw event object
16063          */
16064             "click" : true,
16065         /**
16066          * @event dblclick
16067          * Fires when a template node is double clicked.
16068          * @param {Roo.View} this
16069          * @param {Number} index The index of the target node
16070          * @param {HTMLElement} node The target node
16071          * @param {Roo.EventObject} e The raw event object
16072          */
16073             "dblclick" : true,
16074         /**
16075          * @event contextmenu
16076          * Fires when a template node is right clicked.
16077          * @param {Roo.View} this
16078          * @param {Number} index The index of the target node
16079          * @param {HTMLElement} node The target node
16080          * @param {Roo.EventObject} e The raw event object
16081          */
16082             "contextmenu" : true,
16083         /**
16084          * @event selectionchange
16085          * Fires when the selected nodes change.
16086          * @param {Roo.View} this
16087          * @param {Array} selections Array of the selected nodes
16088          */
16089             "selectionchange" : true,
16090     
16091         /**
16092          * @event beforeselect
16093          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16094          * @param {Roo.View} this
16095          * @param {HTMLElement} node The node to be selected
16096          * @param {Array} selections Array of currently selected nodes
16097          */
16098             "beforeselect" : true,
16099         /**
16100          * @event preparedata
16101          * Fires on every row to render, to allow you to change the data.
16102          * @param {Roo.View} this
16103          * @param {Object} data to be rendered (change this)
16104          */
16105           "preparedata" : true
16106           
16107           
16108         });
16109
16110
16111
16112     this.el.on({
16113         "click": this.onClick,
16114         "dblclick": this.onDblClick,
16115         "contextmenu": this.onContextMenu,
16116         scope:this
16117     });
16118
16119     this.selections = [];
16120     this.nodes = [];
16121     this.cmp = new Roo.CompositeElementLite([]);
16122     if(this.store){
16123         this.store = Roo.factory(this.store, Roo.data);
16124         this.setStore(this.store, true);
16125     }
16126     
16127     if ( this.footer && this.footer.xtype) {
16128            
16129          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16130         
16131         this.footer.dataSource = this.store;
16132         this.footer.container = fctr;
16133         this.footer = Roo.factory(this.footer, Roo);
16134         fctr.insertFirst(this.el);
16135         
16136         // this is a bit insane - as the paging toolbar seems to detach the el..
16137 //        dom.parentNode.parentNode.parentNode
16138          // they get detached?
16139     }
16140     
16141     
16142     Roo.View.superclass.constructor.call(this);
16143     
16144     
16145 };
16146
16147 Roo.extend(Roo.View, Roo.util.Observable, {
16148     
16149      /**
16150      * @cfg {Roo.data.Store} store Data store to load data from.
16151      */
16152     store : false,
16153     
16154     /**
16155      * @cfg {String|Roo.Element} el The container element.
16156      */
16157     el : '',
16158     
16159     /**
16160      * @cfg {String|Roo.Template} tpl The template used by this View 
16161      */
16162     tpl : false,
16163     /**
16164      * @cfg {String} dataName the named area of the template to use as the data area
16165      *                          Works with domtemplates roo-name="name"
16166      */
16167     dataName: false,
16168     /**
16169      * @cfg {String} selectedClass The css class to add to selected nodes
16170      */
16171     selectedClass : "x-view-selected",
16172      /**
16173      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16174      */
16175     emptyText : "",
16176     
16177     /**
16178      * @cfg {String} text to display on mask (default Loading)
16179      */
16180     mask : false,
16181     /**
16182      * @cfg {Boolean} multiSelect Allow multiple selection
16183      */
16184     multiSelect : false,
16185     /**
16186      * @cfg {Boolean} singleSelect Allow single selection
16187      */
16188     singleSelect:  false,
16189     
16190     /**
16191      * @cfg {Boolean} toggleSelect - selecting 
16192      */
16193     toggleSelect : false,
16194     
16195     /**
16196      * @cfg {Boolean} tickable - selecting 
16197      */
16198     tickable : false,
16199     
16200     /**
16201      * Returns the element this view is bound to.
16202      * @return {Roo.Element}
16203      */
16204     getEl : function(){
16205         return this.wrapEl;
16206     },
16207     
16208     
16209
16210     /**
16211      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16212      */
16213     refresh : function(){
16214         //Roo.log('refresh');
16215         var t = this.tpl;
16216         
16217         // if we are using something like 'domtemplate', then
16218         // the what gets used is:
16219         // t.applySubtemplate(NAME, data, wrapping data..)
16220         // the outer template then get' applied with
16221         //     the store 'extra data'
16222         // and the body get's added to the
16223         //      roo-name="data" node?
16224         //      <span class='roo-tpl-{name}'></span> ?????
16225         
16226         
16227         
16228         this.clearSelections();
16229         this.el.update("");
16230         var html = [];
16231         var records = this.store.getRange();
16232         if(records.length < 1) {
16233             
16234             // is this valid??  = should it render a template??
16235             
16236             this.el.update(this.emptyText);
16237             return;
16238         }
16239         var el = this.el;
16240         if (this.dataName) {
16241             this.el.update(t.apply(this.store.meta)); //????
16242             el = this.el.child('.roo-tpl-' + this.dataName);
16243         }
16244         
16245         for(var i = 0, len = records.length; i < len; i++){
16246             var data = this.prepareData(records[i].data, i, records[i]);
16247             this.fireEvent("preparedata", this, data, i, records[i]);
16248             
16249             var d = Roo.apply({}, data);
16250             
16251             if(this.tickable){
16252                 Roo.apply(d, {'roo-id' : Roo.id()});
16253                 
16254                 var _this = this;
16255             
16256                 Roo.each(this.parent.item, function(item){
16257                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16258                         return;
16259                     }
16260                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16261                 });
16262             }
16263             
16264             html[html.length] = Roo.util.Format.trim(
16265                 this.dataName ?
16266                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16267                     t.apply(d)
16268             );
16269         }
16270         
16271         
16272         
16273         el.update(html.join(""));
16274         this.nodes = el.dom.childNodes;
16275         this.updateIndexes(0);
16276     },
16277     
16278
16279     /**
16280      * Function to override to reformat the data that is sent to
16281      * the template for each node.
16282      * DEPRICATED - use the preparedata event handler.
16283      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16284      * a JSON object for an UpdateManager bound view).
16285      */
16286     prepareData : function(data, index, record)
16287     {
16288         this.fireEvent("preparedata", this, data, index, record);
16289         return data;
16290     },
16291
16292     onUpdate : function(ds, record){
16293         // Roo.log('on update');   
16294         this.clearSelections();
16295         var index = this.store.indexOf(record);
16296         var n = this.nodes[index];
16297         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16298         n.parentNode.removeChild(n);
16299         this.updateIndexes(index, index);
16300     },
16301
16302     
16303     
16304 // --------- FIXME     
16305     onAdd : function(ds, records, index)
16306     {
16307         //Roo.log(['on Add', ds, records, index] );        
16308         this.clearSelections();
16309         if(this.nodes.length == 0){
16310             this.refresh();
16311             return;
16312         }
16313         var n = this.nodes[index];
16314         for(var i = 0, len = records.length; i < len; i++){
16315             var d = this.prepareData(records[i].data, i, records[i]);
16316             if(n){
16317                 this.tpl.insertBefore(n, d);
16318             }else{
16319                 
16320                 this.tpl.append(this.el, d);
16321             }
16322         }
16323         this.updateIndexes(index);
16324     },
16325
16326     onRemove : function(ds, record, index){
16327        // Roo.log('onRemove');
16328         this.clearSelections();
16329         var el = this.dataName  ?
16330             this.el.child('.roo-tpl-' + this.dataName) :
16331             this.el; 
16332         
16333         el.dom.removeChild(this.nodes[index]);
16334         this.updateIndexes(index);
16335     },
16336
16337     /**
16338      * Refresh an individual node.
16339      * @param {Number} index
16340      */
16341     refreshNode : function(index){
16342         this.onUpdate(this.store, this.store.getAt(index));
16343     },
16344
16345     updateIndexes : function(startIndex, endIndex){
16346         var ns = this.nodes;
16347         startIndex = startIndex || 0;
16348         endIndex = endIndex || ns.length - 1;
16349         for(var i = startIndex; i <= endIndex; i++){
16350             ns[i].nodeIndex = i;
16351         }
16352     },
16353
16354     /**
16355      * Changes the data store this view uses and refresh the view.
16356      * @param {Store} store
16357      */
16358     setStore : function(store, initial){
16359         if(!initial && this.store){
16360             this.store.un("datachanged", this.refresh);
16361             this.store.un("add", this.onAdd);
16362             this.store.un("remove", this.onRemove);
16363             this.store.un("update", this.onUpdate);
16364             this.store.un("clear", this.refresh);
16365             this.store.un("beforeload", this.onBeforeLoad);
16366             this.store.un("load", this.onLoad);
16367             this.store.un("loadexception", this.onLoad);
16368         }
16369         if(store){
16370           
16371             store.on("datachanged", this.refresh, this);
16372             store.on("add", this.onAdd, this);
16373             store.on("remove", this.onRemove, this);
16374             store.on("update", this.onUpdate, this);
16375             store.on("clear", this.refresh, this);
16376             store.on("beforeload", this.onBeforeLoad, this);
16377             store.on("load", this.onLoad, this);
16378             store.on("loadexception", this.onLoad, this);
16379         }
16380         
16381         if(store){
16382             this.refresh();
16383         }
16384     },
16385     /**
16386      * onbeforeLoad - masks the loading area.
16387      *
16388      */
16389     onBeforeLoad : function(store,opts)
16390     {
16391          //Roo.log('onBeforeLoad');   
16392         if (!opts.add) {
16393             this.el.update("");
16394         }
16395         this.el.mask(this.mask ? this.mask : "Loading" ); 
16396     },
16397     onLoad : function ()
16398     {
16399         this.el.unmask();
16400     },
16401     
16402
16403     /**
16404      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16405      * @param {HTMLElement} node
16406      * @return {HTMLElement} The template node
16407      */
16408     findItemFromChild : function(node){
16409         var el = this.dataName  ?
16410             this.el.child('.roo-tpl-' + this.dataName,true) :
16411             this.el.dom; 
16412         
16413         if(!node || node.parentNode == el){
16414                     return node;
16415             }
16416             var p = node.parentNode;
16417             while(p && p != el){
16418             if(p.parentNode == el){
16419                 return p;
16420             }
16421             p = p.parentNode;
16422         }
16423             return null;
16424     },
16425
16426     /** @ignore */
16427     onClick : function(e){
16428         var item = this.findItemFromChild(e.getTarget());
16429         if(item){
16430             var index = this.indexOf(item);
16431             if(this.onItemClick(item, index, e) !== false){
16432                 this.fireEvent("click", this, index, item, e);
16433             }
16434         }else{
16435             this.clearSelections();
16436         }
16437     },
16438
16439     /** @ignore */
16440     onContextMenu : function(e){
16441         var item = this.findItemFromChild(e.getTarget());
16442         if(item){
16443             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16444         }
16445     },
16446
16447     /** @ignore */
16448     onDblClick : function(e){
16449         var item = this.findItemFromChild(e.getTarget());
16450         if(item){
16451             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16452         }
16453     },
16454
16455     onItemClick : function(item, index, e)
16456     {
16457         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16458             return false;
16459         }
16460         if (this.toggleSelect) {
16461             var m = this.isSelected(item) ? 'unselect' : 'select';
16462             //Roo.log(m);
16463             var _t = this;
16464             _t[m](item, true, false);
16465             return true;
16466         }
16467         if(this.multiSelect || this.singleSelect){
16468             if(this.multiSelect && e.shiftKey && this.lastSelection){
16469                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16470             }else{
16471                 this.select(item, this.multiSelect && e.ctrlKey);
16472                 this.lastSelection = item;
16473             }
16474             
16475             if(!this.tickable){
16476                 e.preventDefault();
16477             }
16478             
16479         }
16480         return true;
16481     },
16482
16483     /**
16484      * Get the number of selected nodes.
16485      * @return {Number}
16486      */
16487     getSelectionCount : function(){
16488         return this.selections.length;
16489     },
16490
16491     /**
16492      * Get the currently selected nodes.
16493      * @return {Array} An array of HTMLElements
16494      */
16495     getSelectedNodes : function(){
16496         return this.selections;
16497     },
16498
16499     /**
16500      * Get the indexes of the selected nodes.
16501      * @return {Array}
16502      */
16503     getSelectedIndexes : function(){
16504         var indexes = [], s = this.selections;
16505         for(var i = 0, len = s.length; i < len; i++){
16506             indexes.push(s[i].nodeIndex);
16507         }
16508         return indexes;
16509     },
16510
16511     /**
16512      * Clear all selections
16513      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16514      */
16515     clearSelections : function(suppressEvent){
16516         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16517             this.cmp.elements = this.selections;
16518             this.cmp.removeClass(this.selectedClass);
16519             this.selections = [];
16520             if(!suppressEvent){
16521                 this.fireEvent("selectionchange", this, this.selections);
16522             }
16523         }
16524     },
16525
16526     /**
16527      * Returns true if the passed node is selected
16528      * @param {HTMLElement/Number} node The node or node index
16529      * @return {Boolean}
16530      */
16531     isSelected : function(node){
16532         var s = this.selections;
16533         if(s.length < 1){
16534             return false;
16535         }
16536         node = this.getNode(node);
16537         return s.indexOf(node) !== -1;
16538     },
16539
16540     /**
16541      * Selects nodes.
16542      * @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
16543      * @param {Boolean} keepExisting (optional) true to keep existing selections
16544      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16545      */
16546     select : function(nodeInfo, keepExisting, suppressEvent){
16547         if(nodeInfo instanceof Array){
16548             if(!keepExisting){
16549                 this.clearSelections(true);
16550             }
16551             for(var i = 0, len = nodeInfo.length; i < len; i++){
16552                 this.select(nodeInfo[i], true, true);
16553             }
16554             return;
16555         } 
16556         var node = this.getNode(nodeInfo);
16557         if(!node || this.isSelected(node)){
16558             return; // already selected.
16559         }
16560         if(!keepExisting){
16561             this.clearSelections(true);
16562         }
16563         
16564         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16565             Roo.fly(node).addClass(this.selectedClass);
16566             this.selections.push(node);
16567             if(!suppressEvent){
16568                 this.fireEvent("selectionchange", this, this.selections);
16569             }
16570         }
16571         
16572         
16573     },
16574       /**
16575      * Unselects nodes.
16576      * @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
16577      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16578      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16579      */
16580     unselect : function(nodeInfo, keepExisting, suppressEvent)
16581     {
16582         if(nodeInfo instanceof Array){
16583             Roo.each(this.selections, function(s) {
16584                 this.unselect(s, nodeInfo);
16585             }, this);
16586             return;
16587         }
16588         var node = this.getNode(nodeInfo);
16589         if(!node || !this.isSelected(node)){
16590             //Roo.log("not selected");
16591             return; // not selected.
16592         }
16593         // fireevent???
16594         var ns = [];
16595         Roo.each(this.selections, function(s) {
16596             if (s == node ) {
16597                 Roo.fly(node).removeClass(this.selectedClass);
16598
16599                 return;
16600             }
16601             ns.push(s);
16602         },this);
16603         
16604         this.selections= ns;
16605         this.fireEvent("selectionchange", this, this.selections);
16606     },
16607
16608     /**
16609      * Gets a template node.
16610      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16611      * @return {HTMLElement} The node or null if it wasn't found
16612      */
16613     getNode : function(nodeInfo){
16614         if(typeof nodeInfo == "string"){
16615             return document.getElementById(nodeInfo);
16616         }else if(typeof nodeInfo == "number"){
16617             return this.nodes[nodeInfo];
16618         }
16619         return nodeInfo;
16620     },
16621
16622     /**
16623      * Gets a range template nodes.
16624      * @param {Number} startIndex
16625      * @param {Number} endIndex
16626      * @return {Array} An array of nodes
16627      */
16628     getNodes : function(start, end){
16629         var ns = this.nodes;
16630         start = start || 0;
16631         end = typeof end == "undefined" ? ns.length - 1 : end;
16632         var nodes = [];
16633         if(start <= end){
16634             for(var i = start; i <= end; i++){
16635                 nodes.push(ns[i]);
16636             }
16637         } else{
16638             for(var i = start; i >= end; i--){
16639                 nodes.push(ns[i]);
16640             }
16641         }
16642         return nodes;
16643     },
16644
16645     /**
16646      * Finds the index of the passed node
16647      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16648      * @return {Number} The index of the node or -1
16649      */
16650     indexOf : function(node){
16651         node = this.getNode(node);
16652         if(typeof node.nodeIndex == "number"){
16653             return node.nodeIndex;
16654         }
16655         var ns = this.nodes;
16656         for(var i = 0, len = ns.length; i < len; i++){
16657             if(ns[i] == node){
16658                 return i;
16659             }
16660         }
16661         return -1;
16662     }
16663 });
16664 /*
16665  * - LGPL
16666  *
16667  * based on jquery fullcalendar
16668  * 
16669  */
16670
16671 Roo.bootstrap = Roo.bootstrap || {};
16672 /**
16673  * @class Roo.bootstrap.Calendar
16674  * @extends Roo.bootstrap.Component
16675  * Bootstrap Calendar class
16676  * @cfg {Boolean} loadMask (true|false) default false
16677  * @cfg {Object} header generate the user specific header of the calendar, default false
16678
16679  * @constructor
16680  * Create a new Container
16681  * @param {Object} config The config object
16682  */
16683
16684
16685
16686 Roo.bootstrap.Calendar = function(config){
16687     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16688      this.addEvents({
16689         /**
16690              * @event select
16691              * Fires when a date is selected
16692              * @param {DatePicker} this
16693              * @param {Date} date The selected date
16694              */
16695         'select': true,
16696         /**
16697              * @event monthchange
16698              * Fires when the displayed month changes 
16699              * @param {DatePicker} this
16700              * @param {Date} date The selected month
16701              */
16702         'monthchange': true,
16703         /**
16704              * @event evententer
16705              * Fires when mouse over an event
16706              * @param {Calendar} this
16707              * @param {event} Event
16708              */
16709         'evententer': true,
16710         /**
16711              * @event eventleave
16712              * Fires when the mouse leaves an
16713              * @param {Calendar} this
16714              * @param {event}
16715              */
16716         'eventleave': true,
16717         /**
16718              * @event eventclick
16719              * Fires when the mouse click an
16720              * @param {Calendar} this
16721              * @param {event}
16722              */
16723         'eventclick': true
16724         
16725     });
16726
16727 };
16728
16729 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16730     
16731      /**
16732      * @cfg {Number} startDay
16733      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16734      */
16735     startDay : 0,
16736     
16737     loadMask : false,
16738     
16739     header : false,
16740       
16741     getAutoCreate : function(){
16742         
16743         
16744         var fc_button = function(name, corner, style, content ) {
16745             return Roo.apply({},{
16746                 tag : 'span',
16747                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16748                          (corner.length ?
16749                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16750                             ''
16751                         ),
16752                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16753                 unselectable: 'on'
16754             });
16755         };
16756         
16757         var header = {};
16758         
16759         if(!this.header){
16760             header = {
16761                 tag : 'table',
16762                 cls : 'fc-header',
16763                 style : 'width:100%',
16764                 cn : [
16765                     {
16766                         tag: 'tr',
16767                         cn : [
16768                             {
16769                                 tag : 'td',
16770                                 cls : 'fc-header-left',
16771                                 cn : [
16772                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16773                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16774                                     { tag: 'span', cls: 'fc-header-space' },
16775                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16776
16777
16778                                 ]
16779                             },
16780
16781                             {
16782                                 tag : 'td',
16783                                 cls : 'fc-header-center',
16784                                 cn : [
16785                                     {
16786                                         tag: 'span',
16787                                         cls: 'fc-header-title',
16788                                         cn : {
16789                                             tag: 'H2',
16790                                             html : 'month / year'
16791                                         }
16792                                     }
16793
16794                                 ]
16795                             },
16796                             {
16797                                 tag : 'td',
16798                                 cls : 'fc-header-right',
16799                                 cn : [
16800                               /*      fc_button('month', 'left', '', 'month' ),
16801                                     fc_button('week', '', '', 'week' ),
16802                                     fc_button('day', 'right', '', 'day' )
16803                                 */    
16804
16805                                 ]
16806                             }
16807
16808                         ]
16809                     }
16810                 ]
16811             };
16812         }
16813         
16814         header = this.header;
16815         
16816        
16817         var cal_heads = function() {
16818             var ret = [];
16819             // fixme - handle this.
16820             
16821             for (var i =0; i < Date.dayNames.length; i++) {
16822                 var d = Date.dayNames[i];
16823                 ret.push({
16824                     tag: 'th',
16825                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16826                     html : d.substring(0,3)
16827                 });
16828                 
16829             }
16830             ret[0].cls += ' fc-first';
16831             ret[6].cls += ' fc-last';
16832             return ret;
16833         };
16834         var cal_cell = function(n) {
16835             return  {
16836                 tag: 'td',
16837                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16838                 cn : [
16839                     {
16840                         cn : [
16841                             {
16842                                 cls: 'fc-day-number',
16843                                 html: 'D'
16844                             },
16845                             {
16846                                 cls: 'fc-day-content',
16847                              
16848                                 cn : [
16849                                      {
16850                                         style: 'position: relative;' // height: 17px;
16851                                     }
16852                                 ]
16853                             }
16854                             
16855                             
16856                         ]
16857                     }
16858                 ]
16859                 
16860             }
16861         };
16862         var cal_rows = function() {
16863             
16864             var ret = [];
16865             for (var r = 0; r < 6; r++) {
16866                 var row= {
16867                     tag : 'tr',
16868                     cls : 'fc-week',
16869                     cn : []
16870                 };
16871                 
16872                 for (var i =0; i < Date.dayNames.length; i++) {
16873                     var d = Date.dayNames[i];
16874                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16875
16876                 }
16877                 row.cn[0].cls+=' fc-first';
16878                 row.cn[0].cn[0].style = 'min-height:90px';
16879                 row.cn[6].cls+=' fc-last';
16880                 ret.push(row);
16881                 
16882             }
16883             ret[0].cls += ' fc-first';
16884             ret[4].cls += ' fc-prev-last';
16885             ret[5].cls += ' fc-last';
16886             return ret;
16887             
16888         };
16889         
16890         var cal_table = {
16891             tag: 'table',
16892             cls: 'fc-border-separate',
16893             style : 'width:100%',
16894             cellspacing  : 0,
16895             cn : [
16896                 { 
16897                     tag: 'thead',
16898                     cn : [
16899                         { 
16900                             tag: 'tr',
16901                             cls : 'fc-first fc-last',
16902                             cn : cal_heads()
16903                         }
16904                     ]
16905                 },
16906                 { 
16907                     tag: 'tbody',
16908                     cn : cal_rows()
16909                 }
16910                   
16911             ]
16912         };
16913          
16914          var cfg = {
16915             cls : 'fc fc-ltr',
16916             cn : [
16917                 header,
16918                 {
16919                     cls : 'fc-content',
16920                     style : "position: relative;",
16921                     cn : [
16922                         {
16923                             cls : 'fc-view fc-view-month fc-grid',
16924                             style : 'position: relative',
16925                             unselectable : 'on',
16926                             cn : [
16927                                 {
16928                                     cls : 'fc-event-container',
16929                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16930                                 },
16931                                 cal_table
16932                             ]
16933                         }
16934                     ]
16935     
16936                 }
16937            ] 
16938             
16939         };
16940         
16941          
16942         
16943         return cfg;
16944     },
16945     
16946     
16947     initEvents : function()
16948     {
16949         if(!this.store){
16950             throw "can not find store for calendar";
16951         }
16952         
16953         var mark = {
16954             tag: "div",
16955             cls:"x-dlg-mask",
16956             style: "text-align:center",
16957             cn: [
16958                 {
16959                     tag: "div",
16960                     style: "background-color:white;width:50%;margin:250 auto",
16961                     cn: [
16962                         {
16963                             tag: "img",
16964                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16965                         },
16966                         {
16967                             tag: "span",
16968                             html: "Loading"
16969                         }
16970                         
16971                     ]
16972                 }
16973             ]
16974         };
16975         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16976         
16977         var size = this.el.select('.fc-content', true).first().getSize();
16978         this.maskEl.setSize(size.width, size.height);
16979         this.maskEl.enableDisplayMode("block");
16980         if(!this.loadMask){
16981             this.maskEl.hide();
16982         }
16983         
16984         this.store = Roo.factory(this.store, Roo.data);
16985         this.store.on('load', this.onLoad, this);
16986         this.store.on('beforeload', this.onBeforeLoad, this);
16987         
16988         this.resize();
16989         
16990         this.cells = this.el.select('.fc-day',true);
16991         //Roo.log(this.cells);
16992         this.textNodes = this.el.query('.fc-day-number');
16993         this.cells.addClassOnOver('fc-state-hover');
16994         
16995         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16996         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16997         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16998         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16999         
17000         this.on('monthchange', this.onMonthChange, this);
17001         
17002         this.update(new Date().clearTime());
17003     },
17004     
17005     resize : function() {
17006         var sz  = this.el.getSize();
17007         
17008         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17009         this.el.select('.fc-day-content div',true).setHeight(34);
17010     },
17011     
17012     
17013     // private
17014     showPrevMonth : function(e){
17015         this.update(this.activeDate.add("mo", -1));
17016     },
17017     showToday : function(e){
17018         this.update(new Date().clearTime());
17019     },
17020     // private
17021     showNextMonth : function(e){
17022         this.update(this.activeDate.add("mo", 1));
17023     },
17024
17025     // private
17026     showPrevYear : function(){
17027         this.update(this.activeDate.add("y", -1));
17028     },
17029
17030     // private
17031     showNextYear : function(){
17032         this.update(this.activeDate.add("y", 1));
17033     },
17034
17035     
17036    // private
17037     update : function(date)
17038     {
17039         var vd = this.activeDate;
17040         this.activeDate = date;
17041 //        if(vd && this.el){
17042 //            var t = date.getTime();
17043 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17044 //                Roo.log('using add remove');
17045 //                
17046 //                this.fireEvent('monthchange', this, date);
17047 //                
17048 //                this.cells.removeClass("fc-state-highlight");
17049 //                this.cells.each(function(c){
17050 //                   if(c.dateValue == t){
17051 //                       c.addClass("fc-state-highlight");
17052 //                       setTimeout(function(){
17053 //                            try{c.dom.firstChild.focus();}catch(e){}
17054 //                       }, 50);
17055 //                       return false;
17056 //                   }
17057 //                   return true;
17058 //                });
17059 //                return;
17060 //            }
17061 //        }
17062         
17063         var days = date.getDaysInMonth();
17064         
17065         var firstOfMonth = date.getFirstDateOfMonth();
17066         var startingPos = firstOfMonth.getDay()-this.startDay;
17067         
17068         if(startingPos < this.startDay){
17069             startingPos += 7;
17070         }
17071         
17072         var pm = date.add(Date.MONTH, -1);
17073         var prevStart = pm.getDaysInMonth()-startingPos;
17074 //        
17075         this.cells = this.el.select('.fc-day',true);
17076         this.textNodes = this.el.query('.fc-day-number');
17077         this.cells.addClassOnOver('fc-state-hover');
17078         
17079         var cells = this.cells.elements;
17080         var textEls = this.textNodes;
17081         
17082         Roo.each(cells, function(cell){
17083             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17084         });
17085         
17086         days += startingPos;
17087
17088         // convert everything to numbers so it's fast
17089         var day = 86400000;
17090         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17091         //Roo.log(d);
17092         //Roo.log(pm);
17093         //Roo.log(prevStart);
17094         
17095         var today = new Date().clearTime().getTime();
17096         var sel = date.clearTime().getTime();
17097         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17098         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17099         var ddMatch = this.disabledDatesRE;
17100         var ddText = this.disabledDatesText;
17101         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17102         var ddaysText = this.disabledDaysText;
17103         var format = this.format;
17104         
17105         var setCellClass = function(cal, cell){
17106             cell.row = 0;
17107             cell.events = [];
17108             cell.more = [];
17109             //Roo.log('set Cell Class');
17110             cell.title = "";
17111             var t = d.getTime();
17112             
17113             //Roo.log(d);
17114             
17115             cell.dateValue = t;
17116             if(t == today){
17117                 cell.className += " fc-today";
17118                 cell.className += " fc-state-highlight";
17119                 cell.title = cal.todayText;
17120             }
17121             if(t == sel){
17122                 // disable highlight in other month..
17123                 //cell.className += " fc-state-highlight";
17124                 
17125             }
17126             // disabling
17127             if(t < min) {
17128                 cell.className = " fc-state-disabled";
17129                 cell.title = cal.minText;
17130                 return;
17131             }
17132             if(t > max) {
17133                 cell.className = " fc-state-disabled";
17134                 cell.title = cal.maxText;
17135                 return;
17136             }
17137             if(ddays){
17138                 if(ddays.indexOf(d.getDay()) != -1){
17139                     cell.title = ddaysText;
17140                     cell.className = " fc-state-disabled";
17141                 }
17142             }
17143             if(ddMatch && format){
17144                 var fvalue = d.dateFormat(format);
17145                 if(ddMatch.test(fvalue)){
17146                     cell.title = ddText.replace("%0", fvalue);
17147                     cell.className = " fc-state-disabled";
17148                 }
17149             }
17150             
17151             if (!cell.initialClassName) {
17152                 cell.initialClassName = cell.dom.className;
17153             }
17154             
17155             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17156         };
17157
17158         var i = 0;
17159         
17160         for(; i < startingPos; i++) {
17161             textEls[i].innerHTML = (++prevStart);
17162             d.setDate(d.getDate()+1);
17163             
17164             cells[i].className = "fc-past fc-other-month";
17165             setCellClass(this, cells[i]);
17166         }
17167         
17168         var intDay = 0;
17169         
17170         for(; i < days; i++){
17171             intDay = i - startingPos + 1;
17172             textEls[i].innerHTML = (intDay);
17173             d.setDate(d.getDate()+1);
17174             
17175             cells[i].className = ''; // "x-date-active";
17176             setCellClass(this, cells[i]);
17177         }
17178         var extraDays = 0;
17179         
17180         for(; i < 42; i++) {
17181             textEls[i].innerHTML = (++extraDays);
17182             d.setDate(d.getDate()+1);
17183             
17184             cells[i].className = "fc-future fc-other-month";
17185             setCellClass(this, cells[i]);
17186         }
17187         
17188         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17189         
17190         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17191         
17192         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17193         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17194         
17195         if(totalRows != 6){
17196             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17197             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17198         }
17199         
17200         this.fireEvent('monthchange', this, date);
17201         
17202         
17203         /*
17204         if(!this.internalRender){
17205             var main = this.el.dom.firstChild;
17206             var w = main.offsetWidth;
17207             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17208             Roo.fly(main).setWidth(w);
17209             this.internalRender = true;
17210             // opera does not respect the auto grow header center column
17211             // then, after it gets a width opera refuses to recalculate
17212             // without a second pass
17213             if(Roo.isOpera && !this.secondPass){
17214                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17215                 this.secondPass = true;
17216                 this.update.defer(10, this, [date]);
17217             }
17218         }
17219         */
17220         
17221     },
17222     
17223     findCell : function(dt) {
17224         dt = dt.clearTime().getTime();
17225         var ret = false;
17226         this.cells.each(function(c){
17227             //Roo.log("check " +c.dateValue + '?=' + dt);
17228             if(c.dateValue == dt){
17229                 ret = c;
17230                 return false;
17231             }
17232             return true;
17233         });
17234         
17235         return ret;
17236     },
17237     
17238     findCells : function(ev) {
17239         var s = ev.start.clone().clearTime().getTime();
17240        // Roo.log(s);
17241         var e= ev.end.clone().clearTime().getTime();
17242        // Roo.log(e);
17243         var ret = [];
17244         this.cells.each(function(c){
17245              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17246             
17247             if(c.dateValue > e){
17248                 return ;
17249             }
17250             if(c.dateValue < s){
17251                 return ;
17252             }
17253             ret.push(c);
17254         });
17255         
17256         return ret;    
17257     },
17258     
17259 //    findBestRow: function(cells)
17260 //    {
17261 //        var ret = 0;
17262 //        
17263 //        for (var i =0 ; i < cells.length;i++) {
17264 //            ret  = Math.max(cells[i].rows || 0,ret);
17265 //        }
17266 //        return ret;
17267 //        
17268 //    },
17269     
17270     
17271     addItem : function(ev)
17272     {
17273         // look for vertical location slot in
17274         var cells = this.findCells(ev);
17275         
17276 //        ev.row = this.findBestRow(cells);
17277         
17278         // work out the location.
17279         
17280         var crow = false;
17281         var rows = [];
17282         for(var i =0; i < cells.length; i++) {
17283             
17284             cells[i].row = cells[0].row;
17285             
17286             if(i == 0){
17287                 cells[i].row = cells[i].row + 1;
17288             }
17289             
17290             if (!crow) {
17291                 crow = {
17292                     start : cells[i],
17293                     end :  cells[i]
17294                 };
17295                 continue;
17296             }
17297             if (crow.start.getY() == cells[i].getY()) {
17298                 // on same row.
17299                 crow.end = cells[i];
17300                 continue;
17301             }
17302             // different row.
17303             rows.push(crow);
17304             crow = {
17305                 start: cells[i],
17306                 end : cells[i]
17307             };
17308             
17309         }
17310         
17311         rows.push(crow);
17312         ev.els = [];
17313         ev.rows = rows;
17314         ev.cells = cells;
17315         
17316         cells[0].events.push(ev);
17317         
17318         this.calevents.push(ev);
17319     },
17320     
17321     clearEvents: function() {
17322         
17323         if(!this.calevents){
17324             return;
17325         }
17326         
17327         Roo.each(this.cells.elements, function(c){
17328             c.row = 0;
17329             c.events = [];
17330             c.more = [];
17331         });
17332         
17333         Roo.each(this.calevents, function(e) {
17334             Roo.each(e.els, function(el) {
17335                 el.un('mouseenter' ,this.onEventEnter, this);
17336                 el.un('mouseleave' ,this.onEventLeave, this);
17337                 el.remove();
17338             },this);
17339         },this);
17340         
17341         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17342             e.remove();
17343         });
17344         
17345     },
17346     
17347     renderEvents: function()
17348     {   
17349         var _this = this;
17350         
17351         this.cells.each(function(c) {
17352             
17353             if(c.row < 5){
17354                 return;
17355             }
17356             
17357             var ev = c.events;
17358             
17359             var r = 4;
17360             if(c.row != c.events.length){
17361                 r = 4 - (4 - (c.row - c.events.length));
17362             }
17363             
17364             c.events = ev.slice(0, r);
17365             c.more = ev.slice(r);
17366             
17367             if(c.more.length && c.more.length == 1){
17368                 c.events.push(c.more.pop());
17369             }
17370             
17371             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17372             
17373         });
17374             
17375         this.cells.each(function(c) {
17376             
17377             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17378             
17379             
17380             for (var e = 0; e < c.events.length; e++){
17381                 var ev = c.events[e];
17382                 var rows = ev.rows;
17383                 
17384                 for(var i = 0; i < rows.length; i++) {
17385                 
17386                     // how many rows should it span..
17387
17388                     var  cfg = {
17389                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17390                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17391
17392                         unselectable : "on",
17393                         cn : [
17394                             {
17395                                 cls: 'fc-event-inner',
17396                                 cn : [
17397     //                                {
17398     //                                  tag:'span',
17399     //                                  cls: 'fc-event-time',
17400     //                                  html : cells.length > 1 ? '' : ev.time
17401     //                                },
17402                                     {
17403                                       tag:'span',
17404                                       cls: 'fc-event-title',
17405                                       html : String.format('{0}', ev.title)
17406                                     }
17407
17408
17409                                 ]
17410                             },
17411                             {
17412                                 cls: 'ui-resizable-handle ui-resizable-e',
17413                                 html : '&nbsp;&nbsp;&nbsp'
17414                             }
17415
17416                         ]
17417                     };
17418
17419                     if (i == 0) {
17420                         cfg.cls += ' fc-event-start';
17421                     }
17422                     if ((i+1) == rows.length) {
17423                         cfg.cls += ' fc-event-end';
17424                     }
17425
17426                     var ctr = _this.el.select('.fc-event-container',true).first();
17427                     var cg = ctr.createChild(cfg);
17428
17429                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17430                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17431
17432                     var r = (c.more.length) ? 1 : 0;
17433                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17434                     cg.setWidth(ebox.right - sbox.x -2);
17435
17436                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17437                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17438                     cg.on('click', _this.onEventClick, _this, ev);
17439
17440                     ev.els.push(cg);
17441                     
17442                 }
17443                 
17444             }
17445             
17446             
17447             if(c.more.length){
17448                 var  cfg = {
17449                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17450                     style : 'position: absolute',
17451                     unselectable : "on",
17452                     cn : [
17453                         {
17454                             cls: 'fc-event-inner',
17455                             cn : [
17456                                 {
17457                                   tag:'span',
17458                                   cls: 'fc-event-title',
17459                                   html : 'More'
17460                                 }
17461
17462
17463                             ]
17464                         },
17465                         {
17466                             cls: 'ui-resizable-handle ui-resizable-e',
17467                             html : '&nbsp;&nbsp;&nbsp'
17468                         }
17469
17470                     ]
17471                 };
17472
17473                 var ctr = _this.el.select('.fc-event-container',true).first();
17474                 var cg = ctr.createChild(cfg);
17475
17476                 var sbox = c.select('.fc-day-content',true).first().getBox();
17477                 var ebox = c.select('.fc-day-content',true).first().getBox();
17478                 //Roo.log(cg);
17479                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17480                 cg.setWidth(ebox.right - sbox.x -2);
17481
17482                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17483                 
17484             }
17485             
17486         });
17487         
17488         
17489         
17490     },
17491     
17492     onEventEnter: function (e, el,event,d) {
17493         this.fireEvent('evententer', this, el, event);
17494     },
17495     
17496     onEventLeave: function (e, el,event,d) {
17497         this.fireEvent('eventleave', this, el, event);
17498     },
17499     
17500     onEventClick: function (e, el,event,d) {
17501         this.fireEvent('eventclick', this, el, event);
17502     },
17503     
17504     onMonthChange: function () {
17505         this.store.load();
17506     },
17507     
17508     onMoreEventClick: function(e, el, more)
17509     {
17510         var _this = this;
17511         
17512         this.calpopover.placement = 'right';
17513         this.calpopover.setTitle('More');
17514         
17515         this.calpopover.setContent('');
17516         
17517         var ctr = this.calpopover.el.select('.popover-content', true).first();
17518         
17519         Roo.each(more, function(m){
17520             var cfg = {
17521                 cls : 'fc-event-hori fc-event-draggable',
17522                 html : m.title
17523             };
17524             var cg = ctr.createChild(cfg);
17525             
17526             cg.on('click', _this.onEventClick, _this, m);
17527         });
17528         
17529         this.calpopover.show(el);
17530         
17531         
17532     },
17533     
17534     onLoad: function () 
17535     {   
17536         this.calevents = [];
17537         var cal = this;
17538         
17539         if(this.store.getCount() > 0){
17540             this.store.data.each(function(d){
17541                cal.addItem({
17542                     id : d.data.id,
17543                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17544                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17545                     time : d.data.start_time,
17546                     title : d.data.title,
17547                     description : d.data.description,
17548                     venue : d.data.venue
17549                 });
17550             });
17551         }
17552         
17553         this.renderEvents();
17554         
17555         if(this.calevents.length && this.loadMask){
17556             this.maskEl.hide();
17557         }
17558     },
17559     
17560     onBeforeLoad: function()
17561     {
17562         this.clearEvents();
17563         if(this.loadMask){
17564             this.maskEl.show();
17565         }
17566     }
17567 });
17568
17569  
17570  /*
17571  * - LGPL
17572  *
17573  * element
17574  * 
17575  */
17576
17577 /**
17578  * @class Roo.bootstrap.Popover
17579  * @extends Roo.bootstrap.Component
17580  * Bootstrap Popover class
17581  * @cfg {String} html contents of the popover   (or false to use children..)
17582  * @cfg {String} title of popover (or false to hide)
17583  * @cfg {String} placement how it is placed
17584  * @cfg {String} trigger click || hover (or false to trigger manually)
17585  * @cfg {String} over what (parent or false to trigger manually.)
17586  * @cfg {Number} delay - delay before showing
17587  
17588  * @constructor
17589  * Create a new Popover
17590  * @param {Object} config The config object
17591  */
17592
17593 Roo.bootstrap.Popover = function(config){
17594     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17595     
17596     this.addEvents({
17597         // raw events
17598          /**
17599          * @event show
17600          * After the popover show
17601          * 
17602          * @param {Roo.bootstrap.Popover} this
17603          */
17604         "show" : true,
17605         /**
17606          * @event hide
17607          * After the popover hide
17608          * 
17609          * @param {Roo.bootstrap.Popover} this
17610          */
17611         "hide" : true
17612     });
17613 };
17614
17615 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17616     
17617     title: 'Fill in a title',
17618     html: false,
17619     
17620     placement : 'right',
17621     trigger : 'hover', // hover
17622     
17623     delay : 0,
17624     
17625     over: 'parent',
17626     
17627     can_build_overlaid : false,
17628     
17629     getChildContainer : function()
17630     {
17631         return this.el.select('.popover-content',true).first();
17632     },
17633     
17634     getAutoCreate : function(){
17635          
17636         var cfg = {
17637            cls : 'popover roo-dynamic',
17638            style: 'display:block',
17639            cn : [
17640                 {
17641                     cls : 'arrow'
17642                 },
17643                 {
17644                     cls : 'popover-inner',
17645                     cn : [
17646                         {
17647                             tag: 'h3',
17648                             cls: 'popover-title popover-header',
17649                             html : this.title
17650                         },
17651                         {
17652                             cls : 'popover-content popover-body',
17653                             html : this.html
17654                         }
17655                     ]
17656                     
17657                 }
17658            ]
17659         };
17660         
17661         return cfg;
17662     },
17663     setTitle: function(str)
17664     {
17665         this.title = str;
17666         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17667     },
17668     setContent: function(str)
17669     {
17670         this.html = str;
17671         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17672     },
17673     // as it get's added to the bottom of the page.
17674     onRender : function(ct, position)
17675     {
17676         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17677         if(!this.el){
17678             var cfg = Roo.apply({},  this.getAutoCreate());
17679             cfg.id = Roo.id();
17680             
17681             if (this.cls) {
17682                 cfg.cls += ' ' + this.cls;
17683             }
17684             if (this.style) {
17685                 cfg.style = this.style;
17686             }
17687             //Roo.log("adding to ");
17688             this.el = Roo.get(document.body).createChild(cfg, position);
17689 //            Roo.log(this.el);
17690         }
17691         this.initEvents();
17692     },
17693     
17694     initEvents : function()
17695     {
17696         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17697         this.el.enableDisplayMode('block');
17698         this.el.hide();
17699         if (this.over === false) {
17700             return; 
17701         }
17702         if (this.triggers === false) {
17703             return;
17704         }
17705         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17706         var triggers = this.trigger ? this.trigger.split(' ') : [];
17707         Roo.each(triggers, function(trigger) {
17708         
17709             if (trigger == 'click') {
17710                 on_el.on('click', this.toggle, this);
17711             } else if (trigger != 'manual') {
17712                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17713                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17714       
17715                 on_el.on(eventIn  ,this.enter, this);
17716                 on_el.on(eventOut, this.leave, this);
17717             }
17718         }, this);
17719         
17720     },
17721     
17722     
17723     // private
17724     timeout : null,
17725     hoverState : null,
17726     
17727     toggle : function () {
17728         this.hoverState == 'in' ? this.leave() : this.enter();
17729     },
17730     
17731     enter : function () {
17732         
17733         clearTimeout(this.timeout);
17734     
17735         this.hoverState = 'in';
17736     
17737         if (!this.delay || !this.delay.show) {
17738             this.show();
17739             return;
17740         }
17741         var _t = this;
17742         this.timeout = setTimeout(function () {
17743             if (_t.hoverState == 'in') {
17744                 _t.show();
17745             }
17746         }, this.delay.show)
17747     },
17748     
17749     leave : function() {
17750         clearTimeout(this.timeout);
17751     
17752         this.hoverState = 'out';
17753     
17754         if (!this.delay || !this.delay.hide) {
17755             this.hide();
17756             return;
17757         }
17758         var _t = this;
17759         this.timeout = setTimeout(function () {
17760             if (_t.hoverState == 'out') {
17761                 _t.hide();
17762             }
17763         }, this.delay.hide)
17764     },
17765     
17766     show : function (on_el)
17767     {
17768         if (!on_el) {
17769             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17770         }
17771         
17772         // set content.
17773         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17774         if (this.html !== false) {
17775             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17776         }
17777         this.el.removeClass([
17778             'fade','top','bottom', 'left', 'right','in',
17779             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17780         ]);
17781         if (!this.title.length) {
17782             this.el.select('.popover-title',true).hide();
17783         }
17784         
17785         var placement = typeof this.placement == 'function' ?
17786             this.placement.call(this, this.el, on_el) :
17787             this.placement;
17788             
17789         var autoToken = /\s?auto?\s?/i;
17790         var autoPlace = autoToken.test(placement);
17791         if (autoPlace) {
17792             placement = placement.replace(autoToken, '') || 'top';
17793         }
17794         
17795         //this.el.detach()
17796         //this.el.setXY([0,0]);
17797         this.el.show();
17798         this.el.dom.style.display='block';
17799         this.el.addClass(placement);
17800         
17801         //this.el.appendTo(on_el);
17802         
17803         var p = this.getPosition();
17804         var box = this.el.getBox();
17805         
17806         if (autoPlace) {
17807             // fixme..
17808         }
17809         var align = Roo.bootstrap.Popover.alignment[placement];
17810         
17811 //        Roo.log(align);
17812         this.el.alignTo(on_el, align[0],align[1]);
17813         //var arrow = this.el.select('.arrow',true).first();
17814         //arrow.set(align[2], 
17815         
17816         this.el.addClass('in');
17817         
17818         
17819         if (this.el.hasClass('fade')) {
17820             // fade it?
17821         }
17822         
17823         this.hoverState = 'in';
17824         
17825         this.fireEvent('show', this);
17826         
17827     },
17828     hide : function()
17829     {
17830         this.el.setXY([0,0]);
17831         this.el.removeClass('in');
17832         this.el.hide();
17833         this.hoverState = null;
17834         
17835         this.fireEvent('hide', this);
17836     }
17837     
17838 });
17839
17840 Roo.bootstrap.Popover.alignment = {
17841     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17842     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17843     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17844     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17845 };
17846
17847  /*
17848  * - LGPL
17849  *
17850  * Progress
17851  * 
17852  */
17853
17854 /**
17855  * @class Roo.bootstrap.Progress
17856  * @extends Roo.bootstrap.Component
17857  * Bootstrap Progress class
17858  * @cfg {Boolean} striped striped of the progress bar
17859  * @cfg {Boolean} active animated of the progress bar
17860  * 
17861  * 
17862  * @constructor
17863  * Create a new Progress
17864  * @param {Object} config The config object
17865  */
17866
17867 Roo.bootstrap.Progress = function(config){
17868     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17869 };
17870
17871 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17872     
17873     striped : false,
17874     active: false,
17875     
17876     getAutoCreate : function(){
17877         var cfg = {
17878             tag: 'div',
17879             cls: 'progress'
17880         };
17881         
17882         
17883         if(this.striped){
17884             cfg.cls += ' progress-striped';
17885         }
17886       
17887         if(this.active){
17888             cfg.cls += ' active';
17889         }
17890         
17891         
17892         return cfg;
17893     }
17894    
17895 });
17896
17897  
17898
17899  /*
17900  * - LGPL
17901  *
17902  * ProgressBar
17903  * 
17904  */
17905
17906 /**
17907  * @class Roo.bootstrap.ProgressBar
17908  * @extends Roo.bootstrap.Component
17909  * Bootstrap ProgressBar class
17910  * @cfg {Number} aria_valuenow aria-value now
17911  * @cfg {Number} aria_valuemin aria-value min
17912  * @cfg {Number} aria_valuemax aria-value max
17913  * @cfg {String} label label for the progress bar
17914  * @cfg {String} panel (success | info | warning | danger )
17915  * @cfg {String} role role of the progress bar
17916  * @cfg {String} sr_only text
17917  * 
17918  * 
17919  * @constructor
17920  * Create a new ProgressBar
17921  * @param {Object} config The config object
17922  */
17923
17924 Roo.bootstrap.ProgressBar = function(config){
17925     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17926 };
17927
17928 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17929     
17930     aria_valuenow : 0,
17931     aria_valuemin : 0,
17932     aria_valuemax : 100,
17933     label : false,
17934     panel : false,
17935     role : false,
17936     sr_only: false,
17937     
17938     getAutoCreate : function()
17939     {
17940         
17941         var cfg = {
17942             tag: 'div',
17943             cls: 'progress-bar',
17944             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17945         };
17946         
17947         if(this.sr_only){
17948             cfg.cn = {
17949                 tag: 'span',
17950                 cls: 'sr-only',
17951                 html: this.sr_only
17952             }
17953         }
17954         
17955         if(this.role){
17956             cfg.role = this.role;
17957         }
17958         
17959         if(this.aria_valuenow){
17960             cfg['aria-valuenow'] = this.aria_valuenow;
17961         }
17962         
17963         if(this.aria_valuemin){
17964             cfg['aria-valuemin'] = this.aria_valuemin;
17965         }
17966         
17967         if(this.aria_valuemax){
17968             cfg['aria-valuemax'] = this.aria_valuemax;
17969         }
17970         
17971         if(this.label && !this.sr_only){
17972             cfg.html = this.label;
17973         }
17974         
17975         if(this.panel){
17976             cfg.cls += ' progress-bar-' + this.panel;
17977         }
17978         
17979         return cfg;
17980     },
17981     
17982     update : function(aria_valuenow)
17983     {
17984         this.aria_valuenow = aria_valuenow;
17985         
17986         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17987     }
17988    
17989 });
17990
17991  
17992
17993  /*
17994  * - LGPL
17995  *
17996  * column
17997  * 
17998  */
17999
18000 /**
18001  * @class Roo.bootstrap.TabGroup
18002  * @extends Roo.bootstrap.Column
18003  * Bootstrap Column class
18004  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18005  * @cfg {Boolean} carousel true to make the group behave like a carousel
18006  * @cfg {Boolean} bullets show bullets for the panels
18007  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18008  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18009  * @cfg {Boolean} showarrow (true|false) show arrow default true
18010  * 
18011  * @constructor
18012  * Create a new TabGroup
18013  * @param {Object} config The config object
18014  */
18015
18016 Roo.bootstrap.TabGroup = function(config){
18017     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18018     if (!this.navId) {
18019         this.navId = Roo.id();
18020     }
18021     this.tabs = [];
18022     Roo.bootstrap.TabGroup.register(this);
18023     
18024 };
18025
18026 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18027     
18028     carousel : false,
18029     transition : false,
18030     bullets : 0,
18031     timer : 0,
18032     autoslide : false,
18033     slideFn : false,
18034     slideOnTouch : false,
18035     showarrow : true,
18036     
18037     getAutoCreate : function()
18038     {
18039         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18040         
18041         cfg.cls += ' tab-content';
18042         
18043         if (this.carousel) {
18044             cfg.cls += ' carousel slide';
18045             
18046             cfg.cn = [{
18047                cls : 'carousel-inner',
18048                cn : []
18049             }];
18050         
18051             if(this.bullets  && !Roo.isTouch){
18052                 
18053                 var bullets = {
18054                     cls : 'carousel-bullets',
18055                     cn : []
18056                 };
18057                
18058                 if(this.bullets_cls){
18059                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18060                 }
18061                 
18062                 bullets.cn.push({
18063                     cls : 'clear'
18064                 });
18065                 
18066                 cfg.cn[0].cn.push(bullets);
18067             }
18068             
18069             if(this.showarrow){
18070                 cfg.cn[0].cn.push({
18071                     tag : 'div',
18072                     class : 'carousel-arrow',
18073                     cn : [
18074                         {
18075                             tag : 'div',
18076                             class : 'carousel-prev',
18077                             cn : [
18078                                 {
18079                                     tag : 'i',
18080                                     class : 'fa fa-chevron-left'
18081                                 }
18082                             ]
18083                         },
18084                         {
18085                             tag : 'div',
18086                             class : 'carousel-next',
18087                             cn : [
18088                                 {
18089                                     tag : 'i',
18090                                     class : 'fa fa-chevron-right'
18091                                 }
18092                             ]
18093                         }
18094                     ]
18095                 });
18096             }
18097             
18098         }
18099         
18100         return cfg;
18101     },
18102     
18103     initEvents:  function()
18104     {
18105 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18106 //            this.el.on("touchstart", this.onTouchStart, this);
18107 //        }
18108         
18109         if(this.autoslide){
18110             var _this = this;
18111             
18112             this.slideFn = window.setInterval(function() {
18113                 _this.showPanelNext();
18114             }, this.timer);
18115         }
18116         
18117         if(this.showarrow){
18118             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18119             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18120         }
18121         
18122         
18123     },
18124     
18125 //    onTouchStart : function(e, el, o)
18126 //    {
18127 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18128 //            return;
18129 //        }
18130 //        
18131 //        this.showPanelNext();
18132 //    },
18133     
18134     
18135     getChildContainer : function()
18136     {
18137         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18138     },
18139     
18140     /**
18141     * register a Navigation item
18142     * @param {Roo.bootstrap.NavItem} the navitem to add
18143     */
18144     register : function(item)
18145     {
18146         this.tabs.push( item);
18147         item.navId = this.navId; // not really needed..
18148         this.addBullet();
18149     
18150     },
18151     
18152     getActivePanel : function()
18153     {
18154         var r = false;
18155         Roo.each(this.tabs, function(t) {
18156             if (t.active) {
18157                 r = t;
18158                 return false;
18159             }
18160             return null;
18161         });
18162         return r;
18163         
18164     },
18165     getPanelByName : function(n)
18166     {
18167         var r = false;
18168         Roo.each(this.tabs, function(t) {
18169             if (t.tabId == n) {
18170                 r = t;
18171                 return false;
18172             }
18173             return null;
18174         });
18175         return r;
18176     },
18177     indexOfPanel : function(p)
18178     {
18179         var r = false;
18180         Roo.each(this.tabs, function(t,i) {
18181             if (t.tabId == p.tabId) {
18182                 r = i;
18183                 return false;
18184             }
18185             return null;
18186         });
18187         return r;
18188     },
18189     /**
18190      * show a specific panel
18191      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18192      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18193      */
18194     showPanel : function (pan)
18195     {
18196         if(this.transition || typeof(pan) == 'undefined'){
18197             Roo.log("waiting for the transitionend");
18198             return;
18199         }
18200         
18201         if (typeof(pan) == 'number') {
18202             pan = this.tabs[pan];
18203         }
18204         
18205         if (typeof(pan) == 'string') {
18206             pan = this.getPanelByName(pan);
18207         }
18208         
18209         var cur = this.getActivePanel();
18210         
18211         if(!pan || !cur){
18212             Roo.log('pan or acitve pan is undefined');
18213             return false;
18214         }
18215         
18216         if (pan.tabId == this.getActivePanel().tabId) {
18217             return true;
18218         }
18219         
18220         if (false === cur.fireEvent('beforedeactivate')) {
18221             return false;
18222         }
18223         
18224         if(this.bullets > 0 && !Roo.isTouch){
18225             this.setActiveBullet(this.indexOfPanel(pan));
18226         }
18227         
18228         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18229             
18230             this.transition = true;
18231             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18232             var lr = dir == 'next' ? 'left' : 'right';
18233             pan.el.addClass(dir); // or prev
18234             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18235             cur.el.addClass(lr); // or right
18236             pan.el.addClass(lr);
18237             
18238             var _this = this;
18239             cur.el.on('transitionend', function() {
18240                 Roo.log("trans end?");
18241                 
18242                 pan.el.removeClass([lr,dir]);
18243                 pan.setActive(true);
18244                 
18245                 cur.el.removeClass([lr]);
18246                 cur.setActive(false);
18247                 
18248                 _this.transition = false;
18249                 
18250             }, this, { single:  true } );
18251             
18252             return true;
18253         }
18254         
18255         cur.setActive(false);
18256         pan.setActive(true);
18257         
18258         return true;
18259         
18260     },
18261     showPanelNext : function()
18262     {
18263         var i = this.indexOfPanel(this.getActivePanel());
18264         
18265         if (i >= this.tabs.length - 1 && !this.autoslide) {
18266             return;
18267         }
18268         
18269         if (i >= this.tabs.length - 1 && this.autoslide) {
18270             i = -1;
18271         }
18272         
18273         this.showPanel(this.tabs[i+1]);
18274     },
18275     
18276     showPanelPrev : function()
18277     {
18278         var i = this.indexOfPanel(this.getActivePanel());
18279         
18280         if (i  < 1 && !this.autoslide) {
18281             return;
18282         }
18283         
18284         if (i < 1 && this.autoslide) {
18285             i = this.tabs.length;
18286         }
18287         
18288         this.showPanel(this.tabs[i-1]);
18289     },
18290     
18291     
18292     addBullet: function()
18293     {
18294         if(!this.bullets || Roo.isTouch){
18295             return;
18296         }
18297         var ctr = this.el.select('.carousel-bullets',true).first();
18298         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18299         var bullet = ctr.createChild({
18300             cls : 'bullet bullet-' + i
18301         },ctr.dom.lastChild);
18302         
18303         
18304         var _this = this;
18305         
18306         bullet.on('click', (function(e, el, o, ii, t){
18307
18308             e.preventDefault();
18309
18310             this.showPanel(ii);
18311
18312             if(this.autoslide && this.slideFn){
18313                 clearInterval(this.slideFn);
18314                 this.slideFn = window.setInterval(function() {
18315                     _this.showPanelNext();
18316                 }, this.timer);
18317             }
18318
18319         }).createDelegate(this, [i, bullet], true));
18320                 
18321         
18322     },
18323      
18324     setActiveBullet : function(i)
18325     {
18326         if(Roo.isTouch){
18327             return;
18328         }
18329         
18330         Roo.each(this.el.select('.bullet', true).elements, function(el){
18331             el.removeClass('selected');
18332         });
18333
18334         var bullet = this.el.select('.bullet-' + i, true).first();
18335         
18336         if(!bullet){
18337             return;
18338         }
18339         
18340         bullet.addClass('selected');
18341     }
18342     
18343     
18344   
18345 });
18346
18347  
18348
18349  
18350  
18351 Roo.apply(Roo.bootstrap.TabGroup, {
18352     
18353     groups: {},
18354      /**
18355     * register a Navigation Group
18356     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18357     */
18358     register : function(navgrp)
18359     {
18360         this.groups[navgrp.navId] = navgrp;
18361         
18362     },
18363     /**
18364     * fetch a Navigation Group based on the navigation ID
18365     * if one does not exist , it will get created.
18366     * @param {string} the navgroup to add
18367     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18368     */
18369     get: function(navId) {
18370         if (typeof(this.groups[navId]) == 'undefined') {
18371             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18372         }
18373         return this.groups[navId] ;
18374     }
18375     
18376     
18377     
18378 });
18379
18380  /*
18381  * - LGPL
18382  *
18383  * TabPanel
18384  * 
18385  */
18386
18387 /**
18388  * @class Roo.bootstrap.TabPanel
18389  * @extends Roo.bootstrap.Component
18390  * Bootstrap TabPanel class
18391  * @cfg {Boolean} active panel active
18392  * @cfg {String} html panel content
18393  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18394  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18395  * @cfg {String} href click to link..
18396  * 
18397  * 
18398  * @constructor
18399  * Create a new TabPanel
18400  * @param {Object} config The config object
18401  */
18402
18403 Roo.bootstrap.TabPanel = function(config){
18404     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18405     this.addEvents({
18406         /**
18407              * @event changed
18408              * Fires when the active status changes
18409              * @param {Roo.bootstrap.TabPanel} this
18410              * @param {Boolean} state the new state
18411             
18412          */
18413         'changed': true,
18414         /**
18415              * @event beforedeactivate
18416              * Fires before a tab is de-activated - can be used to do validation on a form.
18417              * @param {Roo.bootstrap.TabPanel} this
18418              * @return {Boolean} false if there is an error
18419             
18420          */
18421         'beforedeactivate': true
18422      });
18423     
18424     this.tabId = this.tabId || Roo.id();
18425   
18426 };
18427
18428 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18429     
18430     active: false,
18431     html: false,
18432     tabId: false,
18433     navId : false,
18434     href : '',
18435     
18436     getAutoCreate : function(){
18437         var cfg = {
18438             tag: 'div',
18439             // item is needed for carousel - not sure if it has any effect otherwise
18440             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18441             html: this.html || ''
18442         };
18443         
18444         if(this.active){
18445             cfg.cls += ' active';
18446         }
18447         
18448         if(this.tabId){
18449             cfg.tabId = this.tabId;
18450         }
18451         
18452         
18453         return cfg;
18454     },
18455     
18456     initEvents:  function()
18457     {
18458         var p = this.parent();
18459         
18460         this.navId = this.navId || p.navId;
18461         
18462         if (typeof(this.navId) != 'undefined') {
18463             // not really needed.. but just in case.. parent should be a NavGroup.
18464             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18465             
18466             tg.register(this);
18467             
18468             var i = tg.tabs.length - 1;
18469             
18470             if(this.active && tg.bullets > 0 && i < tg.bullets){
18471                 tg.setActiveBullet(i);
18472             }
18473         }
18474         
18475         this.el.on('click', this.onClick, this);
18476         
18477         if(Roo.isTouch){
18478             this.el.on("touchstart", this.onTouchStart, this);
18479             this.el.on("touchmove", this.onTouchMove, this);
18480             this.el.on("touchend", this.onTouchEnd, this);
18481         }
18482         
18483     },
18484     
18485     onRender : function(ct, position)
18486     {
18487         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18488     },
18489     
18490     setActive : function(state)
18491     {
18492         Roo.log("panel - set active " + this.tabId + "=" + state);
18493         
18494         this.active = state;
18495         if (!state) {
18496             this.el.removeClass('active');
18497             
18498         } else  if (!this.el.hasClass('active')) {
18499             this.el.addClass('active');
18500         }
18501         
18502         this.fireEvent('changed', this, state);
18503     },
18504     
18505     onClick : function(e)
18506     {
18507         e.preventDefault();
18508         
18509         if(!this.href.length){
18510             return;
18511         }
18512         
18513         window.location.href = this.href;
18514     },
18515     
18516     startX : 0,
18517     startY : 0,
18518     endX : 0,
18519     endY : 0,
18520     swiping : false,
18521     
18522     onTouchStart : function(e)
18523     {
18524         this.swiping = false;
18525         
18526         this.startX = e.browserEvent.touches[0].clientX;
18527         this.startY = e.browserEvent.touches[0].clientY;
18528     },
18529     
18530     onTouchMove : function(e)
18531     {
18532         this.swiping = true;
18533         
18534         this.endX = e.browserEvent.touches[0].clientX;
18535         this.endY = e.browserEvent.touches[0].clientY;
18536     },
18537     
18538     onTouchEnd : function(e)
18539     {
18540         if(!this.swiping){
18541             this.onClick(e);
18542             return;
18543         }
18544         
18545         var tabGroup = this.parent();
18546         
18547         if(this.endX > this.startX){ // swiping right
18548             tabGroup.showPanelPrev();
18549             return;
18550         }
18551         
18552         if(this.startX > this.endX){ // swiping left
18553             tabGroup.showPanelNext();
18554             return;
18555         }
18556     }
18557     
18558     
18559 });
18560  
18561
18562  
18563
18564  /*
18565  * - LGPL
18566  *
18567  * DateField
18568  * 
18569  */
18570
18571 /**
18572  * @class Roo.bootstrap.DateField
18573  * @extends Roo.bootstrap.Input
18574  * Bootstrap DateField class
18575  * @cfg {Number} weekStart default 0
18576  * @cfg {String} viewMode default empty, (months|years)
18577  * @cfg {String} minViewMode default empty, (months|years)
18578  * @cfg {Number} startDate default -Infinity
18579  * @cfg {Number} endDate default Infinity
18580  * @cfg {Boolean} todayHighlight default false
18581  * @cfg {Boolean} todayBtn default false
18582  * @cfg {Boolean} calendarWeeks default false
18583  * @cfg {Object} daysOfWeekDisabled default empty
18584  * @cfg {Boolean} singleMode default false (true | false)
18585  * 
18586  * @cfg {Boolean} keyboardNavigation default true
18587  * @cfg {String} language default en
18588  * 
18589  * @constructor
18590  * Create a new DateField
18591  * @param {Object} config The config object
18592  */
18593
18594 Roo.bootstrap.DateField = function(config){
18595     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18596      this.addEvents({
18597             /**
18598              * @event show
18599              * Fires when this field show.
18600              * @param {Roo.bootstrap.DateField} this
18601              * @param {Mixed} date The date value
18602              */
18603             show : true,
18604             /**
18605              * @event show
18606              * Fires when this field hide.
18607              * @param {Roo.bootstrap.DateField} this
18608              * @param {Mixed} date The date value
18609              */
18610             hide : true,
18611             /**
18612              * @event select
18613              * Fires when select a date.
18614              * @param {Roo.bootstrap.DateField} this
18615              * @param {Mixed} date The date value
18616              */
18617             select : true,
18618             /**
18619              * @event beforeselect
18620              * Fires when before select a date.
18621              * @param {Roo.bootstrap.DateField} this
18622              * @param {Mixed} date The date value
18623              */
18624             beforeselect : true
18625         });
18626 };
18627
18628 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18629     
18630     /**
18631      * @cfg {String} format
18632      * The default date format string which can be overriden for localization support.  The format must be
18633      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18634      */
18635     format : "m/d/y",
18636     /**
18637      * @cfg {String} altFormats
18638      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18639      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18640      */
18641     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18642     
18643     weekStart : 0,
18644     
18645     viewMode : '',
18646     
18647     minViewMode : '',
18648     
18649     todayHighlight : false,
18650     
18651     todayBtn: false,
18652     
18653     language: 'en',
18654     
18655     keyboardNavigation: true,
18656     
18657     calendarWeeks: false,
18658     
18659     startDate: -Infinity,
18660     
18661     endDate: Infinity,
18662     
18663     daysOfWeekDisabled: [],
18664     
18665     _events: [],
18666     
18667     singleMode : false,
18668     
18669     UTCDate: function()
18670     {
18671         return new Date(Date.UTC.apply(Date, arguments));
18672     },
18673     
18674     UTCToday: function()
18675     {
18676         var today = new Date();
18677         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18678     },
18679     
18680     getDate: function() {
18681             var d = this.getUTCDate();
18682             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18683     },
18684     
18685     getUTCDate: function() {
18686             return this.date;
18687     },
18688     
18689     setDate: function(d) {
18690             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18691     },
18692     
18693     setUTCDate: function(d) {
18694             this.date = d;
18695             this.setValue(this.formatDate(this.date));
18696     },
18697         
18698     onRender: function(ct, position)
18699     {
18700         
18701         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18702         
18703         this.language = this.language || 'en';
18704         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18705         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18706         
18707         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18708         this.format = this.format || 'm/d/y';
18709         this.isInline = false;
18710         this.isInput = true;
18711         this.component = this.el.select('.add-on', true).first() || false;
18712         this.component = (this.component && this.component.length === 0) ? false : this.component;
18713         this.hasInput = this.component && this.inputEl().length;
18714         
18715         if (typeof(this.minViewMode === 'string')) {
18716             switch (this.minViewMode) {
18717                 case 'months':
18718                     this.minViewMode = 1;
18719                     break;
18720                 case 'years':
18721                     this.minViewMode = 2;
18722                     break;
18723                 default:
18724                     this.minViewMode = 0;
18725                     break;
18726             }
18727         }
18728         
18729         if (typeof(this.viewMode === 'string')) {
18730             switch (this.viewMode) {
18731                 case 'months':
18732                     this.viewMode = 1;
18733                     break;
18734                 case 'years':
18735                     this.viewMode = 2;
18736                     break;
18737                 default:
18738                     this.viewMode = 0;
18739                     break;
18740             }
18741         }
18742                 
18743         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18744         
18745 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18746         
18747         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18748         
18749         this.picker().on('mousedown', this.onMousedown, this);
18750         this.picker().on('click', this.onClick, this);
18751         
18752         this.picker().addClass('datepicker-dropdown');
18753         
18754         this.startViewMode = this.viewMode;
18755         
18756         if(this.singleMode){
18757             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18758                 v.setVisibilityMode(Roo.Element.DISPLAY);
18759                 v.hide();
18760             });
18761             
18762             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18763                 v.setStyle('width', '189px');
18764             });
18765         }
18766         
18767         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18768             if(!this.calendarWeeks){
18769                 v.remove();
18770                 return;
18771             }
18772             
18773             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18774             v.attr('colspan', function(i, val){
18775                 return parseInt(val) + 1;
18776             });
18777         });
18778                         
18779         
18780         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18781         
18782         this.setStartDate(this.startDate);
18783         this.setEndDate(this.endDate);
18784         
18785         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18786         
18787         this.fillDow();
18788         this.fillMonths();
18789         this.update();
18790         this.showMode();
18791         
18792         if(this.isInline) {
18793             this.showPopup();
18794         }
18795     },
18796     
18797     picker : function()
18798     {
18799         return this.pickerEl;
18800 //        return this.el.select('.datepicker', true).first();
18801     },
18802     
18803     fillDow: function()
18804     {
18805         var dowCnt = this.weekStart;
18806         
18807         var dow = {
18808             tag: 'tr',
18809             cn: [
18810                 
18811             ]
18812         };
18813         
18814         if(this.calendarWeeks){
18815             dow.cn.push({
18816                 tag: 'th',
18817                 cls: 'cw',
18818                 html: '&nbsp;'
18819             })
18820         }
18821         
18822         while (dowCnt < this.weekStart + 7) {
18823             dow.cn.push({
18824                 tag: 'th',
18825                 cls: 'dow',
18826                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18827             });
18828         }
18829         
18830         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18831     },
18832     
18833     fillMonths: function()
18834     {    
18835         var i = 0;
18836         var months = this.picker().select('>.datepicker-months td', true).first();
18837         
18838         months.dom.innerHTML = '';
18839         
18840         while (i < 12) {
18841             var month = {
18842                 tag: 'span',
18843                 cls: 'month',
18844                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18845             };
18846             
18847             months.createChild(month);
18848         }
18849         
18850     },
18851     
18852     update: function()
18853     {
18854         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;
18855         
18856         if (this.date < this.startDate) {
18857             this.viewDate = new Date(this.startDate);
18858         } else if (this.date > this.endDate) {
18859             this.viewDate = new Date(this.endDate);
18860         } else {
18861             this.viewDate = new Date(this.date);
18862         }
18863         
18864         this.fill();
18865     },
18866     
18867     fill: function() 
18868     {
18869         var d = new Date(this.viewDate),
18870                 year = d.getUTCFullYear(),
18871                 month = d.getUTCMonth(),
18872                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18873                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18874                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18875                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18876                 currentDate = this.date && this.date.valueOf(),
18877                 today = this.UTCToday();
18878         
18879         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18880         
18881 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18882         
18883 //        this.picker.select('>tfoot th.today').
18884 //                                              .text(dates[this.language].today)
18885 //                                              .toggle(this.todayBtn !== false);
18886     
18887         this.updateNavArrows();
18888         this.fillMonths();
18889                                                 
18890         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18891         
18892         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18893          
18894         prevMonth.setUTCDate(day);
18895         
18896         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18897         
18898         var nextMonth = new Date(prevMonth);
18899         
18900         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18901         
18902         nextMonth = nextMonth.valueOf();
18903         
18904         var fillMonths = false;
18905         
18906         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18907         
18908         while(prevMonth.valueOf() <= nextMonth) {
18909             var clsName = '';
18910             
18911             if (prevMonth.getUTCDay() === this.weekStart) {
18912                 if(fillMonths){
18913                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18914                 }
18915                     
18916                 fillMonths = {
18917                     tag: 'tr',
18918                     cn: []
18919                 };
18920                 
18921                 if(this.calendarWeeks){
18922                     // ISO 8601: First week contains first thursday.
18923                     // ISO also states week starts on Monday, but we can be more abstract here.
18924                     var
18925                     // Start of current week: based on weekstart/current date
18926                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18927                     // Thursday of this week
18928                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18929                     // First Thursday of year, year from thursday
18930                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18931                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18932                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18933                     
18934                     fillMonths.cn.push({
18935                         tag: 'td',
18936                         cls: 'cw',
18937                         html: calWeek
18938                     });
18939                 }
18940             }
18941             
18942             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18943                 clsName += ' old';
18944             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18945                 clsName += ' new';
18946             }
18947             if (this.todayHighlight &&
18948                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18949                 prevMonth.getUTCMonth() == today.getMonth() &&
18950                 prevMonth.getUTCDate() == today.getDate()) {
18951                 clsName += ' today';
18952             }
18953             
18954             if (currentDate && prevMonth.valueOf() === currentDate) {
18955                 clsName += ' active';
18956             }
18957             
18958             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18959                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18960                     clsName += ' disabled';
18961             }
18962             
18963             fillMonths.cn.push({
18964                 tag: 'td',
18965                 cls: 'day ' + clsName,
18966                 html: prevMonth.getDate()
18967             });
18968             
18969             prevMonth.setDate(prevMonth.getDate()+1);
18970         }
18971           
18972         var currentYear = this.date && this.date.getUTCFullYear();
18973         var currentMonth = this.date && this.date.getUTCMonth();
18974         
18975         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18976         
18977         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18978             v.removeClass('active');
18979             
18980             if(currentYear === year && k === currentMonth){
18981                 v.addClass('active');
18982             }
18983             
18984             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18985                 v.addClass('disabled');
18986             }
18987             
18988         });
18989         
18990         
18991         year = parseInt(year/10, 10) * 10;
18992         
18993         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18994         
18995         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18996         
18997         year -= 1;
18998         for (var i = -1; i < 11; i++) {
18999             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19000                 tag: 'span',
19001                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19002                 html: year
19003             });
19004             
19005             year += 1;
19006         }
19007     },
19008     
19009     showMode: function(dir) 
19010     {
19011         if (dir) {
19012             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19013         }
19014         
19015         Roo.each(this.picker().select('>div',true).elements, function(v){
19016             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19017             v.hide();
19018         });
19019         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19020     },
19021     
19022     place: function()
19023     {
19024         if(this.isInline) {
19025             return;
19026         }
19027         
19028         this.picker().removeClass(['bottom', 'top']);
19029         
19030         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19031             /*
19032              * place to the top of element!
19033              *
19034              */
19035             
19036             this.picker().addClass('top');
19037             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19038             
19039             return;
19040         }
19041         
19042         this.picker().addClass('bottom');
19043         
19044         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19045     },
19046     
19047     parseDate : function(value)
19048     {
19049         if(!value || value instanceof Date){
19050             return value;
19051         }
19052         var v = Date.parseDate(value, this.format);
19053         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19054             v = Date.parseDate(value, 'Y-m-d');
19055         }
19056         if(!v && this.altFormats){
19057             if(!this.altFormatsArray){
19058                 this.altFormatsArray = this.altFormats.split("|");
19059             }
19060             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19061                 v = Date.parseDate(value, this.altFormatsArray[i]);
19062             }
19063         }
19064         return v;
19065     },
19066     
19067     formatDate : function(date, fmt)
19068     {   
19069         return (!date || !(date instanceof Date)) ?
19070         date : date.dateFormat(fmt || this.format);
19071     },
19072     
19073     onFocus : function()
19074     {
19075         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19076         this.showPopup();
19077     },
19078     
19079     onBlur : function()
19080     {
19081         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19082         
19083         var d = this.inputEl().getValue();
19084         
19085         this.setValue(d);
19086                 
19087         this.hidePopup();
19088     },
19089     
19090     showPopup : function()
19091     {
19092         this.picker().show();
19093         this.update();
19094         this.place();
19095         
19096         this.fireEvent('showpopup', this, this.date);
19097     },
19098     
19099     hidePopup : function()
19100     {
19101         if(this.isInline) {
19102             return;
19103         }
19104         this.picker().hide();
19105         this.viewMode = this.startViewMode;
19106         this.showMode();
19107         
19108         this.fireEvent('hidepopup', this, this.date);
19109         
19110     },
19111     
19112     onMousedown: function(e)
19113     {
19114         e.stopPropagation();
19115         e.preventDefault();
19116     },
19117     
19118     keyup: function(e)
19119     {
19120         Roo.bootstrap.DateField.superclass.keyup.call(this);
19121         this.update();
19122     },
19123
19124     setValue: function(v)
19125     {
19126         if(this.fireEvent('beforeselect', this, v) !== false){
19127             var d = new Date(this.parseDate(v) ).clearTime();
19128         
19129             if(isNaN(d.getTime())){
19130                 this.date = this.viewDate = '';
19131                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19132                 return;
19133             }
19134
19135             v = this.formatDate(d);
19136
19137             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19138
19139             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19140
19141             this.update();
19142
19143             this.fireEvent('select', this, this.date);
19144         }
19145     },
19146     
19147     getValue: function()
19148     {
19149         return this.formatDate(this.date);
19150     },
19151     
19152     fireKey: function(e)
19153     {
19154         if (!this.picker().isVisible()){
19155             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19156                 this.showPopup();
19157             }
19158             return;
19159         }
19160         
19161         var dateChanged = false,
19162         dir, day, month,
19163         newDate, newViewDate;
19164         
19165         switch(e.keyCode){
19166             case 27: // escape
19167                 this.hidePopup();
19168                 e.preventDefault();
19169                 break;
19170             case 37: // left
19171             case 39: // right
19172                 if (!this.keyboardNavigation) {
19173                     break;
19174                 }
19175                 dir = e.keyCode == 37 ? -1 : 1;
19176                 
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);
19186                     newViewDate = new Date(this.viewDate);
19187                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
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 38: // up
19199             case 40: // down
19200                 if (!this.keyboardNavigation) {
19201                     break;
19202                 }
19203                 dir = e.keyCode == 38 ? -1 : 1;
19204                 if (e.ctrlKey){
19205                     newDate = this.moveYear(this.date, dir);
19206                     newViewDate = this.moveYear(this.viewDate, dir);
19207                 } else if (e.shiftKey){
19208                     newDate = this.moveMonth(this.date, dir);
19209                     newViewDate = this.moveMonth(this.viewDate, dir);
19210                 } else {
19211                     newDate = new Date(this.date);
19212                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19213                     newViewDate = new Date(this.viewDate);
19214                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19215                 }
19216                 if (this.dateWithinRange(newDate)){
19217                     this.date = newDate;
19218                     this.viewDate = newViewDate;
19219                     this.setValue(this.formatDate(this.date));
19220 //                    this.update();
19221                     e.preventDefault();
19222                     dateChanged = true;
19223                 }
19224                 break;
19225             case 13: // enter
19226                 this.setValue(this.formatDate(this.date));
19227                 this.hidePopup();
19228                 e.preventDefault();
19229                 break;
19230             case 9: // tab
19231                 this.setValue(this.formatDate(this.date));
19232                 this.hidePopup();
19233                 break;
19234             case 16: // shift
19235             case 17: // ctrl
19236             case 18: // alt
19237                 break;
19238             default :
19239                 this.hidePopup();
19240                 
19241         }
19242     },
19243     
19244     
19245     onClick: function(e) 
19246     {
19247         e.stopPropagation();
19248         e.preventDefault();
19249         
19250         var target = e.getTarget();
19251         
19252         if(target.nodeName.toLowerCase() === 'i'){
19253             target = Roo.get(target).dom.parentNode;
19254         }
19255         
19256         var nodeName = target.nodeName;
19257         var className = target.className;
19258         var html = target.innerHTML;
19259         //Roo.log(nodeName);
19260         
19261         switch(nodeName.toLowerCase()) {
19262             case 'th':
19263                 switch(className) {
19264                     case 'switch':
19265                         this.showMode(1);
19266                         break;
19267                     case 'prev':
19268                     case 'next':
19269                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19270                         switch(this.viewMode){
19271                                 case 0:
19272                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19273                                         break;
19274                                 case 1:
19275                                 case 2:
19276                                         this.viewDate = this.moveYear(this.viewDate, dir);
19277                                         break;
19278                         }
19279                         this.fill();
19280                         break;
19281                     case 'today':
19282                         var date = new Date();
19283                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19284 //                        this.fill()
19285                         this.setValue(this.formatDate(this.date));
19286                         
19287                         this.hidePopup();
19288                         break;
19289                 }
19290                 break;
19291             case 'span':
19292                 if (className.indexOf('disabled') < 0) {
19293                     this.viewDate.setUTCDate(1);
19294                     if (className.indexOf('month') > -1) {
19295                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19296                     } else {
19297                         var year = parseInt(html, 10) || 0;
19298                         this.viewDate.setUTCFullYear(year);
19299                         
19300                     }
19301                     
19302                     if(this.singleMode){
19303                         this.setValue(this.formatDate(this.viewDate));
19304                         this.hidePopup();
19305                         return;
19306                     }
19307                     
19308                     this.showMode(-1);
19309                     this.fill();
19310                 }
19311                 break;
19312                 
19313             case 'td':
19314                 //Roo.log(className);
19315                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19316                     var day = parseInt(html, 10) || 1;
19317                     var year = this.viewDate.getUTCFullYear(),
19318                         month = this.viewDate.getUTCMonth();
19319
19320                     if (className.indexOf('old') > -1) {
19321                         if(month === 0 ){
19322                             month = 11;
19323                             year -= 1;
19324                         }else{
19325                             month -= 1;
19326                         }
19327                     } else if (className.indexOf('new') > -1) {
19328                         if (month == 11) {
19329                             month = 0;
19330                             year += 1;
19331                         } else {
19332                             month += 1;
19333                         }
19334                     }
19335                     //Roo.log([year,month,day]);
19336                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19337                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19338 //                    this.fill();
19339                     //Roo.log(this.formatDate(this.date));
19340                     this.setValue(this.formatDate(this.date));
19341                     this.hidePopup();
19342                 }
19343                 break;
19344         }
19345     },
19346     
19347     setStartDate: function(startDate)
19348     {
19349         this.startDate = startDate || -Infinity;
19350         if (this.startDate !== -Infinity) {
19351             this.startDate = this.parseDate(this.startDate);
19352         }
19353         this.update();
19354         this.updateNavArrows();
19355     },
19356
19357     setEndDate: function(endDate)
19358     {
19359         this.endDate = endDate || Infinity;
19360         if (this.endDate !== Infinity) {
19361             this.endDate = this.parseDate(this.endDate);
19362         }
19363         this.update();
19364         this.updateNavArrows();
19365     },
19366     
19367     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19368     {
19369         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19370         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19371             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19372         }
19373         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19374             return parseInt(d, 10);
19375         });
19376         this.update();
19377         this.updateNavArrows();
19378     },
19379     
19380     updateNavArrows: function() 
19381     {
19382         if(this.singleMode){
19383             return;
19384         }
19385         
19386         var d = new Date(this.viewDate),
19387         year = d.getUTCFullYear(),
19388         month = d.getUTCMonth();
19389         
19390         Roo.each(this.picker().select('.prev', true).elements, function(v){
19391             v.show();
19392             switch (this.viewMode) {
19393                 case 0:
19394
19395                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19396                         v.hide();
19397                     }
19398                     break;
19399                 case 1:
19400                 case 2:
19401                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19402                         v.hide();
19403                     }
19404                     break;
19405             }
19406         });
19407         
19408         Roo.each(this.picker().select('.next', true).elements, function(v){
19409             v.show();
19410             switch (this.viewMode) {
19411                 case 0:
19412
19413                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19414                         v.hide();
19415                     }
19416                     break;
19417                 case 1:
19418                 case 2:
19419                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19420                         v.hide();
19421                     }
19422                     break;
19423             }
19424         })
19425     },
19426     
19427     moveMonth: function(date, dir)
19428     {
19429         if (!dir) {
19430             return date;
19431         }
19432         var new_date = new Date(date.valueOf()),
19433         day = new_date.getUTCDate(),
19434         month = new_date.getUTCMonth(),
19435         mag = Math.abs(dir),
19436         new_month, test;
19437         dir = dir > 0 ? 1 : -1;
19438         if (mag == 1){
19439             test = dir == -1
19440             // If going back one month, make sure month is not current month
19441             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19442             ? function(){
19443                 return new_date.getUTCMonth() == month;
19444             }
19445             // If going forward one month, make sure month is as expected
19446             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19447             : function(){
19448                 return new_date.getUTCMonth() != new_month;
19449             };
19450             new_month = month + dir;
19451             new_date.setUTCMonth(new_month);
19452             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19453             if (new_month < 0 || new_month > 11) {
19454                 new_month = (new_month + 12) % 12;
19455             }
19456         } else {
19457             // For magnitudes >1, move one month at a time...
19458             for (var i=0; i<mag; i++) {
19459                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19460                 new_date = this.moveMonth(new_date, dir);
19461             }
19462             // ...then reset the day, keeping it in the new month
19463             new_month = new_date.getUTCMonth();
19464             new_date.setUTCDate(day);
19465             test = function(){
19466                 return new_month != new_date.getUTCMonth();
19467             };
19468         }
19469         // Common date-resetting loop -- if date is beyond end of month, make it
19470         // end of month
19471         while (test()){
19472             new_date.setUTCDate(--day);
19473             new_date.setUTCMonth(new_month);
19474         }
19475         return new_date;
19476     },
19477
19478     moveYear: function(date, dir)
19479     {
19480         return this.moveMonth(date, dir*12);
19481     },
19482
19483     dateWithinRange: function(date)
19484     {
19485         return date >= this.startDate && date <= this.endDate;
19486     },
19487
19488     
19489     remove: function() 
19490     {
19491         this.picker().remove();
19492     },
19493     
19494     validateValue : function(value)
19495     {
19496         if(this.getVisibilityEl().hasClass('hidden')){
19497             return true;
19498         }
19499         
19500         if(value.length < 1)  {
19501             if(this.allowBlank){
19502                 return true;
19503             }
19504             return false;
19505         }
19506         
19507         if(value.length < this.minLength){
19508             return false;
19509         }
19510         if(value.length > this.maxLength){
19511             return false;
19512         }
19513         if(this.vtype){
19514             var vt = Roo.form.VTypes;
19515             if(!vt[this.vtype](value, this)){
19516                 return false;
19517             }
19518         }
19519         if(typeof this.validator == "function"){
19520             var msg = this.validator(value);
19521             if(msg !== true){
19522                 return false;
19523             }
19524         }
19525         
19526         if(this.regex && !this.regex.test(value)){
19527             return false;
19528         }
19529         
19530         if(typeof(this.parseDate(value)) == 'undefined'){
19531             return false;
19532         }
19533         
19534         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19535             return false;
19536         }      
19537         
19538         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19539             return false;
19540         } 
19541         
19542         
19543         return true;
19544     },
19545     
19546     reset : function()
19547     {
19548         this.date = this.viewDate = '';
19549         
19550         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19551     }
19552    
19553 });
19554
19555 Roo.apply(Roo.bootstrap.DateField,  {
19556     
19557     head : {
19558         tag: 'thead',
19559         cn: [
19560         {
19561             tag: 'tr',
19562             cn: [
19563             {
19564                 tag: 'th',
19565                 cls: 'prev',
19566                 html: '<i class="fa fa-arrow-left"/>'
19567             },
19568             {
19569                 tag: 'th',
19570                 cls: 'switch',
19571                 colspan: '5'
19572             },
19573             {
19574                 tag: 'th',
19575                 cls: 'next',
19576                 html: '<i class="fa fa-arrow-right"/>'
19577             }
19578
19579             ]
19580         }
19581         ]
19582     },
19583     
19584     content : {
19585         tag: 'tbody',
19586         cn: [
19587         {
19588             tag: 'tr',
19589             cn: [
19590             {
19591                 tag: 'td',
19592                 colspan: '7'
19593             }
19594             ]
19595         }
19596         ]
19597     },
19598     
19599     footer : {
19600         tag: 'tfoot',
19601         cn: [
19602         {
19603             tag: 'tr',
19604             cn: [
19605             {
19606                 tag: 'th',
19607                 colspan: '7',
19608                 cls: 'today'
19609             }
19610                     
19611             ]
19612         }
19613         ]
19614     },
19615     
19616     dates:{
19617         en: {
19618             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19619             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19620             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19621             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19622             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19623             today: "Today"
19624         }
19625     },
19626     
19627     modes: [
19628     {
19629         clsName: 'days',
19630         navFnc: 'Month',
19631         navStep: 1
19632     },
19633     {
19634         clsName: 'months',
19635         navFnc: 'FullYear',
19636         navStep: 1
19637     },
19638     {
19639         clsName: 'years',
19640         navFnc: 'FullYear',
19641         navStep: 10
19642     }]
19643 });
19644
19645 Roo.apply(Roo.bootstrap.DateField,  {
19646   
19647     template : {
19648         tag: 'div',
19649         cls: 'datepicker dropdown-menu roo-dynamic',
19650         cn: [
19651         {
19652             tag: 'div',
19653             cls: 'datepicker-days',
19654             cn: [
19655             {
19656                 tag: 'table',
19657                 cls: 'table-condensed',
19658                 cn:[
19659                 Roo.bootstrap.DateField.head,
19660                 {
19661                     tag: 'tbody'
19662                 },
19663                 Roo.bootstrap.DateField.footer
19664                 ]
19665             }
19666             ]
19667         },
19668         {
19669             tag: 'div',
19670             cls: 'datepicker-months',
19671             cn: [
19672             {
19673                 tag: 'table',
19674                 cls: 'table-condensed',
19675                 cn:[
19676                 Roo.bootstrap.DateField.head,
19677                 Roo.bootstrap.DateField.content,
19678                 Roo.bootstrap.DateField.footer
19679                 ]
19680             }
19681             ]
19682         },
19683         {
19684             tag: 'div',
19685             cls: 'datepicker-years',
19686             cn: [
19687             {
19688                 tag: 'table',
19689                 cls: 'table-condensed',
19690                 cn:[
19691                 Roo.bootstrap.DateField.head,
19692                 Roo.bootstrap.DateField.content,
19693                 Roo.bootstrap.DateField.footer
19694                 ]
19695             }
19696             ]
19697         }
19698         ]
19699     }
19700 });
19701
19702  
19703
19704  /*
19705  * - LGPL
19706  *
19707  * TimeField
19708  * 
19709  */
19710
19711 /**
19712  * @class Roo.bootstrap.TimeField
19713  * @extends Roo.bootstrap.Input
19714  * Bootstrap DateField class
19715  * 
19716  * 
19717  * @constructor
19718  * Create a new TimeField
19719  * @param {Object} config The config object
19720  */
19721
19722 Roo.bootstrap.TimeField = function(config){
19723     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19724     this.addEvents({
19725             /**
19726              * @event show
19727              * Fires when this field show.
19728              * @param {Roo.bootstrap.DateField} thisthis
19729              * @param {Mixed} date The date value
19730              */
19731             show : true,
19732             /**
19733              * @event show
19734              * Fires when this field hide.
19735              * @param {Roo.bootstrap.DateField} this
19736              * @param {Mixed} date The date value
19737              */
19738             hide : true,
19739             /**
19740              * @event select
19741              * Fires when select a date.
19742              * @param {Roo.bootstrap.DateField} this
19743              * @param {Mixed} date The date value
19744              */
19745             select : true
19746         });
19747 };
19748
19749 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19750     
19751     /**
19752      * @cfg {String} format
19753      * The default time format string which can be overriden for localization support.  The format must be
19754      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19755      */
19756     format : "H:i",
19757        
19758     onRender: function(ct, position)
19759     {
19760         
19761         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19762                 
19763         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19764         
19765         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19766         
19767         this.pop = this.picker().select('>.datepicker-time',true).first();
19768         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19769         
19770         this.picker().on('mousedown', this.onMousedown, this);
19771         this.picker().on('click', this.onClick, this);
19772         
19773         this.picker().addClass('datepicker-dropdown');
19774     
19775         this.fillTime();
19776         this.update();
19777             
19778         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19779         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19780         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19781         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19782         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19783         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19784
19785     },
19786     
19787     fireKey: function(e){
19788         if (!this.picker().isVisible()){
19789             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19790                 this.show();
19791             }
19792             return;
19793         }
19794
19795         e.preventDefault();
19796         
19797         switch(e.keyCode){
19798             case 27: // escape
19799                 this.hide();
19800                 break;
19801             case 37: // left
19802             case 39: // right
19803                 this.onTogglePeriod();
19804                 break;
19805             case 38: // up
19806                 this.onIncrementMinutes();
19807                 break;
19808             case 40: // down
19809                 this.onDecrementMinutes();
19810                 break;
19811             case 13: // enter
19812             case 9: // tab
19813                 this.setTime();
19814                 break;
19815         }
19816     },
19817     
19818     onClick: function(e) {
19819         e.stopPropagation();
19820         e.preventDefault();
19821     },
19822     
19823     picker : function()
19824     {
19825         return this.el.select('.datepicker', true).first();
19826     },
19827     
19828     fillTime: function()
19829     {    
19830         var time = this.pop.select('tbody', true).first();
19831         
19832         time.dom.innerHTML = '';
19833         
19834         time.createChild({
19835             tag: 'tr',
19836             cn: [
19837                 {
19838                     tag: 'td',
19839                     cn: [
19840                         {
19841                             tag: 'a',
19842                             href: '#',
19843                             cls: 'btn',
19844                             cn: [
19845                                 {
19846                                     tag: 'span',
19847                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19848                                 }
19849                             ]
19850                         } 
19851                     ]
19852                 },
19853                 {
19854                     tag: 'td',
19855                     cls: 'separator'
19856                 },
19857                 {
19858                     tag: 'td',
19859                     cn: [
19860                         {
19861                             tag: 'a',
19862                             href: '#',
19863                             cls: 'btn',
19864                             cn: [
19865                                 {
19866                                     tag: 'span',
19867                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19868                                 }
19869                             ]
19870                         }
19871                     ]
19872                 },
19873                 {
19874                     tag: 'td',
19875                     cls: 'separator'
19876                 }
19877             ]
19878         });
19879         
19880         time.createChild({
19881             tag: 'tr',
19882             cn: [
19883                 {
19884                     tag: 'td',
19885                     cn: [
19886                         {
19887                             tag: 'span',
19888                             cls: 'timepicker-hour',
19889                             html: '00'
19890                         }  
19891                     ]
19892                 },
19893                 {
19894                     tag: 'td',
19895                     cls: 'separator',
19896                     html: ':'
19897                 },
19898                 {
19899                     tag: 'td',
19900                     cn: [
19901                         {
19902                             tag: 'span',
19903                             cls: 'timepicker-minute',
19904                             html: '00'
19905                         }  
19906                     ]
19907                 },
19908                 {
19909                     tag: 'td',
19910                     cls: 'separator'
19911                 },
19912                 {
19913                     tag: 'td',
19914                     cn: [
19915                         {
19916                             tag: 'button',
19917                             type: 'button',
19918                             cls: 'btn btn-primary period',
19919                             html: 'AM'
19920                             
19921                         }
19922                     ]
19923                 }
19924             ]
19925         });
19926         
19927         time.createChild({
19928             tag: 'tr',
19929             cn: [
19930                 {
19931                     tag: 'td',
19932                     cn: [
19933                         {
19934                             tag: 'a',
19935                             href: '#',
19936                             cls: 'btn',
19937                             cn: [
19938                                 {
19939                                     tag: 'span',
19940                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19941                                 }
19942                             ]
19943                         }
19944                     ]
19945                 },
19946                 {
19947                     tag: 'td',
19948                     cls: 'separator'
19949                 },
19950                 {
19951                     tag: 'td',
19952                     cn: [
19953                         {
19954                             tag: 'a',
19955                             href: '#',
19956                             cls: 'btn',
19957                             cn: [
19958                                 {
19959                                     tag: 'span',
19960                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19961                                 }
19962                             ]
19963                         }
19964                     ]
19965                 },
19966                 {
19967                     tag: 'td',
19968                     cls: 'separator'
19969                 }
19970             ]
19971         });
19972         
19973     },
19974     
19975     update: function()
19976     {
19977         
19978         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19979         
19980         this.fill();
19981     },
19982     
19983     fill: function() 
19984     {
19985         var hours = this.time.getHours();
19986         var minutes = this.time.getMinutes();
19987         var period = 'AM';
19988         
19989         if(hours > 11){
19990             period = 'PM';
19991         }
19992         
19993         if(hours == 0){
19994             hours = 12;
19995         }
19996         
19997         
19998         if(hours > 12){
19999             hours = hours - 12;
20000         }
20001         
20002         if(hours < 10){
20003             hours = '0' + hours;
20004         }
20005         
20006         if(minutes < 10){
20007             minutes = '0' + minutes;
20008         }
20009         
20010         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20011         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20012         this.pop.select('button', true).first().dom.innerHTML = period;
20013         
20014     },
20015     
20016     place: function()
20017     {   
20018         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20019         
20020         var cls = ['bottom'];
20021         
20022         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20023             cls.pop();
20024             cls.push('top');
20025         }
20026         
20027         cls.push('right');
20028         
20029         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20030             cls.pop();
20031             cls.push('left');
20032         }
20033         
20034         this.picker().addClass(cls.join('-'));
20035         
20036         var _this = this;
20037         
20038         Roo.each(cls, function(c){
20039             if(c == 'bottom'){
20040                 _this.picker().setTop(_this.inputEl().getHeight());
20041                 return;
20042             }
20043             if(c == 'top'){
20044                 _this.picker().setTop(0 - _this.picker().getHeight());
20045                 return;
20046             }
20047             
20048             if(c == 'left'){
20049                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20050                 return;
20051             }
20052             if(c == 'right'){
20053                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20054                 return;
20055             }
20056         });
20057         
20058     },
20059   
20060     onFocus : function()
20061     {
20062         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20063         this.show();
20064     },
20065     
20066     onBlur : function()
20067     {
20068         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20069         this.hide();
20070     },
20071     
20072     show : function()
20073     {
20074         this.picker().show();
20075         this.pop.show();
20076         this.update();
20077         this.place();
20078         
20079         this.fireEvent('show', this, this.date);
20080     },
20081     
20082     hide : function()
20083     {
20084         this.picker().hide();
20085         this.pop.hide();
20086         
20087         this.fireEvent('hide', this, this.date);
20088     },
20089     
20090     setTime : function()
20091     {
20092         this.hide();
20093         this.setValue(this.time.format(this.format));
20094         
20095         this.fireEvent('select', this, this.date);
20096         
20097         
20098     },
20099     
20100     onMousedown: function(e){
20101         e.stopPropagation();
20102         e.preventDefault();
20103     },
20104     
20105     onIncrementHours: function()
20106     {
20107         Roo.log('onIncrementHours');
20108         this.time = this.time.add(Date.HOUR, 1);
20109         this.update();
20110         
20111     },
20112     
20113     onDecrementHours: function()
20114     {
20115         Roo.log('onDecrementHours');
20116         this.time = this.time.add(Date.HOUR, -1);
20117         this.update();
20118     },
20119     
20120     onIncrementMinutes: function()
20121     {
20122         Roo.log('onIncrementMinutes');
20123         this.time = this.time.add(Date.MINUTE, 1);
20124         this.update();
20125     },
20126     
20127     onDecrementMinutes: function()
20128     {
20129         Roo.log('onDecrementMinutes');
20130         this.time = this.time.add(Date.MINUTE, -1);
20131         this.update();
20132     },
20133     
20134     onTogglePeriod: function()
20135     {
20136         Roo.log('onTogglePeriod');
20137         this.time = this.time.add(Date.HOUR, 12);
20138         this.update();
20139     }
20140     
20141    
20142 });
20143
20144 Roo.apply(Roo.bootstrap.TimeField,  {
20145     
20146     content : {
20147         tag: 'tbody',
20148         cn: [
20149             {
20150                 tag: 'tr',
20151                 cn: [
20152                 {
20153                     tag: 'td',
20154                     colspan: '7'
20155                 }
20156                 ]
20157             }
20158         ]
20159     },
20160     
20161     footer : {
20162         tag: 'tfoot',
20163         cn: [
20164             {
20165                 tag: 'tr',
20166                 cn: [
20167                 {
20168                     tag: 'th',
20169                     colspan: '7',
20170                     cls: '',
20171                     cn: [
20172                         {
20173                             tag: 'button',
20174                             cls: 'btn btn-info ok',
20175                             html: 'OK'
20176                         }
20177                     ]
20178                 }
20179
20180                 ]
20181             }
20182         ]
20183     }
20184 });
20185
20186 Roo.apply(Roo.bootstrap.TimeField,  {
20187   
20188     template : {
20189         tag: 'div',
20190         cls: 'datepicker dropdown-menu',
20191         cn: [
20192             {
20193                 tag: 'div',
20194                 cls: 'datepicker-time',
20195                 cn: [
20196                 {
20197                     tag: 'table',
20198                     cls: 'table-condensed',
20199                     cn:[
20200                     Roo.bootstrap.TimeField.content,
20201                     Roo.bootstrap.TimeField.footer
20202                     ]
20203                 }
20204                 ]
20205             }
20206         ]
20207     }
20208 });
20209
20210  
20211
20212  /*
20213  * - LGPL
20214  *
20215  * MonthField
20216  * 
20217  */
20218
20219 /**
20220  * @class Roo.bootstrap.MonthField
20221  * @extends Roo.bootstrap.Input
20222  * Bootstrap MonthField class
20223  * 
20224  * @cfg {String} language default en
20225  * 
20226  * @constructor
20227  * Create a new MonthField
20228  * @param {Object} config The config object
20229  */
20230
20231 Roo.bootstrap.MonthField = function(config){
20232     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20233     
20234     this.addEvents({
20235         /**
20236          * @event show
20237          * Fires when this field show.
20238          * @param {Roo.bootstrap.MonthField} this
20239          * @param {Mixed} date The date value
20240          */
20241         show : true,
20242         /**
20243          * @event show
20244          * Fires when this field hide.
20245          * @param {Roo.bootstrap.MonthField} this
20246          * @param {Mixed} date The date value
20247          */
20248         hide : true,
20249         /**
20250          * @event select
20251          * Fires when select a date.
20252          * @param {Roo.bootstrap.MonthField} this
20253          * @param {String} oldvalue The old value
20254          * @param {String} newvalue The new value
20255          */
20256         select : true
20257     });
20258 };
20259
20260 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20261     
20262     onRender: function(ct, position)
20263     {
20264         
20265         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20266         
20267         this.language = this.language || 'en';
20268         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20269         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20270         
20271         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20272         this.isInline = false;
20273         this.isInput = true;
20274         this.component = this.el.select('.add-on', true).first() || false;
20275         this.component = (this.component && this.component.length === 0) ? false : this.component;
20276         this.hasInput = this.component && this.inputEL().length;
20277         
20278         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20279         
20280         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20281         
20282         this.picker().on('mousedown', this.onMousedown, this);
20283         this.picker().on('click', this.onClick, this);
20284         
20285         this.picker().addClass('datepicker-dropdown');
20286         
20287         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20288             v.setStyle('width', '189px');
20289         });
20290         
20291         this.fillMonths();
20292         
20293         this.update();
20294         
20295         if(this.isInline) {
20296             this.show();
20297         }
20298         
20299     },
20300     
20301     setValue: function(v, suppressEvent)
20302     {   
20303         var o = this.getValue();
20304         
20305         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20306         
20307         this.update();
20308
20309         if(suppressEvent !== true){
20310             this.fireEvent('select', this, o, v);
20311         }
20312         
20313     },
20314     
20315     getValue: function()
20316     {
20317         return this.value;
20318     },
20319     
20320     onClick: function(e) 
20321     {
20322         e.stopPropagation();
20323         e.preventDefault();
20324         
20325         var target = e.getTarget();
20326         
20327         if(target.nodeName.toLowerCase() === 'i'){
20328             target = Roo.get(target).dom.parentNode;
20329         }
20330         
20331         var nodeName = target.nodeName;
20332         var className = target.className;
20333         var html = target.innerHTML;
20334         
20335         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20336             return;
20337         }
20338         
20339         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20340         
20341         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20342         
20343         this.hide();
20344                         
20345     },
20346     
20347     picker : function()
20348     {
20349         return this.pickerEl;
20350     },
20351     
20352     fillMonths: function()
20353     {    
20354         var i = 0;
20355         var months = this.picker().select('>.datepicker-months td', true).first();
20356         
20357         months.dom.innerHTML = '';
20358         
20359         while (i < 12) {
20360             var month = {
20361                 tag: 'span',
20362                 cls: 'month',
20363                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20364             };
20365             
20366             months.createChild(month);
20367         }
20368         
20369     },
20370     
20371     update: function()
20372     {
20373         var _this = this;
20374         
20375         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20376             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20377         }
20378         
20379         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20380             e.removeClass('active');
20381             
20382             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20383                 e.addClass('active');
20384             }
20385         })
20386     },
20387     
20388     place: function()
20389     {
20390         if(this.isInline) {
20391             return;
20392         }
20393         
20394         this.picker().removeClass(['bottom', 'top']);
20395         
20396         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20397             /*
20398              * place to the top of element!
20399              *
20400              */
20401             
20402             this.picker().addClass('top');
20403             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20404             
20405             return;
20406         }
20407         
20408         this.picker().addClass('bottom');
20409         
20410         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20411     },
20412     
20413     onFocus : function()
20414     {
20415         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20416         this.show();
20417     },
20418     
20419     onBlur : function()
20420     {
20421         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20422         
20423         var d = this.inputEl().getValue();
20424         
20425         this.setValue(d);
20426                 
20427         this.hide();
20428     },
20429     
20430     show : function()
20431     {
20432         this.picker().show();
20433         this.picker().select('>.datepicker-months', true).first().show();
20434         this.update();
20435         this.place();
20436         
20437         this.fireEvent('show', this, this.date);
20438     },
20439     
20440     hide : function()
20441     {
20442         if(this.isInline) {
20443             return;
20444         }
20445         this.picker().hide();
20446         this.fireEvent('hide', this, this.date);
20447         
20448     },
20449     
20450     onMousedown: function(e)
20451     {
20452         e.stopPropagation();
20453         e.preventDefault();
20454     },
20455     
20456     keyup: function(e)
20457     {
20458         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20459         this.update();
20460     },
20461
20462     fireKey: function(e)
20463     {
20464         if (!this.picker().isVisible()){
20465             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20466                 this.show();
20467             }
20468             return;
20469         }
20470         
20471         var dir;
20472         
20473         switch(e.keyCode){
20474             case 27: // escape
20475                 this.hide();
20476                 e.preventDefault();
20477                 break;
20478             case 37: // left
20479             case 39: // right
20480                 dir = e.keyCode == 37 ? -1 : 1;
20481                 
20482                 this.vIndex = this.vIndex + dir;
20483                 
20484                 if(this.vIndex < 0){
20485                     this.vIndex = 0;
20486                 }
20487                 
20488                 if(this.vIndex > 11){
20489                     this.vIndex = 11;
20490                 }
20491                 
20492                 if(isNaN(this.vIndex)){
20493                     this.vIndex = 0;
20494                 }
20495                 
20496                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20497                 
20498                 break;
20499             case 38: // up
20500             case 40: // down
20501                 
20502                 dir = e.keyCode == 38 ? -1 : 1;
20503                 
20504                 this.vIndex = this.vIndex + dir * 4;
20505                 
20506                 if(this.vIndex < 0){
20507                     this.vIndex = 0;
20508                 }
20509                 
20510                 if(this.vIndex > 11){
20511                     this.vIndex = 11;
20512                 }
20513                 
20514                 if(isNaN(this.vIndex)){
20515                     this.vIndex = 0;
20516                 }
20517                 
20518                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20519                 break;
20520                 
20521             case 13: // enter
20522                 
20523                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20524                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20525                 }
20526                 
20527                 this.hide();
20528                 e.preventDefault();
20529                 break;
20530             case 9: // tab
20531                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20532                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20533                 }
20534                 this.hide();
20535                 break;
20536             case 16: // shift
20537             case 17: // ctrl
20538             case 18: // alt
20539                 break;
20540             default :
20541                 this.hide();
20542                 
20543         }
20544     },
20545     
20546     remove: function() 
20547     {
20548         this.picker().remove();
20549     }
20550    
20551 });
20552
20553 Roo.apply(Roo.bootstrap.MonthField,  {
20554     
20555     content : {
20556         tag: 'tbody',
20557         cn: [
20558         {
20559             tag: 'tr',
20560             cn: [
20561             {
20562                 tag: 'td',
20563                 colspan: '7'
20564             }
20565             ]
20566         }
20567         ]
20568     },
20569     
20570     dates:{
20571         en: {
20572             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20573             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20574         }
20575     }
20576 });
20577
20578 Roo.apply(Roo.bootstrap.MonthField,  {
20579   
20580     template : {
20581         tag: 'div',
20582         cls: 'datepicker dropdown-menu roo-dynamic',
20583         cn: [
20584             {
20585                 tag: 'div',
20586                 cls: 'datepicker-months',
20587                 cn: [
20588                 {
20589                     tag: 'table',
20590                     cls: 'table-condensed',
20591                     cn:[
20592                         Roo.bootstrap.DateField.content
20593                     ]
20594                 }
20595                 ]
20596             }
20597         ]
20598     }
20599 });
20600
20601  
20602
20603  
20604  /*
20605  * - LGPL
20606  *
20607  * CheckBox
20608  * 
20609  */
20610
20611 /**
20612  * @class Roo.bootstrap.CheckBox
20613  * @extends Roo.bootstrap.Input
20614  * Bootstrap CheckBox class
20615  * 
20616  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20617  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20618  * @cfg {String} boxLabel The text that appears beside the checkbox
20619  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20620  * @cfg {Boolean} checked initnal the element
20621  * @cfg {Boolean} inline inline the element (default false)
20622  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20623  * @cfg {String} tooltip label tooltip
20624  * 
20625  * @constructor
20626  * Create a new CheckBox
20627  * @param {Object} config The config object
20628  */
20629
20630 Roo.bootstrap.CheckBox = function(config){
20631     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20632    
20633     this.addEvents({
20634         /**
20635         * @event check
20636         * Fires when the element is checked or unchecked.
20637         * @param {Roo.bootstrap.CheckBox} this This input
20638         * @param {Boolean} checked The new checked value
20639         */
20640        check : true,
20641        /**
20642         * @event click
20643         * Fires when the element is click.
20644         * @param {Roo.bootstrap.CheckBox} this This input
20645         */
20646        click : true
20647     });
20648     
20649 };
20650
20651 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20652   
20653     inputType: 'checkbox',
20654     inputValue: 1,
20655     valueOff: 0,
20656     boxLabel: false,
20657     checked: false,
20658     weight : false,
20659     inline: false,
20660     tooltip : '',
20661     
20662     getAutoCreate : function()
20663     {
20664         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20665         
20666         var id = Roo.id();
20667         
20668         var cfg = {};
20669         
20670         cfg.cls = 'form-group ' + this.inputType; //input-group
20671         
20672         if(this.inline){
20673             cfg.cls += ' ' + this.inputType + '-inline';
20674         }
20675         
20676         var input =  {
20677             tag: 'input',
20678             id : id,
20679             type : this.inputType,
20680             value : this.inputValue,
20681             cls : 'roo-' + this.inputType, //'form-box',
20682             placeholder : this.placeholder || ''
20683             
20684         };
20685         
20686         if(this.inputType != 'radio'){
20687             var hidden =  {
20688                 tag: 'input',
20689                 type : 'hidden',
20690                 cls : 'roo-hidden-value',
20691                 value : this.checked ? this.inputValue : this.valueOff
20692             };
20693         }
20694         
20695             
20696         if (this.weight) { // Validity check?
20697             cfg.cls += " " + this.inputType + "-" + this.weight;
20698         }
20699         
20700         if (this.disabled) {
20701             input.disabled=true;
20702         }
20703         
20704         if(this.checked){
20705             input.checked = this.checked;
20706         }
20707         
20708         if (this.name) {
20709             
20710             input.name = this.name;
20711             
20712             if(this.inputType != 'radio'){
20713                 hidden.name = this.name;
20714                 input.name = '_hidden_' + this.name;
20715             }
20716         }
20717         
20718         if (this.size) {
20719             input.cls += ' input-' + this.size;
20720         }
20721         
20722         var settings=this;
20723         
20724         ['xs','sm','md','lg'].map(function(size){
20725             if (settings[size]) {
20726                 cfg.cls += ' col-' + size + '-' + settings[size];
20727             }
20728         });
20729         
20730         var inputblock = input;
20731          
20732         if (this.before || this.after) {
20733             
20734             inputblock = {
20735                 cls : 'input-group',
20736                 cn :  [] 
20737             };
20738             
20739             if (this.before) {
20740                 inputblock.cn.push({
20741                     tag :'span',
20742                     cls : 'input-group-addon',
20743                     html : this.before
20744                 });
20745             }
20746             
20747             inputblock.cn.push(input);
20748             
20749             if(this.inputType != 'radio'){
20750                 inputblock.cn.push(hidden);
20751             }
20752             
20753             if (this.after) {
20754                 inputblock.cn.push({
20755                     tag :'span',
20756                     cls : 'input-group-addon',
20757                     html : this.after
20758                 });
20759             }
20760             
20761         }
20762         
20763         if (align ==='left' && this.fieldLabel.length) {
20764 //                Roo.log("left and has label");
20765             cfg.cn = [
20766                 {
20767                     tag: 'label',
20768                     'for' :  id,
20769                     cls : 'control-label',
20770                     html : this.fieldLabel
20771                 },
20772                 {
20773                     cls : "", 
20774                     cn: [
20775                         inputblock
20776                     ]
20777                 }
20778             ];
20779             
20780             if(this.labelWidth > 12){
20781                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20782             }
20783             
20784             if(this.labelWidth < 13 && this.labelmd == 0){
20785                 this.labelmd = this.labelWidth;
20786             }
20787             
20788             if(this.labellg > 0){
20789                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20790                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20791             }
20792             
20793             if(this.labelmd > 0){
20794                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20795                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20796             }
20797             
20798             if(this.labelsm > 0){
20799                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20800                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20801             }
20802             
20803             if(this.labelxs > 0){
20804                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20805                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20806             }
20807             
20808         } else if ( this.fieldLabel.length) {
20809 //                Roo.log(" label");
20810                 cfg.cn = [
20811                    
20812                     {
20813                         tag: this.boxLabel ? 'span' : 'label',
20814                         'for': id,
20815                         cls: 'control-label box-input-label',
20816                         //cls : 'input-group-addon',
20817                         html : this.fieldLabel
20818                     },
20819                     
20820                     inputblock
20821                     
20822                 ];
20823
20824         } else {
20825             
20826 //                Roo.log(" no label && no align");
20827                 cfg.cn = [  inputblock ] ;
20828                 
20829                 
20830         }
20831         
20832         if(this.boxLabel){
20833              var boxLabelCfg = {
20834                 tag: 'label',
20835                 //'for': id, // box label is handled by onclick - so no for...
20836                 cls: 'box-label',
20837                 html: this.boxLabel
20838             };
20839             
20840             if(this.tooltip){
20841                 boxLabelCfg.tooltip = this.tooltip;
20842             }
20843              
20844             cfg.cn.push(boxLabelCfg);
20845         }
20846         
20847         if(this.inputType != 'radio'){
20848             cfg.cn.push(hidden);
20849         }
20850         
20851         return cfg;
20852         
20853     },
20854     
20855     /**
20856      * return the real input element.
20857      */
20858     inputEl: function ()
20859     {
20860         return this.el.select('input.roo-' + this.inputType,true).first();
20861     },
20862     hiddenEl: function ()
20863     {
20864         return this.el.select('input.roo-hidden-value',true).first();
20865     },
20866     
20867     labelEl: function()
20868     {
20869         return this.el.select('label.control-label',true).first();
20870     },
20871     /* depricated... */
20872     
20873     label: function()
20874     {
20875         return this.labelEl();
20876     },
20877     
20878     boxLabelEl: function()
20879     {
20880         return this.el.select('label.box-label',true).first();
20881     },
20882     
20883     initEvents : function()
20884     {
20885 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20886         
20887         this.inputEl().on('click', this.onClick,  this);
20888         
20889         if (this.boxLabel) { 
20890             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20891         }
20892         
20893         this.startValue = this.getValue();
20894         
20895         if(this.groupId){
20896             Roo.bootstrap.CheckBox.register(this);
20897         }
20898     },
20899     
20900     onClick : function(e)
20901     {   
20902         if(this.fireEvent('click', this, e) !== false){
20903             this.setChecked(!this.checked);
20904         }
20905         
20906     },
20907     
20908     setChecked : function(state,suppressEvent)
20909     {
20910         this.startValue = this.getValue();
20911
20912         if(this.inputType == 'radio'){
20913             
20914             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20915                 e.dom.checked = false;
20916             });
20917             
20918             this.inputEl().dom.checked = true;
20919             
20920             this.inputEl().dom.value = this.inputValue;
20921             
20922             if(suppressEvent !== true){
20923                 this.fireEvent('check', this, true);
20924             }
20925             
20926             this.validate();
20927             
20928             return;
20929         }
20930         
20931         this.checked = state;
20932         
20933         this.inputEl().dom.checked = state;
20934         
20935         
20936         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20937         
20938         if(suppressEvent !== true){
20939             this.fireEvent('check', this, state);
20940         }
20941         
20942         this.validate();
20943     },
20944     
20945     getValue : function()
20946     {
20947         if(this.inputType == 'radio'){
20948             return this.getGroupValue();
20949         }
20950         
20951         return this.hiddenEl().dom.value;
20952         
20953     },
20954     
20955     getGroupValue : function()
20956     {
20957         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20958             return '';
20959         }
20960         
20961         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20962     },
20963     
20964     setValue : function(v,suppressEvent)
20965     {
20966         if(this.inputType == 'radio'){
20967             this.setGroupValue(v, suppressEvent);
20968             return;
20969         }
20970         
20971         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20972         
20973         this.validate();
20974     },
20975     
20976     setGroupValue : function(v, suppressEvent)
20977     {
20978         this.startValue = this.getValue();
20979         
20980         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20981             e.dom.checked = false;
20982             
20983             if(e.dom.value == v){
20984                 e.dom.checked = true;
20985             }
20986         });
20987         
20988         if(suppressEvent !== true){
20989             this.fireEvent('check', this, true);
20990         }
20991
20992         this.validate();
20993         
20994         return;
20995     },
20996     
20997     validate : function()
20998     {
20999         if(this.getVisibilityEl().hasClass('hidden')){
21000             return true;
21001         }
21002         
21003         if(
21004                 this.disabled || 
21005                 (this.inputType == 'radio' && this.validateRadio()) ||
21006                 (this.inputType == 'checkbox' && this.validateCheckbox())
21007         ){
21008             this.markValid();
21009             return true;
21010         }
21011         
21012         this.markInvalid();
21013         return false;
21014     },
21015     
21016     validateRadio : function()
21017     {
21018         if(this.getVisibilityEl().hasClass('hidden')){
21019             return true;
21020         }
21021         
21022         if(this.allowBlank){
21023             return true;
21024         }
21025         
21026         var valid = false;
21027         
21028         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21029             if(!e.dom.checked){
21030                 return;
21031             }
21032             
21033             valid = true;
21034             
21035             return false;
21036         });
21037         
21038         return valid;
21039     },
21040     
21041     validateCheckbox : function()
21042     {
21043         if(!this.groupId){
21044             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21045             //return (this.getValue() == this.inputValue) ? true : false;
21046         }
21047         
21048         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21049         
21050         if(!group){
21051             return false;
21052         }
21053         
21054         var r = false;
21055         
21056         for(var i in group){
21057             if(group[i].el.isVisible(true)){
21058                 r = false;
21059                 break;
21060             }
21061             
21062             r = true;
21063         }
21064         
21065         for(var i in group){
21066             if(r){
21067                 break;
21068             }
21069             
21070             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21071         }
21072         
21073         return r;
21074     },
21075     
21076     /**
21077      * Mark this field as valid
21078      */
21079     markValid : function()
21080     {
21081         var _this = this;
21082         
21083         this.fireEvent('valid', this);
21084         
21085         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21086         
21087         if(this.groupId){
21088             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21089         }
21090         
21091         if(label){
21092             label.markValid();
21093         }
21094
21095         if(this.inputType == 'radio'){
21096             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21097                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21098                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21099             });
21100             
21101             return;
21102         }
21103
21104         if(!this.groupId){
21105             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21106             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21107             return;
21108         }
21109         
21110         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21111         
21112         if(!group){
21113             return;
21114         }
21115         
21116         for(var i in group){
21117             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21118             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21119         }
21120     },
21121     
21122      /**
21123      * Mark this field as invalid
21124      * @param {String} msg The validation message
21125      */
21126     markInvalid : function(msg)
21127     {
21128         if(this.allowBlank){
21129             return;
21130         }
21131         
21132         var _this = this;
21133         
21134         this.fireEvent('invalid', this, msg);
21135         
21136         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21137         
21138         if(this.groupId){
21139             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21140         }
21141         
21142         if(label){
21143             label.markInvalid();
21144         }
21145             
21146         if(this.inputType == 'radio'){
21147             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21148                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21149                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21150             });
21151             
21152             return;
21153         }
21154         
21155         if(!this.groupId){
21156             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21157             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21158             return;
21159         }
21160         
21161         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21162         
21163         if(!group){
21164             return;
21165         }
21166         
21167         for(var i in group){
21168             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21169             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21170         }
21171         
21172     },
21173     
21174     clearInvalid : function()
21175     {
21176         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21177         
21178         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21179         
21180         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21181         
21182         if (label && label.iconEl) {
21183             label.iconEl.removeClass(label.validClass);
21184             label.iconEl.removeClass(label.invalidClass);
21185         }
21186     },
21187     
21188     disable : function()
21189     {
21190         if(this.inputType != 'radio'){
21191             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21192             return;
21193         }
21194         
21195         var _this = this;
21196         
21197         if(this.rendered){
21198             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21199                 _this.getActionEl().addClass(this.disabledClass);
21200                 e.dom.disabled = true;
21201             });
21202         }
21203         
21204         this.disabled = true;
21205         this.fireEvent("disable", this);
21206         return this;
21207     },
21208
21209     enable : function()
21210     {
21211         if(this.inputType != 'radio'){
21212             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21213             return;
21214         }
21215         
21216         var _this = this;
21217         
21218         if(this.rendered){
21219             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21220                 _this.getActionEl().removeClass(this.disabledClass);
21221                 e.dom.disabled = false;
21222             });
21223         }
21224         
21225         this.disabled = false;
21226         this.fireEvent("enable", this);
21227         return this;
21228     },
21229     
21230     setBoxLabel : function(v)
21231     {
21232         this.boxLabel = v;
21233         
21234         if(this.rendered){
21235             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21236         }
21237     }
21238
21239 });
21240
21241 Roo.apply(Roo.bootstrap.CheckBox, {
21242     
21243     groups: {},
21244     
21245      /**
21246     * register a CheckBox Group
21247     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21248     */
21249     register : function(checkbox)
21250     {
21251         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21252             this.groups[checkbox.groupId] = {};
21253         }
21254         
21255         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21256             return;
21257         }
21258         
21259         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21260         
21261     },
21262     /**
21263     * fetch a CheckBox Group based on the group ID
21264     * @param {string} the group ID
21265     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21266     */
21267     get: function(groupId) {
21268         if (typeof(this.groups[groupId]) == 'undefined') {
21269             return false;
21270         }
21271         
21272         return this.groups[groupId] ;
21273     }
21274     
21275     
21276 });
21277 /*
21278  * - LGPL
21279  *
21280  * RadioItem
21281  * 
21282  */
21283
21284 /**
21285  * @class Roo.bootstrap.Radio
21286  * @extends Roo.bootstrap.Component
21287  * Bootstrap Radio class
21288  * @cfg {String} boxLabel - the label associated
21289  * @cfg {String} value - the value of radio
21290  * 
21291  * @constructor
21292  * Create a new Radio
21293  * @param {Object} config The config object
21294  */
21295 Roo.bootstrap.Radio = function(config){
21296     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21297     
21298 };
21299
21300 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21301     
21302     boxLabel : '',
21303     
21304     value : '',
21305     
21306     getAutoCreate : function()
21307     {
21308         var cfg = {
21309             tag : 'div',
21310             cls : 'form-group radio',
21311             cn : [
21312                 {
21313                     tag : 'label',
21314                     cls : 'box-label',
21315                     html : this.boxLabel
21316                 }
21317             ]
21318         };
21319         
21320         return cfg;
21321     },
21322     
21323     initEvents : function() 
21324     {
21325         this.parent().register(this);
21326         
21327         this.el.on('click', this.onClick, this);
21328         
21329     },
21330     
21331     onClick : function(e)
21332     {
21333         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21334             this.setChecked(true);
21335         }
21336     },
21337     
21338     setChecked : function(state, suppressEvent)
21339     {
21340         this.parent().setValue(this.value, suppressEvent);
21341         
21342     },
21343     
21344     setBoxLabel : function(v)
21345     {
21346         this.boxLabel = v;
21347         
21348         if(this.rendered){
21349             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21350         }
21351     }
21352     
21353 });
21354  
21355
21356  /*
21357  * - LGPL
21358  *
21359  * Input
21360  * 
21361  */
21362
21363 /**
21364  * @class Roo.bootstrap.SecurePass
21365  * @extends Roo.bootstrap.Input
21366  * Bootstrap SecurePass class
21367  *
21368  * 
21369  * @constructor
21370  * Create a new SecurePass
21371  * @param {Object} config The config object
21372  */
21373  
21374 Roo.bootstrap.SecurePass = function (config) {
21375     // these go here, so the translation tool can replace them..
21376     this.errors = {
21377         PwdEmpty: "Please type a password, and then retype it to confirm.",
21378         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21379         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21380         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21381         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21382         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21383         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21384         TooWeak: "Your password is Too Weak."
21385     },
21386     this.meterLabel = "Password strength:";
21387     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21388     this.meterClass = [
21389         "roo-password-meter-tooweak", 
21390         "roo-password-meter-weak", 
21391         "roo-password-meter-medium", 
21392         "roo-password-meter-strong", 
21393         "roo-password-meter-grey"
21394     ];
21395     
21396     this.errors = {};
21397     
21398     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21399 }
21400
21401 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21402     /**
21403      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21404      * {
21405      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21406      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21407      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21408      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21409      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21410      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21411      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21412      * })
21413      */
21414     // private
21415     
21416     meterWidth: 300,
21417     errorMsg :'',    
21418     errors: false,
21419     imageRoot: '/',
21420     /**
21421      * @cfg {String/Object} Label for the strength meter (defaults to
21422      * 'Password strength:')
21423      */
21424     // private
21425     meterLabel: '',
21426     /**
21427      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21428      * ['Weak', 'Medium', 'Strong'])
21429      */
21430     // private    
21431     pwdStrengths: false,    
21432     // private
21433     strength: 0,
21434     // private
21435     _lastPwd: null,
21436     // private
21437     kCapitalLetter: 0,
21438     kSmallLetter: 1,
21439     kDigit: 2,
21440     kPunctuation: 3,
21441     
21442     insecure: false,
21443     // private
21444     initEvents: function ()
21445     {
21446         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21447
21448         if (this.el.is('input[type=password]') && Roo.isSafari) {
21449             this.el.on('keydown', this.SafariOnKeyDown, this);
21450         }
21451
21452         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21453     },
21454     // private
21455     onRender: function (ct, position)
21456     {
21457         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21458         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21459         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21460
21461         this.trigger.createChild({
21462                    cn: [
21463                     {
21464                     //id: 'PwdMeter',
21465                     tag: 'div',
21466                     cls: 'roo-password-meter-grey col-xs-12',
21467                     style: {
21468                         //width: 0,
21469                         //width: this.meterWidth + 'px'                                                
21470                         }
21471                     },
21472                     {                            
21473                          cls: 'roo-password-meter-text'                          
21474                     }
21475                 ]            
21476         });
21477
21478          
21479         if (this.hideTrigger) {
21480             this.trigger.setDisplayed(false);
21481         }
21482         this.setSize(this.width || '', this.height || '');
21483     },
21484     // private
21485     onDestroy: function ()
21486     {
21487         if (this.trigger) {
21488             this.trigger.removeAllListeners();
21489             this.trigger.remove();
21490         }
21491         if (this.wrap) {
21492             this.wrap.remove();
21493         }
21494         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21495     },
21496     // private
21497     checkStrength: function ()
21498     {
21499         var pwd = this.inputEl().getValue();
21500         if (pwd == this._lastPwd) {
21501             return;
21502         }
21503
21504         var strength;
21505         if (this.ClientSideStrongPassword(pwd)) {
21506             strength = 3;
21507         } else if (this.ClientSideMediumPassword(pwd)) {
21508             strength = 2;
21509         } else if (this.ClientSideWeakPassword(pwd)) {
21510             strength = 1;
21511         } else {
21512             strength = 0;
21513         }
21514         
21515         Roo.log('strength1: ' + strength);
21516         
21517         //var pm = this.trigger.child('div/div/div').dom;
21518         var pm = this.trigger.child('div/div');
21519         pm.removeClass(this.meterClass);
21520         pm.addClass(this.meterClass[strength]);
21521                 
21522         
21523         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21524                 
21525         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21526         
21527         this._lastPwd = pwd;
21528     },
21529     reset: function ()
21530     {
21531         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21532         
21533         this._lastPwd = '';
21534         
21535         var pm = this.trigger.child('div/div');
21536         pm.removeClass(this.meterClass);
21537         pm.addClass('roo-password-meter-grey');        
21538         
21539         
21540         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21541         
21542         pt.innerHTML = '';
21543         this.inputEl().dom.type='password';
21544     },
21545     // private
21546     validateValue: function (value)
21547     {
21548         
21549         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21550             return false;
21551         }
21552         if (value.length == 0) {
21553             if (this.allowBlank) {
21554                 this.clearInvalid();
21555                 return true;
21556             }
21557
21558             this.markInvalid(this.errors.PwdEmpty);
21559             this.errorMsg = this.errors.PwdEmpty;
21560             return false;
21561         }
21562         
21563         if(this.insecure){
21564             return true;
21565         }
21566         
21567         if ('[\x21-\x7e]*'.match(value)) {
21568             this.markInvalid(this.errors.PwdBadChar);
21569             this.errorMsg = this.errors.PwdBadChar;
21570             return false;
21571         }
21572         if (value.length < 6) {
21573             this.markInvalid(this.errors.PwdShort);
21574             this.errorMsg = this.errors.PwdShort;
21575             return false;
21576         }
21577         if (value.length > 16) {
21578             this.markInvalid(this.errors.PwdLong);
21579             this.errorMsg = this.errors.PwdLong;
21580             return false;
21581         }
21582         var strength;
21583         if (this.ClientSideStrongPassword(value)) {
21584             strength = 3;
21585         } else if (this.ClientSideMediumPassword(value)) {
21586             strength = 2;
21587         } else if (this.ClientSideWeakPassword(value)) {
21588             strength = 1;
21589         } else {
21590             strength = 0;
21591         }
21592
21593         
21594         if (strength < 2) {
21595             //this.markInvalid(this.errors.TooWeak);
21596             this.errorMsg = this.errors.TooWeak;
21597             //return false;
21598         }
21599         
21600         
21601         console.log('strength2: ' + strength);
21602         
21603         //var pm = this.trigger.child('div/div/div').dom;
21604         
21605         var pm = this.trigger.child('div/div');
21606         pm.removeClass(this.meterClass);
21607         pm.addClass(this.meterClass[strength]);
21608                 
21609         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21610                 
21611         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21612         
21613         this.errorMsg = ''; 
21614         return true;
21615     },
21616     // private
21617     CharacterSetChecks: function (type)
21618     {
21619         this.type = type;
21620         this.fResult = false;
21621     },
21622     // private
21623     isctype: function (character, type)
21624     {
21625         switch (type) {  
21626             case this.kCapitalLetter:
21627                 if (character >= 'A' && character <= 'Z') {
21628                     return true;
21629                 }
21630                 break;
21631             
21632             case this.kSmallLetter:
21633                 if (character >= 'a' && character <= 'z') {
21634                     return true;
21635                 }
21636                 break;
21637             
21638             case this.kDigit:
21639                 if (character >= '0' && character <= '9') {
21640                     return true;
21641                 }
21642                 break;
21643             
21644             case this.kPunctuation:
21645                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21646                     return true;
21647                 }
21648                 break;
21649             
21650             default:
21651                 return false;
21652         }
21653
21654     },
21655     // private
21656     IsLongEnough: function (pwd, size)
21657     {
21658         return !(pwd == null || isNaN(size) || pwd.length < size);
21659     },
21660     // private
21661     SpansEnoughCharacterSets: function (word, nb)
21662     {
21663         if (!this.IsLongEnough(word, nb))
21664         {
21665             return false;
21666         }
21667
21668         var characterSetChecks = new Array(
21669             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21670             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21671         );
21672         
21673         for (var index = 0; index < word.length; ++index) {
21674             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21675                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21676                     characterSetChecks[nCharSet].fResult = true;
21677                     break;
21678                 }
21679             }
21680         }
21681
21682         var nCharSets = 0;
21683         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21684             if (characterSetChecks[nCharSet].fResult) {
21685                 ++nCharSets;
21686             }
21687         }
21688
21689         if (nCharSets < nb) {
21690             return false;
21691         }
21692         return true;
21693     },
21694     // private
21695     ClientSideStrongPassword: function (pwd)
21696     {
21697         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21698     },
21699     // private
21700     ClientSideMediumPassword: function (pwd)
21701     {
21702         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21703     },
21704     // private
21705     ClientSideWeakPassword: function (pwd)
21706     {
21707         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21708     }
21709           
21710 })//<script type="text/javascript">
21711
21712 /*
21713  * Based  Ext JS Library 1.1.1
21714  * Copyright(c) 2006-2007, Ext JS, LLC.
21715  * LGPL
21716  *
21717  */
21718  
21719 /**
21720  * @class Roo.HtmlEditorCore
21721  * @extends Roo.Component
21722  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21723  *
21724  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21725  */
21726
21727 Roo.HtmlEditorCore = function(config){
21728     
21729     
21730     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21731     
21732     
21733     this.addEvents({
21734         /**
21735          * @event initialize
21736          * Fires when the editor is fully initialized (including the iframe)
21737          * @param {Roo.HtmlEditorCore} this
21738          */
21739         initialize: true,
21740         /**
21741          * @event activate
21742          * Fires when the editor is first receives the focus. Any insertion must wait
21743          * until after this event.
21744          * @param {Roo.HtmlEditorCore} this
21745          */
21746         activate: true,
21747          /**
21748          * @event beforesync
21749          * Fires before the textarea is updated with content from the editor iframe. Return false
21750          * to cancel the sync.
21751          * @param {Roo.HtmlEditorCore} this
21752          * @param {String} html
21753          */
21754         beforesync: true,
21755          /**
21756          * @event beforepush
21757          * Fires before the iframe editor is updated with content from the textarea. Return false
21758          * to cancel the push.
21759          * @param {Roo.HtmlEditorCore} this
21760          * @param {String} html
21761          */
21762         beforepush: true,
21763          /**
21764          * @event sync
21765          * Fires when the textarea is updated with content from the editor iframe.
21766          * @param {Roo.HtmlEditorCore} this
21767          * @param {String} html
21768          */
21769         sync: true,
21770          /**
21771          * @event push
21772          * Fires when the iframe editor is updated with content from the textarea.
21773          * @param {Roo.HtmlEditorCore} this
21774          * @param {String} html
21775          */
21776         push: true,
21777         
21778         /**
21779          * @event editorevent
21780          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21781          * @param {Roo.HtmlEditorCore} this
21782          */
21783         editorevent: true
21784         
21785     });
21786     
21787     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21788     
21789     // defaults : white / black...
21790     this.applyBlacklists();
21791     
21792     
21793     
21794 };
21795
21796
21797 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21798
21799
21800      /**
21801      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21802      */
21803     
21804     owner : false,
21805     
21806      /**
21807      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21808      *                        Roo.resizable.
21809      */
21810     resizable : false,
21811      /**
21812      * @cfg {Number} height (in pixels)
21813      */   
21814     height: 300,
21815    /**
21816      * @cfg {Number} width (in pixels)
21817      */   
21818     width: 500,
21819     
21820     /**
21821      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21822      * 
21823      */
21824     stylesheets: false,
21825     
21826     // id of frame..
21827     frameId: false,
21828     
21829     // private properties
21830     validationEvent : false,
21831     deferHeight: true,
21832     initialized : false,
21833     activated : false,
21834     sourceEditMode : false,
21835     onFocus : Roo.emptyFn,
21836     iframePad:3,
21837     hideMode:'offsets',
21838     
21839     clearUp: true,
21840     
21841     // blacklist + whitelisted elements..
21842     black: false,
21843     white: false,
21844      
21845     bodyCls : '',
21846
21847     /**
21848      * Protected method that will not generally be called directly. It
21849      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21850      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21851      */
21852     getDocMarkup : function(){
21853         // body styles..
21854         var st = '';
21855         
21856         // inherit styels from page...?? 
21857         if (this.stylesheets === false) {
21858             
21859             Roo.get(document.head).select('style').each(function(node) {
21860                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21861             });
21862             
21863             Roo.get(document.head).select('link').each(function(node) { 
21864                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21865             });
21866             
21867         } else if (!this.stylesheets.length) {
21868                 // simple..
21869                 st = '<style type="text/css">' +
21870                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21871                    '</style>';
21872         } else { 
21873             st = '<style type="text/css">' +
21874                     this.stylesheets +
21875                 '</style>';
21876         }
21877         
21878         st +=  '<style type="text/css">' +
21879             'IMG { cursor: pointer } ' +
21880         '</style>';
21881
21882         var cls = 'roo-htmleditor-body';
21883         
21884         if(this.bodyCls.length){
21885             cls += ' ' + this.bodyCls;
21886         }
21887         
21888         return '<html><head>' + st  +
21889             //<style type="text/css">' +
21890             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21891             //'</style>' +
21892             ' </head><body class="' +  cls + '"></body></html>';
21893     },
21894
21895     // private
21896     onRender : function(ct, position)
21897     {
21898         var _t = this;
21899         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21900         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21901         
21902         
21903         this.el.dom.style.border = '0 none';
21904         this.el.dom.setAttribute('tabIndex', -1);
21905         this.el.addClass('x-hidden hide');
21906         
21907         
21908         
21909         if(Roo.isIE){ // fix IE 1px bogus margin
21910             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21911         }
21912        
21913         
21914         this.frameId = Roo.id();
21915         
21916          
21917         
21918         var iframe = this.owner.wrap.createChild({
21919             tag: 'iframe',
21920             cls: 'form-control', // bootstrap..
21921             id: this.frameId,
21922             name: this.frameId,
21923             frameBorder : 'no',
21924             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21925         }, this.el
21926         );
21927         
21928         
21929         this.iframe = iframe.dom;
21930
21931          this.assignDocWin();
21932         
21933         this.doc.designMode = 'on';
21934        
21935         this.doc.open();
21936         this.doc.write(this.getDocMarkup());
21937         this.doc.close();
21938
21939         
21940         var task = { // must defer to wait for browser to be ready
21941             run : function(){
21942                 //console.log("run task?" + this.doc.readyState);
21943                 this.assignDocWin();
21944                 if(this.doc.body || this.doc.readyState == 'complete'){
21945                     try {
21946                         this.doc.designMode="on";
21947                     } catch (e) {
21948                         return;
21949                     }
21950                     Roo.TaskMgr.stop(task);
21951                     this.initEditor.defer(10, this);
21952                 }
21953             },
21954             interval : 10,
21955             duration: 10000,
21956             scope: this
21957         };
21958         Roo.TaskMgr.start(task);
21959
21960     },
21961
21962     // private
21963     onResize : function(w, h)
21964     {
21965          Roo.log('resize: ' +w + ',' + h );
21966         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21967         if(!this.iframe){
21968             return;
21969         }
21970         if(typeof w == 'number'){
21971             
21972             this.iframe.style.width = w + 'px';
21973         }
21974         if(typeof h == 'number'){
21975             
21976             this.iframe.style.height = h + 'px';
21977             if(this.doc){
21978                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21979             }
21980         }
21981         
21982     },
21983
21984     /**
21985      * Toggles the editor between standard and source edit mode.
21986      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21987      */
21988     toggleSourceEdit : function(sourceEditMode){
21989         
21990         this.sourceEditMode = sourceEditMode === true;
21991         
21992         if(this.sourceEditMode){
21993  
21994             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21995             
21996         }else{
21997             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21998             //this.iframe.className = '';
21999             this.deferFocus();
22000         }
22001         //this.setSize(this.owner.wrap.getSize());
22002         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22003     },
22004
22005     
22006   
22007
22008     /**
22009      * Protected method that will not generally be called directly. If you need/want
22010      * custom HTML cleanup, this is the method you should override.
22011      * @param {String} html The HTML to be cleaned
22012      * return {String} The cleaned HTML
22013      */
22014     cleanHtml : function(html){
22015         html = String(html);
22016         if(html.length > 5){
22017             if(Roo.isSafari){ // strip safari nonsense
22018                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22019             }
22020         }
22021         if(html == '&nbsp;'){
22022             html = '';
22023         }
22024         return html;
22025     },
22026
22027     /**
22028      * HTML Editor -> Textarea
22029      * Protected method that will not generally be called directly. Syncs the contents
22030      * of the editor iframe with the textarea.
22031      */
22032     syncValue : function(){
22033         if(this.initialized){
22034             var bd = (this.doc.body || this.doc.documentElement);
22035             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22036             var html = bd.innerHTML;
22037             if(Roo.isSafari){
22038                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22039                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22040                 if(m && m[1]){
22041                     html = '<div style="'+m[0]+'">' + html + '</div>';
22042                 }
22043             }
22044             html = this.cleanHtml(html);
22045             // fix up the special chars.. normaly like back quotes in word...
22046             // however we do not want to do this with chinese..
22047             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22048                 var cc = b.charCodeAt();
22049                 if (
22050                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22051                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22052                     (cc >= 0xf900 && cc < 0xfb00 )
22053                 ) {
22054                         return b;
22055                 }
22056                 return "&#"+cc+";" 
22057             });
22058             if(this.owner.fireEvent('beforesync', this, html) !== false){
22059                 this.el.dom.value = html;
22060                 this.owner.fireEvent('sync', this, html);
22061             }
22062         }
22063     },
22064
22065     /**
22066      * Protected method that will not generally be called directly. Pushes the value of the textarea
22067      * into the iframe editor.
22068      */
22069     pushValue : function(){
22070         if(this.initialized){
22071             var v = this.el.dom.value.trim();
22072             
22073 //            if(v.length < 1){
22074 //                v = '&#160;';
22075 //            }
22076             
22077             if(this.owner.fireEvent('beforepush', this, v) !== false){
22078                 var d = (this.doc.body || this.doc.documentElement);
22079                 d.innerHTML = v;
22080                 this.cleanUpPaste();
22081                 this.el.dom.value = d.innerHTML;
22082                 this.owner.fireEvent('push', this, v);
22083             }
22084         }
22085     },
22086
22087     // private
22088     deferFocus : function(){
22089         this.focus.defer(10, this);
22090     },
22091
22092     // doc'ed in Field
22093     focus : function(){
22094         if(this.win && !this.sourceEditMode){
22095             this.win.focus();
22096         }else{
22097             this.el.focus();
22098         }
22099     },
22100     
22101     assignDocWin: function()
22102     {
22103         var iframe = this.iframe;
22104         
22105          if(Roo.isIE){
22106             this.doc = iframe.contentWindow.document;
22107             this.win = iframe.contentWindow;
22108         } else {
22109 //            if (!Roo.get(this.frameId)) {
22110 //                return;
22111 //            }
22112 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22113 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22114             
22115             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22116                 return;
22117             }
22118             
22119             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22120             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22121         }
22122     },
22123     
22124     // private
22125     initEditor : function(){
22126         //console.log("INIT EDITOR");
22127         this.assignDocWin();
22128         
22129         
22130         
22131         this.doc.designMode="on";
22132         this.doc.open();
22133         this.doc.write(this.getDocMarkup());
22134         this.doc.close();
22135         
22136         var dbody = (this.doc.body || this.doc.documentElement);
22137         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22138         // this copies styles from the containing element into thsi one..
22139         // not sure why we need all of this..
22140         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22141         
22142         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22143         //ss['background-attachment'] = 'fixed'; // w3c
22144         dbody.bgProperties = 'fixed'; // ie
22145         //Roo.DomHelper.applyStyles(dbody, ss);
22146         Roo.EventManager.on(this.doc, {
22147             //'mousedown': this.onEditorEvent,
22148             'mouseup': this.onEditorEvent,
22149             'dblclick': this.onEditorEvent,
22150             'click': this.onEditorEvent,
22151             'keyup': this.onEditorEvent,
22152             buffer:100,
22153             scope: this
22154         });
22155         if(Roo.isGecko){
22156             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22157         }
22158         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22159             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22160         }
22161         this.initialized = true;
22162
22163         this.owner.fireEvent('initialize', this);
22164         this.pushValue();
22165     },
22166
22167     // private
22168     onDestroy : function(){
22169         
22170         
22171         
22172         if(this.rendered){
22173             
22174             //for (var i =0; i < this.toolbars.length;i++) {
22175             //    // fixme - ask toolbars for heights?
22176             //    this.toolbars[i].onDestroy();
22177            // }
22178             
22179             //this.wrap.dom.innerHTML = '';
22180             //this.wrap.remove();
22181         }
22182     },
22183
22184     // private
22185     onFirstFocus : function(){
22186         
22187         this.assignDocWin();
22188         
22189         
22190         this.activated = true;
22191          
22192     
22193         if(Roo.isGecko){ // prevent silly gecko errors
22194             this.win.focus();
22195             var s = this.win.getSelection();
22196             if(!s.focusNode || s.focusNode.nodeType != 3){
22197                 var r = s.getRangeAt(0);
22198                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22199                 r.collapse(true);
22200                 this.deferFocus();
22201             }
22202             try{
22203                 this.execCmd('useCSS', true);
22204                 this.execCmd('styleWithCSS', false);
22205             }catch(e){}
22206         }
22207         this.owner.fireEvent('activate', this);
22208     },
22209
22210     // private
22211     adjustFont: function(btn){
22212         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22213         //if(Roo.isSafari){ // safari
22214         //    adjust *= 2;
22215        // }
22216         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22217         if(Roo.isSafari){ // safari
22218             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22219             v =  (v < 10) ? 10 : v;
22220             v =  (v > 48) ? 48 : v;
22221             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22222             
22223         }
22224         
22225         
22226         v = Math.max(1, v+adjust);
22227         
22228         this.execCmd('FontSize', v  );
22229     },
22230
22231     onEditorEvent : function(e)
22232     {
22233         this.owner.fireEvent('editorevent', this, e);
22234       //  this.updateToolbar();
22235         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22236     },
22237
22238     insertTag : function(tg)
22239     {
22240         // could be a bit smarter... -> wrap the current selected tRoo..
22241         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22242             
22243             range = this.createRange(this.getSelection());
22244             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22245             wrappingNode.appendChild(range.extractContents());
22246             range.insertNode(wrappingNode);
22247
22248             return;
22249             
22250             
22251             
22252         }
22253         this.execCmd("formatblock",   tg);
22254         
22255     },
22256     
22257     insertText : function(txt)
22258     {
22259         
22260         
22261         var range = this.createRange();
22262         range.deleteContents();
22263                //alert(Sender.getAttribute('label'));
22264                
22265         range.insertNode(this.doc.createTextNode(txt));
22266     } ,
22267     
22268      
22269
22270     /**
22271      * Executes a Midas editor command on the editor document and performs necessary focus and
22272      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22273      * @param {String} cmd The Midas command
22274      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22275      */
22276     relayCmd : function(cmd, value){
22277         this.win.focus();
22278         this.execCmd(cmd, value);
22279         this.owner.fireEvent('editorevent', this);
22280         //this.updateToolbar();
22281         this.owner.deferFocus();
22282     },
22283
22284     /**
22285      * Executes a Midas editor command directly on the editor document.
22286      * For visual commands, you should use {@link #relayCmd} instead.
22287      * <b>This should only be called after the editor is initialized.</b>
22288      * @param {String} cmd The Midas command
22289      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22290      */
22291     execCmd : function(cmd, value){
22292         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22293         this.syncValue();
22294     },
22295  
22296  
22297    
22298     /**
22299      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22300      * to insert tRoo.
22301      * @param {String} text | dom node.. 
22302      */
22303     insertAtCursor : function(text)
22304     {
22305         
22306         if(!this.activated){
22307             return;
22308         }
22309         /*
22310         if(Roo.isIE){
22311             this.win.focus();
22312             var r = this.doc.selection.createRange();
22313             if(r){
22314                 r.collapse(true);
22315                 r.pasteHTML(text);
22316                 this.syncValue();
22317                 this.deferFocus();
22318             
22319             }
22320             return;
22321         }
22322         */
22323         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22324             this.win.focus();
22325             
22326             
22327             // from jquery ui (MIT licenced)
22328             var range, node;
22329             var win = this.win;
22330             
22331             if (win.getSelection && win.getSelection().getRangeAt) {
22332                 range = win.getSelection().getRangeAt(0);
22333                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22334                 range.insertNode(node);
22335             } else if (win.document.selection && win.document.selection.createRange) {
22336                 // no firefox support
22337                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22338                 win.document.selection.createRange().pasteHTML(txt);
22339             } else {
22340                 // no firefox support
22341                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22342                 this.execCmd('InsertHTML', txt);
22343             } 
22344             
22345             this.syncValue();
22346             
22347             this.deferFocus();
22348         }
22349     },
22350  // private
22351     mozKeyPress : function(e){
22352         if(e.ctrlKey){
22353             var c = e.getCharCode(), cmd;
22354           
22355             if(c > 0){
22356                 c = String.fromCharCode(c).toLowerCase();
22357                 switch(c){
22358                     case 'b':
22359                         cmd = 'bold';
22360                         break;
22361                     case 'i':
22362                         cmd = 'italic';
22363                         break;
22364                     
22365                     case 'u':
22366                         cmd = 'underline';
22367                         break;
22368                     
22369                     case 'v':
22370                         this.cleanUpPaste.defer(100, this);
22371                         return;
22372                         
22373                 }
22374                 if(cmd){
22375                     this.win.focus();
22376                     this.execCmd(cmd);
22377                     this.deferFocus();
22378                     e.preventDefault();
22379                 }
22380                 
22381             }
22382         }
22383     },
22384
22385     // private
22386     fixKeys : function(){ // load time branching for fastest keydown performance
22387         if(Roo.isIE){
22388             return function(e){
22389                 var k = e.getKey(), r;
22390                 if(k == e.TAB){
22391                     e.stopEvent();
22392                     r = this.doc.selection.createRange();
22393                     if(r){
22394                         r.collapse(true);
22395                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22396                         this.deferFocus();
22397                     }
22398                     return;
22399                 }
22400                 
22401                 if(k == e.ENTER){
22402                     r = this.doc.selection.createRange();
22403                     if(r){
22404                         var target = r.parentElement();
22405                         if(!target || target.tagName.toLowerCase() != 'li'){
22406                             e.stopEvent();
22407                             r.pasteHTML('<br />');
22408                             r.collapse(false);
22409                             r.select();
22410                         }
22411                     }
22412                 }
22413                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22414                     this.cleanUpPaste.defer(100, this);
22415                     return;
22416                 }
22417                 
22418                 
22419             };
22420         }else if(Roo.isOpera){
22421             return function(e){
22422                 var k = e.getKey();
22423                 if(k == e.TAB){
22424                     e.stopEvent();
22425                     this.win.focus();
22426                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22427                     this.deferFocus();
22428                 }
22429                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22430                     this.cleanUpPaste.defer(100, this);
22431                     return;
22432                 }
22433                 
22434             };
22435         }else if(Roo.isSafari){
22436             return function(e){
22437                 var k = e.getKey();
22438                 
22439                 if(k == e.TAB){
22440                     e.stopEvent();
22441                     this.execCmd('InsertText','\t');
22442                     this.deferFocus();
22443                     return;
22444                 }
22445                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22446                     this.cleanUpPaste.defer(100, this);
22447                     return;
22448                 }
22449                 
22450              };
22451         }
22452     }(),
22453     
22454     getAllAncestors: function()
22455     {
22456         var p = this.getSelectedNode();
22457         var a = [];
22458         if (!p) {
22459             a.push(p); // push blank onto stack..
22460             p = this.getParentElement();
22461         }
22462         
22463         
22464         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22465             a.push(p);
22466             p = p.parentNode;
22467         }
22468         a.push(this.doc.body);
22469         return a;
22470     },
22471     lastSel : false,
22472     lastSelNode : false,
22473     
22474     
22475     getSelection : function() 
22476     {
22477         this.assignDocWin();
22478         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22479     },
22480     
22481     getSelectedNode: function() 
22482     {
22483         // this may only work on Gecko!!!
22484         
22485         // should we cache this!!!!
22486         
22487         
22488         
22489          
22490         var range = this.createRange(this.getSelection()).cloneRange();
22491         
22492         if (Roo.isIE) {
22493             var parent = range.parentElement();
22494             while (true) {
22495                 var testRange = range.duplicate();
22496                 testRange.moveToElementText(parent);
22497                 if (testRange.inRange(range)) {
22498                     break;
22499                 }
22500                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22501                     break;
22502                 }
22503                 parent = parent.parentElement;
22504             }
22505             return parent;
22506         }
22507         
22508         // is ancestor a text element.
22509         var ac =  range.commonAncestorContainer;
22510         if (ac.nodeType == 3) {
22511             ac = ac.parentNode;
22512         }
22513         
22514         var ar = ac.childNodes;
22515          
22516         var nodes = [];
22517         var other_nodes = [];
22518         var has_other_nodes = false;
22519         for (var i=0;i<ar.length;i++) {
22520             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22521                 continue;
22522             }
22523             // fullly contained node.
22524             
22525             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22526                 nodes.push(ar[i]);
22527                 continue;
22528             }
22529             
22530             // probably selected..
22531             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22532                 other_nodes.push(ar[i]);
22533                 continue;
22534             }
22535             // outer..
22536             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22537                 continue;
22538             }
22539             
22540             
22541             has_other_nodes = true;
22542         }
22543         if (!nodes.length && other_nodes.length) {
22544             nodes= other_nodes;
22545         }
22546         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22547             return false;
22548         }
22549         
22550         return nodes[0];
22551     },
22552     createRange: function(sel)
22553     {
22554         // this has strange effects when using with 
22555         // top toolbar - not sure if it's a great idea.
22556         //this.editor.contentWindow.focus();
22557         if (typeof sel != "undefined") {
22558             try {
22559                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22560             } catch(e) {
22561                 return this.doc.createRange();
22562             }
22563         } else {
22564             return this.doc.createRange();
22565         }
22566     },
22567     getParentElement: function()
22568     {
22569         
22570         this.assignDocWin();
22571         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22572         
22573         var range = this.createRange(sel);
22574          
22575         try {
22576             var p = range.commonAncestorContainer;
22577             while (p.nodeType == 3) { // text node
22578                 p = p.parentNode;
22579             }
22580             return p;
22581         } catch (e) {
22582             return null;
22583         }
22584     
22585     },
22586     /***
22587      *
22588      * Range intersection.. the hard stuff...
22589      *  '-1' = before
22590      *  '0' = hits..
22591      *  '1' = after.
22592      *         [ -- selected range --- ]
22593      *   [fail]                        [fail]
22594      *
22595      *    basically..
22596      *      if end is before start or  hits it. fail.
22597      *      if start is after end or hits it fail.
22598      *
22599      *   if either hits (but other is outside. - then it's not 
22600      *   
22601      *    
22602      **/
22603     
22604     
22605     // @see http://www.thismuchiknow.co.uk/?p=64.
22606     rangeIntersectsNode : function(range, node)
22607     {
22608         var nodeRange = node.ownerDocument.createRange();
22609         try {
22610             nodeRange.selectNode(node);
22611         } catch (e) {
22612             nodeRange.selectNodeContents(node);
22613         }
22614     
22615         var rangeStartRange = range.cloneRange();
22616         rangeStartRange.collapse(true);
22617     
22618         var rangeEndRange = range.cloneRange();
22619         rangeEndRange.collapse(false);
22620     
22621         var nodeStartRange = nodeRange.cloneRange();
22622         nodeStartRange.collapse(true);
22623     
22624         var nodeEndRange = nodeRange.cloneRange();
22625         nodeEndRange.collapse(false);
22626     
22627         return rangeStartRange.compareBoundaryPoints(
22628                  Range.START_TO_START, nodeEndRange) == -1 &&
22629                rangeEndRange.compareBoundaryPoints(
22630                  Range.START_TO_START, nodeStartRange) == 1;
22631         
22632          
22633     },
22634     rangeCompareNode : function(range, node)
22635     {
22636         var nodeRange = node.ownerDocument.createRange();
22637         try {
22638             nodeRange.selectNode(node);
22639         } catch (e) {
22640             nodeRange.selectNodeContents(node);
22641         }
22642         
22643         
22644         range.collapse(true);
22645     
22646         nodeRange.collapse(true);
22647      
22648         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22649         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22650          
22651         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22652         
22653         var nodeIsBefore   =  ss == 1;
22654         var nodeIsAfter    = ee == -1;
22655         
22656         if (nodeIsBefore && nodeIsAfter) {
22657             return 0; // outer
22658         }
22659         if (!nodeIsBefore && nodeIsAfter) {
22660             return 1; //right trailed.
22661         }
22662         
22663         if (nodeIsBefore && !nodeIsAfter) {
22664             return 2;  // left trailed.
22665         }
22666         // fully contined.
22667         return 3;
22668     },
22669
22670     // private? - in a new class?
22671     cleanUpPaste :  function()
22672     {
22673         // cleans up the whole document..
22674         Roo.log('cleanuppaste');
22675         
22676         this.cleanUpChildren(this.doc.body);
22677         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22678         if (clean != this.doc.body.innerHTML) {
22679             this.doc.body.innerHTML = clean;
22680         }
22681         
22682     },
22683     
22684     cleanWordChars : function(input) {// change the chars to hex code
22685         var he = Roo.HtmlEditorCore;
22686         
22687         var output = input;
22688         Roo.each(he.swapCodes, function(sw) { 
22689             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22690             
22691             output = output.replace(swapper, sw[1]);
22692         });
22693         
22694         return output;
22695     },
22696     
22697     
22698     cleanUpChildren : function (n)
22699     {
22700         if (!n.childNodes.length) {
22701             return;
22702         }
22703         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22704            this.cleanUpChild(n.childNodes[i]);
22705         }
22706     },
22707     
22708     
22709         
22710     
22711     cleanUpChild : function (node)
22712     {
22713         var ed = this;
22714         //console.log(node);
22715         if (node.nodeName == "#text") {
22716             // clean up silly Windows -- stuff?
22717             return; 
22718         }
22719         if (node.nodeName == "#comment") {
22720             node.parentNode.removeChild(node);
22721             // clean up silly Windows -- stuff?
22722             return; 
22723         }
22724         var lcname = node.tagName.toLowerCase();
22725         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22726         // whitelist of tags..
22727         
22728         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22729             // remove node.
22730             node.parentNode.removeChild(node);
22731             return;
22732             
22733         }
22734         
22735         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22736         
22737         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22738         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22739         
22740         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22741         //    remove_keep_children = true;
22742         //}
22743         
22744         if (remove_keep_children) {
22745             this.cleanUpChildren(node);
22746             // inserts everything just before this node...
22747             while (node.childNodes.length) {
22748                 var cn = node.childNodes[0];
22749                 node.removeChild(cn);
22750                 node.parentNode.insertBefore(cn, node);
22751             }
22752             node.parentNode.removeChild(node);
22753             return;
22754         }
22755         
22756         if (!node.attributes || !node.attributes.length) {
22757             this.cleanUpChildren(node);
22758             return;
22759         }
22760         
22761         function cleanAttr(n,v)
22762         {
22763             
22764             if (v.match(/^\./) || v.match(/^\//)) {
22765                 return;
22766             }
22767             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22768                 return;
22769             }
22770             if (v.match(/^#/)) {
22771                 return;
22772             }
22773 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22774             node.removeAttribute(n);
22775             
22776         }
22777         
22778         var cwhite = this.cwhite;
22779         var cblack = this.cblack;
22780             
22781         function cleanStyle(n,v)
22782         {
22783             if (v.match(/expression/)) { //XSS?? should we even bother..
22784                 node.removeAttribute(n);
22785                 return;
22786             }
22787             
22788             var parts = v.split(/;/);
22789             var clean = [];
22790             
22791             Roo.each(parts, function(p) {
22792                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22793                 if (!p.length) {
22794                     return true;
22795                 }
22796                 var l = p.split(':').shift().replace(/\s+/g,'');
22797                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22798                 
22799                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22800 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22801                     //node.removeAttribute(n);
22802                     return true;
22803                 }
22804                 //Roo.log()
22805                 // only allow 'c whitelisted system attributes'
22806                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22807 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22808                     //node.removeAttribute(n);
22809                     return true;
22810                 }
22811                 
22812                 
22813                  
22814                 
22815                 clean.push(p);
22816                 return true;
22817             });
22818             if (clean.length) { 
22819                 node.setAttribute(n, clean.join(';'));
22820             } else {
22821                 node.removeAttribute(n);
22822             }
22823             
22824         }
22825         
22826         
22827         for (var i = node.attributes.length-1; i > -1 ; i--) {
22828             var a = node.attributes[i];
22829             //console.log(a);
22830             
22831             if (a.name.toLowerCase().substr(0,2)=='on')  {
22832                 node.removeAttribute(a.name);
22833                 continue;
22834             }
22835             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22836                 node.removeAttribute(a.name);
22837                 continue;
22838             }
22839             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22840                 cleanAttr(a.name,a.value); // fixme..
22841                 continue;
22842             }
22843             if (a.name == 'style') {
22844                 cleanStyle(a.name,a.value);
22845                 continue;
22846             }
22847             /// clean up MS crap..
22848             // tecnically this should be a list of valid class'es..
22849             
22850             
22851             if (a.name == 'class') {
22852                 if (a.value.match(/^Mso/)) {
22853                     node.className = '';
22854                 }
22855                 
22856                 if (a.value.match(/^body$/)) {
22857                     node.className = '';
22858                 }
22859                 continue;
22860             }
22861             
22862             // style cleanup!?
22863             // class cleanup?
22864             
22865         }
22866         
22867         
22868         this.cleanUpChildren(node);
22869         
22870         
22871     },
22872     
22873     /**
22874      * Clean up MS wordisms...
22875      */
22876     cleanWord : function(node)
22877     {
22878         
22879         
22880         if (!node) {
22881             this.cleanWord(this.doc.body);
22882             return;
22883         }
22884         if (node.nodeName == "#text") {
22885             // clean up silly Windows -- stuff?
22886             return; 
22887         }
22888         if (node.nodeName == "#comment") {
22889             node.parentNode.removeChild(node);
22890             // clean up silly Windows -- stuff?
22891             return; 
22892         }
22893         
22894         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22895             node.parentNode.removeChild(node);
22896             return;
22897         }
22898         
22899         // remove - but keep children..
22900         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22901             while (node.childNodes.length) {
22902                 var cn = node.childNodes[0];
22903                 node.removeChild(cn);
22904                 node.parentNode.insertBefore(cn, node);
22905             }
22906             node.parentNode.removeChild(node);
22907             this.iterateChildren(node, this.cleanWord);
22908             return;
22909         }
22910         // clean styles
22911         if (node.className.length) {
22912             
22913             var cn = node.className.split(/\W+/);
22914             var cna = [];
22915             Roo.each(cn, function(cls) {
22916                 if (cls.match(/Mso[a-zA-Z]+/)) {
22917                     return;
22918                 }
22919                 cna.push(cls);
22920             });
22921             node.className = cna.length ? cna.join(' ') : '';
22922             if (!cna.length) {
22923                 node.removeAttribute("class");
22924             }
22925         }
22926         
22927         if (node.hasAttribute("lang")) {
22928             node.removeAttribute("lang");
22929         }
22930         
22931         if (node.hasAttribute("style")) {
22932             
22933             var styles = node.getAttribute("style").split(";");
22934             var nstyle = [];
22935             Roo.each(styles, function(s) {
22936                 if (!s.match(/:/)) {
22937                     return;
22938                 }
22939                 var kv = s.split(":");
22940                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22941                     return;
22942                 }
22943                 // what ever is left... we allow.
22944                 nstyle.push(s);
22945             });
22946             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22947             if (!nstyle.length) {
22948                 node.removeAttribute('style');
22949             }
22950         }
22951         this.iterateChildren(node, this.cleanWord);
22952         
22953         
22954         
22955     },
22956     /**
22957      * iterateChildren of a Node, calling fn each time, using this as the scole..
22958      * @param {DomNode} node node to iterate children of.
22959      * @param {Function} fn method of this class to call on each item.
22960      */
22961     iterateChildren : function(node, fn)
22962     {
22963         if (!node.childNodes.length) {
22964                 return;
22965         }
22966         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22967            fn.call(this, node.childNodes[i])
22968         }
22969     },
22970     
22971     
22972     /**
22973      * cleanTableWidths.
22974      *
22975      * Quite often pasting from word etc.. results in tables with column and widths.
22976      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22977      *
22978      */
22979     cleanTableWidths : function(node)
22980     {
22981          
22982          
22983         if (!node) {
22984             this.cleanTableWidths(this.doc.body);
22985             return;
22986         }
22987         
22988         // ignore list...
22989         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22990             return; 
22991         }
22992         Roo.log(node.tagName);
22993         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22994             this.iterateChildren(node, this.cleanTableWidths);
22995             return;
22996         }
22997         if (node.hasAttribute('width')) {
22998             node.removeAttribute('width');
22999         }
23000         
23001          
23002         if (node.hasAttribute("style")) {
23003             // pretty basic...
23004             
23005             var styles = node.getAttribute("style").split(";");
23006             var nstyle = [];
23007             Roo.each(styles, function(s) {
23008                 if (!s.match(/:/)) {
23009                     return;
23010                 }
23011                 var kv = s.split(":");
23012                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23013                     return;
23014                 }
23015                 // what ever is left... we allow.
23016                 nstyle.push(s);
23017             });
23018             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23019             if (!nstyle.length) {
23020                 node.removeAttribute('style');
23021             }
23022         }
23023         
23024         this.iterateChildren(node, this.cleanTableWidths);
23025         
23026         
23027     },
23028     
23029     
23030     
23031     
23032     domToHTML : function(currentElement, depth, nopadtext) {
23033         
23034         depth = depth || 0;
23035         nopadtext = nopadtext || false;
23036     
23037         if (!currentElement) {
23038             return this.domToHTML(this.doc.body);
23039         }
23040         
23041         //Roo.log(currentElement);
23042         var j;
23043         var allText = false;
23044         var nodeName = currentElement.nodeName;
23045         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23046         
23047         if  (nodeName == '#text') {
23048             
23049             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23050         }
23051         
23052         
23053         var ret = '';
23054         if (nodeName != 'BODY') {
23055              
23056             var i = 0;
23057             // Prints the node tagName, such as <A>, <IMG>, etc
23058             if (tagName) {
23059                 var attr = [];
23060                 for(i = 0; i < currentElement.attributes.length;i++) {
23061                     // quoting?
23062                     var aname = currentElement.attributes.item(i).name;
23063                     if (!currentElement.attributes.item(i).value.length) {
23064                         continue;
23065                     }
23066                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23067                 }
23068                 
23069                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23070             } 
23071             else {
23072                 
23073                 // eack
23074             }
23075         } else {
23076             tagName = false;
23077         }
23078         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23079             return ret;
23080         }
23081         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23082             nopadtext = true;
23083         }
23084         
23085         
23086         // Traverse the tree
23087         i = 0;
23088         var currentElementChild = currentElement.childNodes.item(i);
23089         var allText = true;
23090         var innerHTML  = '';
23091         lastnode = '';
23092         while (currentElementChild) {
23093             // Formatting code (indent the tree so it looks nice on the screen)
23094             var nopad = nopadtext;
23095             if (lastnode == 'SPAN') {
23096                 nopad  = true;
23097             }
23098             // text
23099             if  (currentElementChild.nodeName == '#text') {
23100                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23101                 toadd = nopadtext ? toadd : toadd.trim();
23102                 if (!nopad && toadd.length > 80) {
23103                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23104                 }
23105                 innerHTML  += toadd;
23106                 
23107                 i++;
23108                 currentElementChild = currentElement.childNodes.item(i);
23109                 lastNode = '';
23110                 continue;
23111             }
23112             allText = false;
23113             
23114             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23115                 
23116             // Recursively traverse the tree structure of the child node
23117             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23118             lastnode = currentElementChild.nodeName;
23119             i++;
23120             currentElementChild=currentElement.childNodes.item(i);
23121         }
23122         
23123         ret += innerHTML;
23124         
23125         if (!allText) {
23126                 // The remaining code is mostly for formatting the tree
23127             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23128         }
23129         
23130         
23131         if (tagName) {
23132             ret+= "</"+tagName+">";
23133         }
23134         return ret;
23135         
23136     },
23137         
23138     applyBlacklists : function()
23139     {
23140         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23141         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23142         
23143         this.white = [];
23144         this.black = [];
23145         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23146             if (b.indexOf(tag) > -1) {
23147                 return;
23148             }
23149             this.white.push(tag);
23150             
23151         }, this);
23152         
23153         Roo.each(w, function(tag) {
23154             if (b.indexOf(tag) > -1) {
23155                 return;
23156             }
23157             if (this.white.indexOf(tag) > -1) {
23158                 return;
23159             }
23160             this.white.push(tag);
23161             
23162         }, this);
23163         
23164         
23165         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23166             if (w.indexOf(tag) > -1) {
23167                 return;
23168             }
23169             this.black.push(tag);
23170             
23171         }, this);
23172         
23173         Roo.each(b, function(tag) {
23174             if (w.indexOf(tag) > -1) {
23175                 return;
23176             }
23177             if (this.black.indexOf(tag) > -1) {
23178                 return;
23179             }
23180             this.black.push(tag);
23181             
23182         }, this);
23183         
23184         
23185         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23186         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23187         
23188         this.cwhite = [];
23189         this.cblack = [];
23190         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23191             if (b.indexOf(tag) > -1) {
23192                 return;
23193             }
23194             this.cwhite.push(tag);
23195             
23196         }, this);
23197         
23198         Roo.each(w, function(tag) {
23199             if (b.indexOf(tag) > -1) {
23200                 return;
23201             }
23202             if (this.cwhite.indexOf(tag) > -1) {
23203                 return;
23204             }
23205             this.cwhite.push(tag);
23206             
23207         }, this);
23208         
23209         
23210         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23211             if (w.indexOf(tag) > -1) {
23212                 return;
23213             }
23214             this.cblack.push(tag);
23215             
23216         }, this);
23217         
23218         Roo.each(b, function(tag) {
23219             if (w.indexOf(tag) > -1) {
23220                 return;
23221             }
23222             if (this.cblack.indexOf(tag) > -1) {
23223                 return;
23224             }
23225             this.cblack.push(tag);
23226             
23227         }, this);
23228     },
23229     
23230     setStylesheets : function(stylesheets)
23231     {
23232         if(typeof(stylesheets) == 'string'){
23233             Roo.get(this.iframe.contentDocument.head).createChild({
23234                 tag : 'link',
23235                 rel : 'stylesheet',
23236                 type : 'text/css',
23237                 href : stylesheets
23238             });
23239             
23240             return;
23241         }
23242         var _this = this;
23243      
23244         Roo.each(stylesheets, function(s) {
23245             if(!s.length){
23246                 return;
23247             }
23248             
23249             Roo.get(_this.iframe.contentDocument.head).createChild({
23250                 tag : 'link',
23251                 rel : 'stylesheet',
23252                 type : 'text/css',
23253                 href : s
23254             });
23255         });
23256
23257         
23258     },
23259     
23260     removeStylesheets : function()
23261     {
23262         var _this = this;
23263         
23264         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23265             s.remove();
23266         });
23267     },
23268     
23269     setStyle : function(style)
23270     {
23271         Roo.get(this.iframe.contentDocument.head).createChild({
23272             tag : 'style',
23273             type : 'text/css',
23274             html : style
23275         });
23276
23277         return;
23278     }
23279     
23280     // hide stuff that is not compatible
23281     /**
23282      * @event blur
23283      * @hide
23284      */
23285     /**
23286      * @event change
23287      * @hide
23288      */
23289     /**
23290      * @event focus
23291      * @hide
23292      */
23293     /**
23294      * @event specialkey
23295      * @hide
23296      */
23297     /**
23298      * @cfg {String} fieldClass @hide
23299      */
23300     /**
23301      * @cfg {String} focusClass @hide
23302      */
23303     /**
23304      * @cfg {String} autoCreate @hide
23305      */
23306     /**
23307      * @cfg {String} inputType @hide
23308      */
23309     /**
23310      * @cfg {String} invalidClass @hide
23311      */
23312     /**
23313      * @cfg {String} invalidText @hide
23314      */
23315     /**
23316      * @cfg {String} msgFx @hide
23317      */
23318     /**
23319      * @cfg {String} validateOnBlur @hide
23320      */
23321 });
23322
23323 Roo.HtmlEditorCore.white = [
23324         'area', 'br', 'img', 'input', 'hr', 'wbr',
23325         
23326        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23327        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23328        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23329        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23330        'table',   'ul',         'xmp', 
23331        
23332        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23333       'thead',   'tr', 
23334      
23335       'dir', 'menu', 'ol', 'ul', 'dl',
23336        
23337       'embed',  'object'
23338 ];
23339
23340
23341 Roo.HtmlEditorCore.black = [
23342     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23343         'applet', // 
23344         'base',   'basefont', 'bgsound', 'blink',  'body', 
23345         'frame',  'frameset', 'head',    'html',   'ilayer', 
23346         'iframe', 'layer',  'link',     'meta',    'object',   
23347         'script', 'style' ,'title',  'xml' // clean later..
23348 ];
23349 Roo.HtmlEditorCore.clean = [
23350     'script', 'style', 'title', 'xml'
23351 ];
23352 Roo.HtmlEditorCore.remove = [
23353     'font'
23354 ];
23355 // attributes..
23356
23357 Roo.HtmlEditorCore.ablack = [
23358     'on'
23359 ];
23360     
23361 Roo.HtmlEditorCore.aclean = [ 
23362     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23363 ];
23364
23365 // protocols..
23366 Roo.HtmlEditorCore.pwhite= [
23367         'http',  'https',  'mailto'
23368 ];
23369
23370 // white listed style attributes.
23371 Roo.HtmlEditorCore.cwhite= [
23372       //  'text-align', /// default is to allow most things..
23373       
23374          
23375 //        'font-size'//??
23376 ];
23377
23378 // black listed style attributes.
23379 Roo.HtmlEditorCore.cblack= [
23380       //  'font-size' -- this can be set by the project 
23381 ];
23382
23383
23384 Roo.HtmlEditorCore.swapCodes   =[ 
23385     [    8211, "--" ], 
23386     [    8212, "--" ], 
23387     [    8216,  "'" ],  
23388     [    8217, "'" ],  
23389     [    8220, '"' ],  
23390     [    8221, '"' ],  
23391     [    8226, "*" ],  
23392     [    8230, "..." ]
23393 ]; 
23394
23395     /*
23396  * - LGPL
23397  *
23398  * HtmlEditor
23399  * 
23400  */
23401
23402 /**
23403  * @class Roo.bootstrap.HtmlEditor
23404  * @extends Roo.bootstrap.TextArea
23405  * Bootstrap HtmlEditor class
23406
23407  * @constructor
23408  * Create a new HtmlEditor
23409  * @param {Object} config The config object
23410  */
23411
23412 Roo.bootstrap.HtmlEditor = function(config){
23413     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23414     if (!this.toolbars) {
23415         this.toolbars = [];
23416     }
23417     
23418     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23419     this.addEvents({
23420             /**
23421              * @event initialize
23422              * Fires when the editor is fully initialized (including the iframe)
23423              * @param {HtmlEditor} this
23424              */
23425             initialize: true,
23426             /**
23427              * @event activate
23428              * Fires when the editor is first receives the focus. Any insertion must wait
23429              * until after this event.
23430              * @param {HtmlEditor} this
23431              */
23432             activate: true,
23433              /**
23434              * @event beforesync
23435              * Fires before the textarea is updated with content from the editor iframe. Return false
23436              * to cancel the sync.
23437              * @param {HtmlEditor} this
23438              * @param {String} html
23439              */
23440             beforesync: true,
23441              /**
23442              * @event beforepush
23443              * Fires before the iframe editor is updated with content from the textarea. Return false
23444              * to cancel the push.
23445              * @param {HtmlEditor} this
23446              * @param {String} html
23447              */
23448             beforepush: true,
23449              /**
23450              * @event sync
23451              * Fires when the textarea is updated with content from the editor iframe.
23452              * @param {HtmlEditor} this
23453              * @param {String} html
23454              */
23455             sync: true,
23456              /**
23457              * @event push
23458              * Fires when the iframe editor is updated with content from the textarea.
23459              * @param {HtmlEditor} this
23460              * @param {String} html
23461              */
23462             push: true,
23463              /**
23464              * @event editmodechange
23465              * Fires when the editor switches edit modes
23466              * @param {HtmlEditor} this
23467              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23468              */
23469             editmodechange: true,
23470             /**
23471              * @event editorevent
23472              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23473              * @param {HtmlEditor} this
23474              */
23475             editorevent: true,
23476             /**
23477              * @event firstfocus
23478              * Fires when on first focus - needed by toolbars..
23479              * @param {HtmlEditor} this
23480              */
23481             firstfocus: true,
23482             /**
23483              * @event autosave
23484              * Auto save the htmlEditor value as a file into Events
23485              * @param {HtmlEditor} this
23486              */
23487             autosave: true,
23488             /**
23489              * @event savedpreview
23490              * preview the saved version of htmlEditor
23491              * @param {HtmlEditor} this
23492              */
23493             savedpreview: true
23494         });
23495 };
23496
23497
23498 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23499     
23500     
23501       /**
23502      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23503      */
23504     toolbars : false,
23505     
23506      /**
23507     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23508     */
23509     btns : [],
23510    
23511      /**
23512      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23513      *                        Roo.resizable.
23514      */
23515     resizable : false,
23516      /**
23517      * @cfg {Number} height (in pixels)
23518      */   
23519     height: 300,
23520    /**
23521      * @cfg {Number} width (in pixels)
23522      */   
23523     width: false,
23524     
23525     /**
23526      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23527      * 
23528      */
23529     stylesheets: false,
23530     
23531     // id of frame..
23532     frameId: false,
23533     
23534     // private properties
23535     validationEvent : false,
23536     deferHeight: true,
23537     initialized : false,
23538     activated : false,
23539     
23540     onFocus : Roo.emptyFn,
23541     iframePad:3,
23542     hideMode:'offsets',
23543     
23544     tbContainer : false,
23545     
23546     bodyCls : '',
23547     
23548     toolbarContainer :function() {
23549         return this.wrap.select('.x-html-editor-tb',true).first();
23550     },
23551
23552     /**
23553      * Protected method that will not generally be called directly. It
23554      * is called when the editor creates its toolbar. Override this method if you need to
23555      * add custom toolbar buttons.
23556      * @param {HtmlEditor} editor
23557      */
23558     createToolbar : function(){
23559         Roo.log('renewing');
23560         Roo.log("create toolbars");
23561         
23562         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23563         this.toolbars[0].render(this.toolbarContainer());
23564         
23565         return;
23566         
23567 //        if (!editor.toolbars || !editor.toolbars.length) {
23568 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23569 //        }
23570 //        
23571 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23572 //            editor.toolbars[i] = Roo.factory(
23573 //                    typeof(editor.toolbars[i]) == 'string' ?
23574 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23575 //                Roo.bootstrap.HtmlEditor);
23576 //            editor.toolbars[i].init(editor);
23577 //        }
23578     },
23579
23580      
23581     // private
23582     onRender : function(ct, position)
23583     {
23584        // Roo.log("Call onRender: " + this.xtype);
23585         var _t = this;
23586         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23587       
23588         this.wrap = this.inputEl().wrap({
23589             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23590         });
23591         
23592         this.editorcore.onRender(ct, position);
23593          
23594         if (this.resizable) {
23595             this.resizeEl = new Roo.Resizable(this.wrap, {
23596                 pinned : true,
23597                 wrap: true,
23598                 dynamic : true,
23599                 minHeight : this.height,
23600                 height: this.height,
23601                 handles : this.resizable,
23602                 width: this.width,
23603                 listeners : {
23604                     resize : function(r, w, h) {
23605                         _t.onResize(w,h); // -something
23606                     }
23607                 }
23608             });
23609             
23610         }
23611         this.createToolbar(this);
23612        
23613         
23614         if(!this.width && this.resizable){
23615             this.setSize(this.wrap.getSize());
23616         }
23617         if (this.resizeEl) {
23618             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23619             // should trigger onReize..
23620         }
23621         
23622     },
23623
23624     // private
23625     onResize : function(w, h)
23626     {
23627         Roo.log('resize: ' +w + ',' + h );
23628         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23629         var ew = false;
23630         var eh = false;
23631         
23632         if(this.inputEl() ){
23633             if(typeof w == 'number'){
23634                 var aw = w - this.wrap.getFrameWidth('lr');
23635                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23636                 ew = aw;
23637             }
23638             if(typeof h == 'number'){
23639                  var tbh = -11;  // fixme it needs to tool bar size!
23640                 for (var i =0; i < this.toolbars.length;i++) {
23641                     // fixme - ask toolbars for heights?
23642                     tbh += this.toolbars[i].el.getHeight();
23643                     //if (this.toolbars[i].footer) {
23644                     //    tbh += this.toolbars[i].footer.el.getHeight();
23645                     //}
23646                 }
23647               
23648                 
23649                 
23650                 
23651                 
23652                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23653                 ah -= 5; // knock a few pixes off for look..
23654                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23655                 var eh = ah;
23656             }
23657         }
23658         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23659         this.editorcore.onResize(ew,eh);
23660         
23661     },
23662
23663     /**
23664      * Toggles the editor between standard and source edit mode.
23665      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23666      */
23667     toggleSourceEdit : function(sourceEditMode)
23668     {
23669         this.editorcore.toggleSourceEdit(sourceEditMode);
23670         
23671         if(this.editorcore.sourceEditMode){
23672             Roo.log('editor - showing textarea');
23673             
23674 //            Roo.log('in');
23675 //            Roo.log(this.syncValue());
23676             this.syncValue();
23677             this.inputEl().removeClass(['hide', 'x-hidden']);
23678             this.inputEl().dom.removeAttribute('tabIndex');
23679             this.inputEl().focus();
23680         }else{
23681             Roo.log('editor - hiding textarea');
23682 //            Roo.log('out')
23683 //            Roo.log(this.pushValue()); 
23684             this.pushValue();
23685             
23686             this.inputEl().addClass(['hide', 'x-hidden']);
23687             this.inputEl().dom.setAttribute('tabIndex', -1);
23688             //this.deferFocus();
23689         }
23690          
23691         if(this.resizable){
23692             this.setSize(this.wrap.getSize());
23693         }
23694         
23695         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23696     },
23697  
23698     // private (for BoxComponent)
23699     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23700
23701     // private (for BoxComponent)
23702     getResizeEl : function(){
23703         return this.wrap;
23704     },
23705
23706     // private (for BoxComponent)
23707     getPositionEl : function(){
23708         return this.wrap;
23709     },
23710
23711     // private
23712     initEvents : function(){
23713         this.originalValue = this.getValue();
23714     },
23715
23716 //    /**
23717 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23718 //     * @method
23719 //     */
23720 //    markInvalid : Roo.emptyFn,
23721 //    /**
23722 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23723 //     * @method
23724 //     */
23725 //    clearInvalid : Roo.emptyFn,
23726
23727     setValue : function(v){
23728         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23729         this.editorcore.pushValue();
23730     },
23731
23732      
23733     // private
23734     deferFocus : function(){
23735         this.focus.defer(10, this);
23736     },
23737
23738     // doc'ed in Field
23739     focus : function(){
23740         this.editorcore.focus();
23741         
23742     },
23743       
23744
23745     // private
23746     onDestroy : function(){
23747         
23748         
23749         
23750         if(this.rendered){
23751             
23752             for (var i =0; i < this.toolbars.length;i++) {
23753                 // fixme - ask toolbars for heights?
23754                 this.toolbars[i].onDestroy();
23755             }
23756             
23757             this.wrap.dom.innerHTML = '';
23758             this.wrap.remove();
23759         }
23760     },
23761
23762     // private
23763     onFirstFocus : function(){
23764         //Roo.log("onFirstFocus");
23765         this.editorcore.onFirstFocus();
23766          for (var i =0; i < this.toolbars.length;i++) {
23767             this.toolbars[i].onFirstFocus();
23768         }
23769         
23770     },
23771     
23772     // private
23773     syncValue : function()
23774     {   
23775         this.editorcore.syncValue();
23776     },
23777     
23778     pushValue : function()
23779     {   
23780         this.editorcore.pushValue();
23781     }
23782      
23783     
23784     // hide stuff that is not compatible
23785     /**
23786      * @event blur
23787      * @hide
23788      */
23789     /**
23790      * @event change
23791      * @hide
23792      */
23793     /**
23794      * @event focus
23795      * @hide
23796      */
23797     /**
23798      * @event specialkey
23799      * @hide
23800      */
23801     /**
23802      * @cfg {String} fieldClass @hide
23803      */
23804     /**
23805      * @cfg {String} focusClass @hide
23806      */
23807     /**
23808      * @cfg {String} autoCreate @hide
23809      */
23810     /**
23811      * @cfg {String} inputType @hide
23812      */
23813     /**
23814      * @cfg {String} invalidClass @hide
23815      */
23816     /**
23817      * @cfg {String} invalidText @hide
23818      */
23819     /**
23820      * @cfg {String} msgFx @hide
23821      */
23822     /**
23823      * @cfg {String} validateOnBlur @hide
23824      */
23825 });
23826  
23827     
23828    
23829    
23830    
23831       
23832 Roo.namespace('Roo.bootstrap.htmleditor');
23833 /**
23834  * @class Roo.bootstrap.HtmlEditorToolbar1
23835  * Basic Toolbar
23836  * 
23837  * Usage:
23838  *
23839  new Roo.bootstrap.HtmlEditor({
23840     ....
23841     toolbars : [
23842         new Roo.bootstrap.HtmlEditorToolbar1({
23843             disable : { fonts: 1 , format: 1, ..., ... , ...],
23844             btns : [ .... ]
23845         })
23846     }
23847      
23848  * 
23849  * @cfg {Object} disable List of elements to disable..
23850  * @cfg {Array} btns List of additional buttons.
23851  * 
23852  * 
23853  * NEEDS Extra CSS? 
23854  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23855  */
23856  
23857 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23858 {
23859     
23860     Roo.apply(this, config);
23861     
23862     // default disabled, based on 'good practice'..
23863     this.disable = this.disable || {};
23864     Roo.applyIf(this.disable, {
23865         fontSize : true,
23866         colors : true,
23867         specialElements : true
23868     });
23869     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23870     
23871     this.editor = config.editor;
23872     this.editorcore = config.editor.editorcore;
23873     
23874     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23875     
23876     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23877     // dont call parent... till later.
23878 }
23879 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23880      
23881     bar : true,
23882     
23883     editor : false,
23884     editorcore : false,
23885     
23886     
23887     formats : [
23888         "p" ,  
23889         "h1","h2","h3","h4","h5","h6", 
23890         "pre", "code", 
23891         "abbr", "acronym", "address", "cite", "samp", "var",
23892         'div','span'
23893     ],
23894     
23895     onRender : function(ct, position)
23896     {
23897        // Roo.log("Call onRender: " + this.xtype);
23898         
23899        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23900        Roo.log(this.el);
23901        this.el.dom.style.marginBottom = '0';
23902        var _this = this;
23903        var editorcore = this.editorcore;
23904        var editor= this.editor;
23905        
23906        var children = [];
23907        var btn = function(id,cmd , toggle, handler, html){
23908        
23909             var  event = toggle ? 'toggle' : 'click';
23910        
23911             var a = {
23912                 size : 'sm',
23913                 xtype: 'Button',
23914                 xns: Roo.bootstrap,
23915                 glyphicon : id,
23916                 cmd : id || cmd,
23917                 enableToggle:toggle !== false,
23918                 html : html || '',
23919                 pressed : toggle ? false : null,
23920                 listeners : {}
23921             };
23922             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23923                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23924             };
23925             children.push(a);
23926             return a;
23927        }
23928        
23929     //    var cb_box = function...
23930         
23931         var style = {
23932                 xtype: 'Button',
23933                 size : 'sm',
23934                 xns: Roo.bootstrap,
23935                 glyphicon : 'font',
23936                 //html : 'submit'
23937                 menu : {
23938                     xtype: 'Menu',
23939                     xns: Roo.bootstrap,
23940                     items:  []
23941                 }
23942         };
23943         Roo.each(this.formats, function(f) {
23944             style.menu.items.push({
23945                 xtype :'MenuItem',
23946                 xns: Roo.bootstrap,
23947                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23948                 tagname : f,
23949                 listeners : {
23950                     click : function()
23951                     {
23952                         editorcore.insertTag(this.tagname);
23953                         editor.focus();
23954                     }
23955                 }
23956                 
23957             });
23958         });
23959         children.push(style);   
23960         
23961         btn('bold',false,true);
23962         btn('italic',false,true);
23963         btn('align-left', 'justifyleft',true);
23964         btn('align-center', 'justifycenter',true);
23965         btn('align-right' , 'justifyright',true);
23966         btn('link', false, false, function(btn) {
23967             //Roo.log("create link?");
23968             var url = prompt(this.createLinkText, this.defaultLinkValue);
23969             if(url && url != 'http:/'+'/'){
23970                 this.editorcore.relayCmd('createlink', url);
23971             }
23972         }),
23973         btn('list','insertunorderedlist',true);
23974         btn('pencil', false,true, function(btn){
23975                 Roo.log(this);
23976                 this.toggleSourceEdit(btn.pressed);
23977         });
23978         
23979         if (this.editor.btns.length > 0) {
23980             for (var i = 0; i<this.editor.btns.length; i++) {
23981                 children.push(this.editor.btns[i]);
23982             }
23983         }
23984         
23985         /*
23986         var cog = {
23987                 xtype: 'Button',
23988                 size : 'sm',
23989                 xns: Roo.bootstrap,
23990                 glyphicon : 'cog',
23991                 //html : 'submit'
23992                 menu : {
23993                     xtype: 'Menu',
23994                     xns: Roo.bootstrap,
23995                     items:  []
23996                 }
23997         };
23998         
23999         cog.menu.items.push({
24000             xtype :'MenuItem',
24001             xns: Roo.bootstrap,
24002             html : Clean styles,
24003             tagname : f,
24004             listeners : {
24005                 click : function()
24006                 {
24007                     editorcore.insertTag(this.tagname);
24008                     editor.focus();
24009                 }
24010             }
24011             
24012         });
24013        */
24014         
24015          
24016        this.xtype = 'NavSimplebar';
24017         
24018         for(var i=0;i< children.length;i++) {
24019             
24020             this.buttons.add(this.addxtypeChild(children[i]));
24021             
24022         }
24023         
24024         editor.on('editorevent', this.updateToolbar, this);
24025     },
24026     onBtnClick : function(id)
24027     {
24028        this.editorcore.relayCmd(id);
24029        this.editorcore.focus();
24030     },
24031     
24032     /**
24033      * Protected method that will not generally be called directly. It triggers
24034      * a toolbar update by reading the markup state of the current selection in the editor.
24035      */
24036     updateToolbar: function(){
24037
24038         if(!this.editorcore.activated){
24039             this.editor.onFirstFocus(); // is this neeed?
24040             return;
24041         }
24042
24043         var btns = this.buttons; 
24044         var doc = this.editorcore.doc;
24045         btns.get('bold').setActive(doc.queryCommandState('bold'));
24046         btns.get('italic').setActive(doc.queryCommandState('italic'));
24047         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24048         
24049         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24050         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24051         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24052         
24053         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24054         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24055          /*
24056         
24057         var ans = this.editorcore.getAllAncestors();
24058         if (this.formatCombo) {
24059             
24060             
24061             var store = this.formatCombo.store;
24062             this.formatCombo.setValue("");
24063             for (var i =0; i < ans.length;i++) {
24064                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24065                     // select it..
24066                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24067                     break;
24068                 }
24069             }
24070         }
24071         
24072         
24073         
24074         // hides menus... - so this cant be on a menu...
24075         Roo.bootstrap.MenuMgr.hideAll();
24076         */
24077         Roo.bootstrap.MenuMgr.hideAll();
24078         //this.editorsyncValue();
24079     },
24080     onFirstFocus: function() {
24081         this.buttons.each(function(item){
24082            item.enable();
24083         });
24084     },
24085     toggleSourceEdit : function(sourceEditMode){
24086         
24087           
24088         if(sourceEditMode){
24089             Roo.log("disabling buttons");
24090            this.buttons.each( function(item){
24091                 if(item.cmd != 'pencil'){
24092                     item.disable();
24093                 }
24094             });
24095           
24096         }else{
24097             Roo.log("enabling buttons");
24098             if(this.editorcore.initialized){
24099                 this.buttons.each( function(item){
24100                     item.enable();
24101                 });
24102             }
24103             
24104         }
24105         Roo.log("calling toggole on editor");
24106         // tell the editor that it's been pressed..
24107         this.editor.toggleSourceEdit(sourceEditMode);
24108        
24109     }
24110 });
24111
24112
24113
24114
24115
24116 /**
24117  * @class Roo.bootstrap.Table.AbstractSelectionModel
24118  * @extends Roo.util.Observable
24119  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24120  * implemented by descendant classes.  This class should not be directly instantiated.
24121  * @constructor
24122  */
24123 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24124     this.locked = false;
24125     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24126 };
24127
24128
24129 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24130     /** @ignore Called by the grid automatically. Do not call directly. */
24131     init : function(grid){
24132         this.grid = grid;
24133         this.initEvents();
24134     },
24135
24136     /**
24137      * Locks the selections.
24138      */
24139     lock : function(){
24140         this.locked = true;
24141     },
24142
24143     /**
24144      * Unlocks the selections.
24145      */
24146     unlock : function(){
24147         this.locked = false;
24148     },
24149
24150     /**
24151      * Returns true if the selections are locked.
24152      * @return {Boolean}
24153      */
24154     isLocked : function(){
24155         return this.locked;
24156     }
24157 });
24158 /**
24159  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24160  * @class Roo.bootstrap.Table.RowSelectionModel
24161  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24162  * It supports multiple selections and keyboard selection/navigation. 
24163  * @constructor
24164  * @param {Object} config
24165  */
24166
24167 Roo.bootstrap.Table.RowSelectionModel = function(config){
24168     Roo.apply(this, config);
24169     this.selections = new Roo.util.MixedCollection(false, function(o){
24170         return o.id;
24171     });
24172
24173     this.last = false;
24174     this.lastActive = false;
24175
24176     this.addEvents({
24177         /**
24178              * @event selectionchange
24179              * Fires when the selection changes
24180              * @param {SelectionModel} this
24181              */
24182             "selectionchange" : true,
24183         /**
24184              * @event afterselectionchange
24185              * Fires after the selection changes (eg. by key press or clicking)
24186              * @param {SelectionModel} this
24187              */
24188             "afterselectionchange" : true,
24189         /**
24190              * @event beforerowselect
24191              * Fires when a row is selected being selected, return false to cancel.
24192              * @param {SelectionModel} this
24193              * @param {Number} rowIndex The selected index
24194              * @param {Boolean} keepExisting False if other selections will be cleared
24195              */
24196             "beforerowselect" : true,
24197         /**
24198              * @event rowselect
24199              * Fires when a row is selected.
24200              * @param {SelectionModel} this
24201              * @param {Number} rowIndex The selected index
24202              * @param {Roo.data.Record} r The record
24203              */
24204             "rowselect" : true,
24205         /**
24206              * @event rowdeselect
24207              * Fires when a row is deselected.
24208              * @param {SelectionModel} this
24209              * @param {Number} rowIndex The selected index
24210              */
24211         "rowdeselect" : true
24212     });
24213     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24214     this.locked = false;
24215  };
24216
24217 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24218     /**
24219      * @cfg {Boolean} singleSelect
24220      * True to allow selection of only one row at a time (defaults to false)
24221      */
24222     singleSelect : false,
24223
24224     // private
24225     initEvents : function()
24226     {
24227
24228         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24229         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24230         //}else{ // allow click to work like normal
24231          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24232         //}
24233         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24234         this.grid.on("rowclick", this.handleMouseDown, this);
24235         
24236         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24237             "up" : function(e){
24238                 if(!e.shiftKey){
24239                     this.selectPrevious(e.shiftKey);
24240                 }else if(this.last !== false && this.lastActive !== false){
24241                     var last = this.last;
24242                     this.selectRange(this.last,  this.lastActive-1);
24243                     this.grid.getView().focusRow(this.lastActive);
24244                     if(last !== false){
24245                         this.last = last;
24246                     }
24247                 }else{
24248                     this.selectFirstRow();
24249                 }
24250                 this.fireEvent("afterselectionchange", this);
24251             },
24252             "down" : function(e){
24253                 if(!e.shiftKey){
24254                     this.selectNext(e.shiftKey);
24255                 }else if(this.last !== false && this.lastActive !== false){
24256                     var last = this.last;
24257                     this.selectRange(this.last,  this.lastActive+1);
24258                     this.grid.getView().focusRow(this.lastActive);
24259                     if(last !== false){
24260                         this.last = last;
24261                     }
24262                 }else{
24263                     this.selectFirstRow();
24264                 }
24265                 this.fireEvent("afterselectionchange", this);
24266             },
24267             scope: this
24268         });
24269         this.grid.store.on('load', function(){
24270             this.selections.clear();
24271         },this);
24272         /*
24273         var view = this.grid.view;
24274         view.on("refresh", this.onRefresh, this);
24275         view.on("rowupdated", this.onRowUpdated, this);
24276         view.on("rowremoved", this.onRemove, this);
24277         */
24278     },
24279
24280     // private
24281     onRefresh : function()
24282     {
24283         var ds = this.grid.store, i, v = this.grid.view;
24284         var s = this.selections;
24285         s.each(function(r){
24286             if((i = ds.indexOfId(r.id)) != -1){
24287                 v.onRowSelect(i);
24288             }else{
24289                 s.remove(r);
24290             }
24291         });
24292     },
24293
24294     // private
24295     onRemove : function(v, index, r){
24296         this.selections.remove(r);
24297     },
24298
24299     // private
24300     onRowUpdated : function(v, index, r){
24301         if(this.isSelected(r)){
24302             v.onRowSelect(index);
24303         }
24304     },
24305
24306     /**
24307      * Select records.
24308      * @param {Array} records The records to select
24309      * @param {Boolean} keepExisting (optional) True to keep existing selections
24310      */
24311     selectRecords : function(records, keepExisting)
24312     {
24313         if(!keepExisting){
24314             this.clearSelections();
24315         }
24316             var ds = this.grid.store;
24317         for(var i = 0, len = records.length; i < len; i++){
24318             this.selectRow(ds.indexOf(records[i]), true);
24319         }
24320     },
24321
24322     /**
24323      * Gets the number of selected rows.
24324      * @return {Number}
24325      */
24326     getCount : function(){
24327         return this.selections.length;
24328     },
24329
24330     /**
24331      * Selects the first row in the grid.
24332      */
24333     selectFirstRow : function(){
24334         this.selectRow(0);
24335     },
24336
24337     /**
24338      * Select the last row.
24339      * @param {Boolean} keepExisting (optional) True to keep existing selections
24340      */
24341     selectLastRow : function(keepExisting){
24342         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24343         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24344     },
24345
24346     /**
24347      * Selects the row immediately following the last selected row.
24348      * @param {Boolean} keepExisting (optional) True to keep existing selections
24349      */
24350     selectNext : function(keepExisting)
24351     {
24352             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24353             this.selectRow(this.last+1, keepExisting);
24354             this.grid.getView().focusRow(this.last);
24355         }
24356     },
24357
24358     /**
24359      * Selects the row that precedes the last selected row.
24360      * @param {Boolean} keepExisting (optional) True to keep existing selections
24361      */
24362     selectPrevious : function(keepExisting){
24363         if(this.last){
24364             this.selectRow(this.last-1, keepExisting);
24365             this.grid.getView().focusRow(this.last);
24366         }
24367     },
24368
24369     /**
24370      * Returns the selected records
24371      * @return {Array} Array of selected records
24372      */
24373     getSelections : function(){
24374         return [].concat(this.selections.items);
24375     },
24376
24377     /**
24378      * Returns the first selected record.
24379      * @return {Record}
24380      */
24381     getSelected : function(){
24382         return this.selections.itemAt(0);
24383     },
24384
24385
24386     /**
24387      * Clears all selections.
24388      */
24389     clearSelections : function(fast)
24390     {
24391         if(this.locked) {
24392             return;
24393         }
24394         if(fast !== true){
24395                 var ds = this.grid.store;
24396             var s = this.selections;
24397             s.each(function(r){
24398                 this.deselectRow(ds.indexOfId(r.id));
24399             }, this);
24400             s.clear();
24401         }else{
24402             this.selections.clear();
24403         }
24404         this.last = false;
24405     },
24406
24407
24408     /**
24409      * Selects all rows.
24410      */
24411     selectAll : function(){
24412         if(this.locked) {
24413             return;
24414         }
24415         this.selections.clear();
24416         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24417             this.selectRow(i, true);
24418         }
24419     },
24420
24421     /**
24422      * Returns True if there is a selection.
24423      * @return {Boolean}
24424      */
24425     hasSelection : function(){
24426         return this.selections.length > 0;
24427     },
24428
24429     /**
24430      * Returns True if the specified row is selected.
24431      * @param {Number/Record} record The record or index of the record to check
24432      * @return {Boolean}
24433      */
24434     isSelected : function(index){
24435             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24436         return (r && this.selections.key(r.id) ? true : false);
24437     },
24438
24439     /**
24440      * Returns True if the specified record id is selected.
24441      * @param {String} id The id of record to check
24442      * @return {Boolean}
24443      */
24444     isIdSelected : function(id){
24445         return (this.selections.key(id) ? true : false);
24446     },
24447
24448
24449     // private
24450     handleMouseDBClick : function(e, t){
24451         
24452     },
24453     // private
24454     handleMouseDown : function(e, t)
24455     {
24456             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24457         if(this.isLocked() || rowIndex < 0 ){
24458             return;
24459         };
24460         if(e.shiftKey && this.last !== false){
24461             var last = this.last;
24462             this.selectRange(last, rowIndex, e.ctrlKey);
24463             this.last = last; // reset the last
24464             t.focus();
24465     
24466         }else{
24467             var isSelected = this.isSelected(rowIndex);
24468             //Roo.log("select row:" + rowIndex);
24469             if(isSelected){
24470                 this.deselectRow(rowIndex);
24471             } else {
24472                         this.selectRow(rowIndex, true);
24473             }
24474     
24475             /*
24476                 if(e.button !== 0 && isSelected){
24477                 alert('rowIndex 2: ' + rowIndex);
24478                     view.focusRow(rowIndex);
24479                 }else if(e.ctrlKey && isSelected){
24480                     this.deselectRow(rowIndex);
24481                 }else if(!isSelected){
24482                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24483                     view.focusRow(rowIndex);
24484                 }
24485             */
24486         }
24487         this.fireEvent("afterselectionchange", this);
24488     },
24489     // private
24490     handleDragableRowClick :  function(grid, rowIndex, e) 
24491     {
24492         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24493             this.selectRow(rowIndex, false);
24494             grid.view.focusRow(rowIndex);
24495              this.fireEvent("afterselectionchange", this);
24496         }
24497     },
24498     
24499     /**
24500      * Selects multiple rows.
24501      * @param {Array} rows Array of the indexes of the row to select
24502      * @param {Boolean} keepExisting (optional) True to keep existing selections
24503      */
24504     selectRows : function(rows, keepExisting){
24505         if(!keepExisting){
24506             this.clearSelections();
24507         }
24508         for(var i = 0, len = rows.length; i < len; i++){
24509             this.selectRow(rows[i], true);
24510         }
24511     },
24512
24513     /**
24514      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24515      * @param {Number} startRow The index of the first row in the range
24516      * @param {Number} endRow The index of the last row in the range
24517      * @param {Boolean} keepExisting (optional) True to retain existing selections
24518      */
24519     selectRange : function(startRow, endRow, keepExisting){
24520         if(this.locked) {
24521             return;
24522         }
24523         if(!keepExisting){
24524             this.clearSelections();
24525         }
24526         if(startRow <= endRow){
24527             for(var i = startRow; i <= endRow; i++){
24528                 this.selectRow(i, true);
24529             }
24530         }else{
24531             for(var i = startRow; i >= endRow; i--){
24532                 this.selectRow(i, true);
24533             }
24534         }
24535     },
24536
24537     /**
24538      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24539      * @param {Number} startRow The index of the first row in the range
24540      * @param {Number} endRow The index of the last row in the range
24541      */
24542     deselectRange : function(startRow, endRow, preventViewNotify){
24543         if(this.locked) {
24544             return;
24545         }
24546         for(var i = startRow; i <= endRow; i++){
24547             this.deselectRow(i, preventViewNotify);
24548         }
24549     },
24550
24551     /**
24552      * Selects a row.
24553      * @param {Number} row The index of the row to select
24554      * @param {Boolean} keepExisting (optional) True to keep existing selections
24555      */
24556     selectRow : function(index, keepExisting, preventViewNotify)
24557     {
24558             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24559             return;
24560         }
24561         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24562             if(!keepExisting || this.singleSelect){
24563                 this.clearSelections();
24564             }
24565             
24566             var r = this.grid.store.getAt(index);
24567             //console.log('selectRow - record id :' + r.id);
24568             
24569             this.selections.add(r);
24570             this.last = this.lastActive = index;
24571             if(!preventViewNotify){
24572                 var proxy = new Roo.Element(
24573                                 this.grid.getRowDom(index)
24574                 );
24575                 proxy.addClass('bg-info info');
24576             }
24577             this.fireEvent("rowselect", this, index, r);
24578             this.fireEvent("selectionchange", this);
24579         }
24580     },
24581
24582     /**
24583      * Deselects a row.
24584      * @param {Number} row The index of the row to deselect
24585      */
24586     deselectRow : function(index, preventViewNotify)
24587     {
24588         if(this.locked) {
24589             return;
24590         }
24591         if(this.last == index){
24592             this.last = false;
24593         }
24594         if(this.lastActive == index){
24595             this.lastActive = false;
24596         }
24597         
24598         var r = this.grid.store.getAt(index);
24599         if (!r) {
24600             return;
24601         }
24602         
24603         this.selections.remove(r);
24604         //.console.log('deselectRow - record id :' + r.id);
24605         if(!preventViewNotify){
24606         
24607             var proxy = new Roo.Element(
24608                 this.grid.getRowDom(index)
24609             );
24610             proxy.removeClass('bg-info info');
24611         }
24612         this.fireEvent("rowdeselect", this, index);
24613         this.fireEvent("selectionchange", this);
24614     },
24615
24616     // private
24617     restoreLast : function(){
24618         if(this._last){
24619             this.last = this._last;
24620         }
24621     },
24622
24623     // private
24624     acceptsNav : function(row, col, cm){
24625         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24626     },
24627
24628     // private
24629     onEditorKey : function(field, e){
24630         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24631         if(k == e.TAB){
24632             e.stopEvent();
24633             ed.completeEdit();
24634             if(e.shiftKey){
24635                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24636             }else{
24637                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24638             }
24639         }else if(k == e.ENTER && !e.ctrlKey){
24640             e.stopEvent();
24641             ed.completeEdit();
24642             if(e.shiftKey){
24643                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24644             }else{
24645                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24646             }
24647         }else if(k == e.ESC){
24648             ed.cancelEdit();
24649         }
24650         if(newCell){
24651             g.startEditing(newCell[0], newCell[1]);
24652         }
24653     }
24654 });
24655 /*
24656  * Based on:
24657  * Ext JS Library 1.1.1
24658  * Copyright(c) 2006-2007, Ext JS, LLC.
24659  *
24660  * Originally Released Under LGPL - original licence link has changed is not relivant.
24661  *
24662  * Fork - LGPL
24663  * <script type="text/javascript">
24664  */
24665  
24666 /**
24667  * @class Roo.bootstrap.PagingToolbar
24668  * @extends Roo.bootstrap.NavSimplebar
24669  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24670  * @constructor
24671  * Create a new PagingToolbar
24672  * @param {Object} config The config object
24673  * @param {Roo.data.Store} store
24674  */
24675 Roo.bootstrap.PagingToolbar = function(config)
24676 {
24677     // old args format still supported... - xtype is prefered..
24678         // created from xtype...
24679     
24680     this.ds = config.dataSource;
24681     
24682     if (config.store && !this.ds) {
24683         this.store= Roo.factory(config.store, Roo.data);
24684         this.ds = this.store;
24685         this.ds.xmodule = this.xmodule || false;
24686     }
24687     
24688     this.toolbarItems = [];
24689     if (config.items) {
24690         this.toolbarItems = config.items;
24691     }
24692     
24693     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24694     
24695     this.cursor = 0;
24696     
24697     if (this.ds) { 
24698         this.bind(this.ds);
24699     }
24700     
24701     if (Roo.bootstrap.version == 4) {
24702         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24703     } else {
24704         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24705     }
24706     
24707 };
24708
24709 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24710     /**
24711      * @cfg {Roo.data.Store} dataSource
24712      * The underlying data store providing the paged data
24713      */
24714     /**
24715      * @cfg {String/HTMLElement/Element} container
24716      * container The id or element that will contain the toolbar
24717      */
24718     /**
24719      * @cfg {Boolean} displayInfo
24720      * True to display the displayMsg (defaults to false)
24721      */
24722     /**
24723      * @cfg {Number} pageSize
24724      * The number of records to display per page (defaults to 20)
24725      */
24726     pageSize: 20,
24727     /**
24728      * @cfg {String} displayMsg
24729      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24730      */
24731     displayMsg : 'Displaying {0} - {1} of {2}',
24732     /**
24733      * @cfg {String} emptyMsg
24734      * The message to display when no records are found (defaults to "No data to display")
24735      */
24736     emptyMsg : 'No data to display',
24737     /**
24738      * Customizable piece of the default paging text (defaults to "Page")
24739      * @type String
24740      */
24741     beforePageText : "Page",
24742     /**
24743      * Customizable piece of the default paging text (defaults to "of %0")
24744      * @type String
24745      */
24746     afterPageText : "of {0}",
24747     /**
24748      * Customizable piece of the default paging text (defaults to "First Page")
24749      * @type String
24750      */
24751     firstText : "First Page",
24752     /**
24753      * Customizable piece of the default paging text (defaults to "Previous Page")
24754      * @type String
24755      */
24756     prevText : "Previous Page",
24757     /**
24758      * Customizable piece of the default paging text (defaults to "Next Page")
24759      * @type String
24760      */
24761     nextText : "Next Page",
24762     /**
24763      * Customizable piece of the default paging text (defaults to "Last Page")
24764      * @type String
24765      */
24766     lastText : "Last Page",
24767     /**
24768      * Customizable piece of the default paging text (defaults to "Refresh")
24769      * @type String
24770      */
24771     refreshText : "Refresh",
24772
24773     buttons : false,
24774     // private
24775     onRender : function(ct, position) 
24776     {
24777         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24778         this.navgroup.parentId = this.id;
24779         this.navgroup.onRender(this.el, null);
24780         // add the buttons to the navgroup
24781         
24782         if(this.displayInfo){
24783             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24784             this.displayEl = this.el.select('.x-paging-info', true).first();
24785 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24786 //            this.displayEl = navel.el.select('span',true).first();
24787         }
24788         
24789         var _this = this;
24790         
24791         if(this.buttons){
24792             Roo.each(_this.buttons, function(e){ // this might need to use render????
24793                Roo.factory(e).render(_this.el);
24794             });
24795         }
24796             
24797         Roo.each(_this.toolbarItems, function(e) {
24798             _this.navgroup.addItem(e);
24799         });
24800         
24801         
24802         this.first = this.navgroup.addItem({
24803             tooltip: this.firstText,
24804             cls: "prev btn-outline-secondary",
24805             html : ' <i class="fa fa-step-backward"></i>',
24806             disabled: true,
24807             preventDefault: true,
24808             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24809         });
24810         
24811         this.prev =  this.navgroup.addItem({
24812             tooltip: this.prevText,
24813             cls: "prev btn-outline-secondary",
24814             html : ' <i class="fa fa-backward"></i>',
24815             disabled: true,
24816             preventDefault: true,
24817             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24818         });
24819     //this.addSeparator();
24820         
24821         
24822         var field = this.navgroup.addItem( {
24823             tagtype : 'span',
24824             cls : 'x-paging-position  btn-outline-secondary',
24825              disabled: true,
24826             html : this.beforePageText  +
24827                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24828                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24829          } ); //?? escaped?
24830         
24831         this.field = field.el.select('input', true).first();
24832         this.field.on("keydown", this.onPagingKeydown, this);
24833         this.field.on("focus", function(){this.dom.select();});
24834     
24835     
24836         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24837         //this.field.setHeight(18);
24838         //this.addSeparator();
24839         this.next = this.navgroup.addItem({
24840             tooltip: this.nextText,
24841             cls: "next btn-outline-secondary",
24842             html : ' <i class="fa fa-forward"></i>',
24843             disabled: true,
24844             preventDefault: true,
24845             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24846         });
24847         this.last = this.navgroup.addItem({
24848             tooltip: this.lastText,
24849             html : ' <i class="fa fa-step-forward"></i>',
24850             cls: "next btn-outline-secondary",
24851             disabled: true,
24852             preventDefault: true,
24853             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24854         });
24855     //this.addSeparator();
24856         this.loading = this.navgroup.addItem({
24857             tooltip: this.refreshText,
24858             cls: "btn-outline-secondary",
24859             html : ' <i class="fa fa-refresh"></i>',
24860             preventDefault: true,
24861             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24862         });
24863         
24864     },
24865
24866     // private
24867     updateInfo : function(){
24868         if(this.displayEl){
24869             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24870             var msg = count == 0 ?
24871                 this.emptyMsg :
24872                 String.format(
24873                     this.displayMsg,
24874                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24875                 );
24876             this.displayEl.update(msg);
24877         }
24878     },
24879
24880     // private
24881     onLoad : function(ds, r, o)
24882     {
24883         this.cursor = o.params.start ? o.params.start : 0;
24884         
24885         var d = this.getPageData(),
24886             ap = d.activePage,
24887             ps = d.pages;
24888         
24889         
24890         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24891         this.field.dom.value = ap;
24892         this.first.setDisabled(ap == 1);
24893         this.prev.setDisabled(ap == 1);
24894         this.next.setDisabled(ap == ps);
24895         this.last.setDisabled(ap == ps);
24896         this.loading.enable();
24897         this.updateInfo();
24898     },
24899
24900     // private
24901     getPageData : function(){
24902         var total = this.ds.getTotalCount();
24903         return {
24904             total : total,
24905             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24906             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24907         };
24908     },
24909
24910     // private
24911     onLoadError : function(){
24912         this.loading.enable();
24913     },
24914
24915     // private
24916     onPagingKeydown : function(e){
24917         var k = e.getKey();
24918         var d = this.getPageData();
24919         if(k == e.RETURN){
24920             var v = this.field.dom.value, pageNum;
24921             if(!v || isNaN(pageNum = parseInt(v, 10))){
24922                 this.field.dom.value = d.activePage;
24923                 return;
24924             }
24925             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24926             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24927             e.stopEvent();
24928         }
24929         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))
24930         {
24931           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24932           this.field.dom.value = pageNum;
24933           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24934           e.stopEvent();
24935         }
24936         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24937         {
24938           var v = this.field.dom.value, pageNum; 
24939           var increment = (e.shiftKey) ? 10 : 1;
24940           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24941                 increment *= -1;
24942           }
24943           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24944             this.field.dom.value = d.activePage;
24945             return;
24946           }
24947           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24948           {
24949             this.field.dom.value = parseInt(v, 10) + increment;
24950             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24951             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24952           }
24953           e.stopEvent();
24954         }
24955     },
24956
24957     // private
24958     beforeLoad : function(){
24959         if(this.loading){
24960             this.loading.disable();
24961         }
24962     },
24963
24964     // private
24965     onClick : function(which){
24966         
24967         var ds = this.ds;
24968         if (!ds) {
24969             return;
24970         }
24971         
24972         switch(which){
24973             case "first":
24974                 ds.load({params:{start: 0, limit: this.pageSize}});
24975             break;
24976             case "prev":
24977                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24978             break;
24979             case "next":
24980                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24981             break;
24982             case "last":
24983                 var total = ds.getTotalCount();
24984                 var extra = total % this.pageSize;
24985                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24986                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24987             break;
24988             case "refresh":
24989                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24990             break;
24991         }
24992     },
24993
24994     /**
24995      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24996      * @param {Roo.data.Store} store The data store to unbind
24997      */
24998     unbind : function(ds){
24999         ds.un("beforeload", this.beforeLoad, this);
25000         ds.un("load", this.onLoad, this);
25001         ds.un("loadexception", this.onLoadError, this);
25002         ds.un("remove", this.updateInfo, this);
25003         ds.un("add", this.updateInfo, this);
25004         this.ds = undefined;
25005     },
25006
25007     /**
25008      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25009      * @param {Roo.data.Store} store The data store to bind
25010      */
25011     bind : function(ds){
25012         ds.on("beforeload", this.beforeLoad, this);
25013         ds.on("load", this.onLoad, this);
25014         ds.on("loadexception", this.onLoadError, this);
25015         ds.on("remove", this.updateInfo, this);
25016         ds.on("add", this.updateInfo, this);
25017         this.ds = ds;
25018     }
25019 });/*
25020  * - LGPL
25021  *
25022  * element
25023  * 
25024  */
25025
25026 /**
25027  * @class Roo.bootstrap.MessageBar
25028  * @extends Roo.bootstrap.Component
25029  * Bootstrap MessageBar class
25030  * @cfg {String} html contents of the MessageBar
25031  * @cfg {String} weight (info | success | warning | danger) default info
25032  * @cfg {String} beforeClass insert the bar before the given class
25033  * @cfg {Boolean} closable (true | false) default false
25034  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25035  * 
25036  * @constructor
25037  * Create a new Element
25038  * @param {Object} config The config object
25039  */
25040
25041 Roo.bootstrap.MessageBar = function(config){
25042     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25043 };
25044
25045 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25046     
25047     html: '',
25048     weight: 'info',
25049     closable: false,
25050     fixed: false,
25051     beforeClass: 'bootstrap-sticky-wrap',
25052     
25053     getAutoCreate : function(){
25054         
25055         var cfg = {
25056             tag: 'div',
25057             cls: 'alert alert-dismissable alert-' + this.weight,
25058             cn: [
25059                 {
25060                     tag: 'span',
25061                     cls: 'message',
25062                     html: this.html || ''
25063                 }
25064             ]
25065         };
25066         
25067         if(this.fixed){
25068             cfg.cls += ' alert-messages-fixed';
25069         }
25070         
25071         if(this.closable){
25072             cfg.cn.push({
25073                 tag: 'button',
25074                 cls: 'close',
25075                 html: 'x'
25076             });
25077         }
25078         
25079         return cfg;
25080     },
25081     
25082     onRender : function(ct, position)
25083     {
25084         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25085         
25086         if(!this.el){
25087             var cfg = Roo.apply({},  this.getAutoCreate());
25088             cfg.id = Roo.id();
25089             
25090             if (this.cls) {
25091                 cfg.cls += ' ' + this.cls;
25092             }
25093             if (this.style) {
25094                 cfg.style = this.style;
25095             }
25096             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25097             
25098             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25099         }
25100         
25101         this.el.select('>button.close').on('click', this.hide, this);
25102         
25103     },
25104     
25105     show : function()
25106     {
25107         if (!this.rendered) {
25108             this.render();
25109         }
25110         
25111         this.el.show();
25112         
25113         this.fireEvent('show', this);
25114         
25115     },
25116     
25117     hide : function()
25118     {
25119         if (!this.rendered) {
25120             this.render();
25121         }
25122         
25123         this.el.hide();
25124         
25125         this.fireEvent('hide', this);
25126     },
25127     
25128     update : function()
25129     {
25130 //        var e = this.el.dom.firstChild;
25131 //        
25132 //        if(this.closable){
25133 //            e = e.nextSibling;
25134 //        }
25135 //        
25136 //        e.data = this.html || '';
25137
25138         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25139     }
25140    
25141 });
25142
25143  
25144
25145      /*
25146  * - LGPL
25147  *
25148  * Graph
25149  * 
25150  */
25151
25152
25153 /**
25154  * @class Roo.bootstrap.Graph
25155  * @extends Roo.bootstrap.Component
25156  * Bootstrap Graph class
25157 > Prameters
25158  -sm {number} sm 4
25159  -md {number} md 5
25160  @cfg {String} graphtype  bar | vbar | pie
25161  @cfg {number} g_x coodinator | centre x (pie)
25162  @cfg {number} g_y coodinator | centre y (pie)
25163  @cfg {number} g_r radius (pie)
25164  @cfg {number} g_height height of the chart (respected by all elements in the set)
25165  @cfg {number} g_width width of the chart (respected by all elements in the set)
25166  @cfg {Object} title The title of the chart
25167     
25168  -{Array}  values
25169  -opts (object) options for the chart 
25170      o {
25171      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25172      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25173      o vgutter (number)
25174      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.
25175      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25176      o to
25177      o stretch (boolean)
25178      o }
25179  -opts (object) options for the pie
25180      o{
25181      o cut
25182      o startAngle (number)
25183      o endAngle (number)
25184      } 
25185  *
25186  * @constructor
25187  * Create a new Input
25188  * @param {Object} config The config object
25189  */
25190
25191 Roo.bootstrap.Graph = function(config){
25192     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25193     
25194     this.addEvents({
25195         // img events
25196         /**
25197          * @event click
25198          * The img click event for the img.
25199          * @param {Roo.EventObject} e
25200          */
25201         "click" : true
25202     });
25203 };
25204
25205 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25206     
25207     sm: 4,
25208     md: 5,
25209     graphtype: 'bar',
25210     g_height: 250,
25211     g_width: 400,
25212     g_x: 50,
25213     g_y: 50,
25214     g_r: 30,
25215     opts:{
25216         //g_colors: this.colors,
25217         g_type: 'soft',
25218         g_gutter: '20%'
25219
25220     },
25221     title : false,
25222
25223     getAutoCreate : function(){
25224         
25225         var cfg = {
25226             tag: 'div',
25227             html : null
25228         };
25229         
25230         
25231         return  cfg;
25232     },
25233
25234     onRender : function(ct,position){
25235         
25236         
25237         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25238         
25239         if (typeof(Raphael) == 'undefined') {
25240             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25241             return;
25242         }
25243         
25244         this.raphael = Raphael(this.el.dom);
25245         
25246                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25247                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25248                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25249                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25250                 /*
25251                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25252                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25253                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25254                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25255                 
25256                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25257                 r.barchart(330, 10, 300, 220, data1);
25258                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25259                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25260                 */
25261                 
25262                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25263                 // r.barchart(30, 30, 560, 250,  xdata, {
25264                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25265                 //     axis : "0 0 1 1",
25266                 //     axisxlabels :  xdata
25267                 //     //yvalues : cols,
25268                    
25269                 // });
25270 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25271 //        
25272 //        this.load(null,xdata,{
25273 //                axis : "0 0 1 1",
25274 //                axisxlabels :  xdata
25275 //                });
25276
25277     },
25278
25279     load : function(graphtype,xdata,opts)
25280     {
25281         this.raphael.clear();
25282         if(!graphtype) {
25283             graphtype = this.graphtype;
25284         }
25285         if(!opts){
25286             opts = this.opts;
25287         }
25288         var r = this.raphael,
25289             fin = function () {
25290                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25291             },
25292             fout = function () {
25293                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25294             },
25295             pfin = function() {
25296                 this.sector.stop();
25297                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25298
25299                 if (this.label) {
25300                     this.label[0].stop();
25301                     this.label[0].attr({ r: 7.5 });
25302                     this.label[1].attr({ "font-weight": 800 });
25303                 }
25304             },
25305             pfout = function() {
25306                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25307
25308                 if (this.label) {
25309                     this.label[0].animate({ r: 5 }, 500, "bounce");
25310                     this.label[1].attr({ "font-weight": 400 });
25311                 }
25312             };
25313
25314         switch(graphtype){
25315             case 'bar':
25316                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25317                 break;
25318             case 'hbar':
25319                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25320                 break;
25321             case 'pie':
25322 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25323 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25324 //            
25325                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25326                 
25327                 break;
25328
25329         }
25330         
25331         if(this.title){
25332             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25333         }
25334         
25335     },
25336     
25337     setTitle: function(o)
25338     {
25339         this.title = o;
25340     },
25341     
25342     initEvents: function() {
25343         
25344         if(!this.href){
25345             this.el.on('click', this.onClick, this);
25346         }
25347     },
25348     
25349     onClick : function(e)
25350     {
25351         Roo.log('img onclick');
25352         this.fireEvent('click', this, e);
25353     }
25354    
25355 });
25356
25357  
25358 /*
25359  * - LGPL
25360  *
25361  * numberBox
25362  * 
25363  */
25364 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25365
25366 /**
25367  * @class Roo.bootstrap.dash.NumberBox
25368  * @extends Roo.bootstrap.Component
25369  * Bootstrap NumberBox class
25370  * @cfg {String} headline Box headline
25371  * @cfg {String} content Box content
25372  * @cfg {String} icon Box icon
25373  * @cfg {String} footer Footer text
25374  * @cfg {String} fhref Footer href
25375  * 
25376  * @constructor
25377  * Create a new NumberBox
25378  * @param {Object} config The config object
25379  */
25380
25381
25382 Roo.bootstrap.dash.NumberBox = function(config){
25383     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25384     
25385 };
25386
25387 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25388     
25389     headline : '',
25390     content : '',
25391     icon : '',
25392     footer : '',
25393     fhref : '',
25394     ficon : '',
25395     
25396     getAutoCreate : function(){
25397         
25398         var cfg = {
25399             tag : 'div',
25400             cls : 'small-box ',
25401             cn : [
25402                 {
25403                     tag : 'div',
25404                     cls : 'inner',
25405                     cn :[
25406                         {
25407                             tag : 'h3',
25408                             cls : 'roo-headline',
25409                             html : this.headline
25410                         },
25411                         {
25412                             tag : 'p',
25413                             cls : 'roo-content',
25414                             html : this.content
25415                         }
25416                     ]
25417                 }
25418             ]
25419         };
25420         
25421         if(this.icon){
25422             cfg.cn.push({
25423                 tag : 'div',
25424                 cls : 'icon',
25425                 cn :[
25426                     {
25427                         tag : 'i',
25428                         cls : 'ion ' + this.icon
25429                     }
25430                 ]
25431             });
25432         }
25433         
25434         if(this.footer){
25435             var footer = {
25436                 tag : 'a',
25437                 cls : 'small-box-footer',
25438                 href : this.fhref || '#',
25439                 html : this.footer
25440             };
25441             
25442             cfg.cn.push(footer);
25443             
25444         }
25445         
25446         return  cfg;
25447     },
25448
25449     onRender : function(ct,position){
25450         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25451
25452
25453        
25454                 
25455     },
25456
25457     setHeadline: function (value)
25458     {
25459         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25460     },
25461     
25462     setFooter: function (value, href)
25463     {
25464         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25465         
25466         if(href){
25467             this.el.select('a.small-box-footer',true).first().attr('href', href);
25468         }
25469         
25470     },
25471
25472     setContent: function (value)
25473     {
25474         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25475     },
25476
25477     initEvents: function() 
25478     {   
25479         
25480     }
25481     
25482 });
25483
25484  
25485 /*
25486  * - LGPL
25487  *
25488  * TabBox
25489  * 
25490  */
25491 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25492
25493 /**
25494  * @class Roo.bootstrap.dash.TabBox
25495  * @extends Roo.bootstrap.Component
25496  * Bootstrap TabBox class
25497  * @cfg {String} title Title of the TabBox
25498  * @cfg {String} icon Icon of the TabBox
25499  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25500  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25501  * 
25502  * @constructor
25503  * Create a new TabBox
25504  * @param {Object} config The config object
25505  */
25506
25507
25508 Roo.bootstrap.dash.TabBox = function(config){
25509     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25510     this.addEvents({
25511         // raw events
25512         /**
25513          * @event addpane
25514          * When a pane is added
25515          * @param {Roo.bootstrap.dash.TabPane} pane
25516          */
25517         "addpane" : true,
25518         /**
25519          * @event activatepane
25520          * When a pane is activated
25521          * @param {Roo.bootstrap.dash.TabPane} pane
25522          */
25523         "activatepane" : true
25524         
25525          
25526     });
25527     
25528     this.panes = [];
25529 };
25530
25531 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25532
25533     title : '',
25534     icon : false,
25535     showtabs : true,
25536     tabScrollable : false,
25537     
25538     getChildContainer : function()
25539     {
25540         return this.el.select('.tab-content', true).first();
25541     },
25542     
25543     getAutoCreate : function(){
25544         
25545         var header = {
25546             tag: 'li',
25547             cls: 'pull-left header',
25548             html: this.title,
25549             cn : []
25550         };
25551         
25552         if(this.icon){
25553             header.cn.push({
25554                 tag: 'i',
25555                 cls: 'fa ' + this.icon
25556             });
25557         }
25558         
25559         var h = {
25560             tag: 'ul',
25561             cls: 'nav nav-tabs pull-right',
25562             cn: [
25563                 header
25564             ]
25565         };
25566         
25567         if(this.tabScrollable){
25568             h = {
25569                 tag: 'div',
25570                 cls: 'tab-header',
25571                 cn: [
25572                     {
25573                         tag: 'ul',
25574                         cls: 'nav nav-tabs pull-right',
25575                         cn: [
25576                             header
25577                         ]
25578                     }
25579                 ]
25580             };
25581         }
25582         
25583         var cfg = {
25584             tag: 'div',
25585             cls: 'nav-tabs-custom',
25586             cn: [
25587                 h,
25588                 {
25589                     tag: 'div',
25590                     cls: 'tab-content no-padding',
25591                     cn: []
25592                 }
25593             ]
25594         };
25595
25596         return  cfg;
25597     },
25598     initEvents : function()
25599     {
25600         //Roo.log('add add pane handler');
25601         this.on('addpane', this.onAddPane, this);
25602     },
25603      /**
25604      * Updates the box title
25605      * @param {String} html to set the title to.
25606      */
25607     setTitle : function(value)
25608     {
25609         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25610     },
25611     onAddPane : function(pane)
25612     {
25613         this.panes.push(pane);
25614         //Roo.log('addpane');
25615         //Roo.log(pane);
25616         // tabs are rendere left to right..
25617         if(!this.showtabs){
25618             return;
25619         }
25620         
25621         var ctr = this.el.select('.nav-tabs', true).first();
25622          
25623          
25624         var existing = ctr.select('.nav-tab',true);
25625         var qty = existing.getCount();;
25626         
25627         
25628         var tab = ctr.createChild({
25629             tag : 'li',
25630             cls : 'nav-tab' + (qty ? '' : ' active'),
25631             cn : [
25632                 {
25633                     tag : 'a',
25634                     href:'#',
25635                     html : pane.title
25636                 }
25637             ]
25638         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25639         pane.tab = tab;
25640         
25641         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25642         if (!qty) {
25643             pane.el.addClass('active');
25644         }
25645         
25646                 
25647     },
25648     onTabClick : function(ev,un,ob,pane)
25649     {
25650         //Roo.log('tab - prev default');
25651         ev.preventDefault();
25652         
25653         
25654         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25655         pane.tab.addClass('active');
25656         //Roo.log(pane.title);
25657         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25658         // technically we should have a deactivate event.. but maybe add later.
25659         // and it should not de-activate the selected tab...
25660         this.fireEvent('activatepane', pane);
25661         pane.el.addClass('active');
25662         pane.fireEvent('activate');
25663         
25664         
25665     },
25666     
25667     getActivePane : function()
25668     {
25669         var r = false;
25670         Roo.each(this.panes, function(p) {
25671             if(p.el.hasClass('active')){
25672                 r = p;
25673                 return false;
25674             }
25675             
25676             return;
25677         });
25678         
25679         return r;
25680     }
25681     
25682     
25683 });
25684
25685  
25686 /*
25687  * - LGPL
25688  *
25689  * Tab pane
25690  * 
25691  */
25692 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25693 /**
25694  * @class Roo.bootstrap.TabPane
25695  * @extends Roo.bootstrap.Component
25696  * Bootstrap TabPane class
25697  * @cfg {Boolean} active (false | true) Default false
25698  * @cfg {String} title title of panel
25699
25700  * 
25701  * @constructor
25702  * Create a new TabPane
25703  * @param {Object} config The config object
25704  */
25705
25706 Roo.bootstrap.dash.TabPane = function(config){
25707     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25708     
25709     this.addEvents({
25710         // raw events
25711         /**
25712          * @event activate
25713          * When a pane is activated
25714          * @param {Roo.bootstrap.dash.TabPane} pane
25715          */
25716         "activate" : true
25717          
25718     });
25719 };
25720
25721 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25722     
25723     active : false,
25724     title : '',
25725     
25726     // the tabBox that this is attached to.
25727     tab : false,
25728      
25729     getAutoCreate : function() 
25730     {
25731         var cfg = {
25732             tag: 'div',
25733             cls: 'tab-pane'
25734         };
25735         
25736         if(this.active){
25737             cfg.cls += ' active';
25738         }
25739         
25740         return cfg;
25741     },
25742     initEvents  : function()
25743     {
25744         //Roo.log('trigger add pane handler');
25745         this.parent().fireEvent('addpane', this)
25746     },
25747     
25748      /**
25749      * Updates the tab title 
25750      * @param {String} html to set the title to.
25751      */
25752     setTitle: function(str)
25753     {
25754         if (!this.tab) {
25755             return;
25756         }
25757         this.title = str;
25758         this.tab.select('a', true).first().dom.innerHTML = str;
25759         
25760     }
25761     
25762     
25763     
25764 });
25765
25766  
25767
25768
25769  /*
25770  * - LGPL
25771  *
25772  * menu
25773  * 
25774  */
25775 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25776
25777 /**
25778  * @class Roo.bootstrap.menu.Menu
25779  * @extends Roo.bootstrap.Component
25780  * Bootstrap Menu class - container for Menu
25781  * @cfg {String} html Text of the menu
25782  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25783  * @cfg {String} icon Font awesome icon
25784  * @cfg {String} pos Menu align to (top | bottom) default bottom
25785  * 
25786  * 
25787  * @constructor
25788  * Create a new Menu
25789  * @param {Object} config The config object
25790  */
25791
25792
25793 Roo.bootstrap.menu.Menu = function(config){
25794     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25795     
25796     this.addEvents({
25797         /**
25798          * @event beforeshow
25799          * Fires before this menu is displayed
25800          * @param {Roo.bootstrap.menu.Menu} this
25801          */
25802         beforeshow : true,
25803         /**
25804          * @event beforehide
25805          * Fires before this menu is hidden
25806          * @param {Roo.bootstrap.menu.Menu} this
25807          */
25808         beforehide : true,
25809         /**
25810          * @event show
25811          * Fires after this menu is displayed
25812          * @param {Roo.bootstrap.menu.Menu} this
25813          */
25814         show : true,
25815         /**
25816          * @event hide
25817          * Fires after this menu is hidden
25818          * @param {Roo.bootstrap.menu.Menu} this
25819          */
25820         hide : true,
25821         /**
25822          * @event click
25823          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25824          * @param {Roo.bootstrap.menu.Menu} this
25825          * @param {Roo.EventObject} e
25826          */
25827         click : true
25828     });
25829     
25830 };
25831
25832 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25833     
25834     submenu : false,
25835     html : '',
25836     weight : 'default',
25837     icon : false,
25838     pos : 'bottom',
25839     
25840     
25841     getChildContainer : function() {
25842         if(this.isSubMenu){
25843             return this.el;
25844         }
25845         
25846         return this.el.select('ul.dropdown-menu', true).first();  
25847     },
25848     
25849     getAutoCreate : function()
25850     {
25851         var text = [
25852             {
25853                 tag : 'span',
25854                 cls : 'roo-menu-text',
25855                 html : this.html
25856             }
25857         ];
25858         
25859         if(this.icon){
25860             text.unshift({
25861                 tag : 'i',
25862                 cls : 'fa ' + this.icon
25863             })
25864         }
25865         
25866         
25867         var cfg = {
25868             tag : 'div',
25869             cls : 'btn-group',
25870             cn : [
25871                 {
25872                     tag : 'button',
25873                     cls : 'dropdown-button btn btn-' + this.weight,
25874                     cn : text
25875                 },
25876                 {
25877                     tag : 'button',
25878                     cls : 'dropdown-toggle btn btn-' + this.weight,
25879                     cn : [
25880                         {
25881                             tag : 'span',
25882                             cls : 'caret'
25883                         }
25884                     ]
25885                 },
25886                 {
25887                     tag : 'ul',
25888                     cls : 'dropdown-menu'
25889                 }
25890             ]
25891             
25892         };
25893         
25894         if(this.pos == 'top'){
25895             cfg.cls += ' dropup';
25896         }
25897         
25898         if(this.isSubMenu){
25899             cfg = {
25900                 tag : 'ul',
25901                 cls : 'dropdown-menu'
25902             }
25903         }
25904         
25905         return cfg;
25906     },
25907     
25908     onRender : function(ct, position)
25909     {
25910         this.isSubMenu = ct.hasClass('dropdown-submenu');
25911         
25912         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25913     },
25914     
25915     initEvents : function() 
25916     {
25917         if(this.isSubMenu){
25918             return;
25919         }
25920         
25921         this.hidden = true;
25922         
25923         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25924         this.triggerEl.on('click', this.onTriggerPress, this);
25925         
25926         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25927         this.buttonEl.on('click', this.onClick, this);
25928         
25929     },
25930     
25931     list : function()
25932     {
25933         if(this.isSubMenu){
25934             return this.el;
25935         }
25936         
25937         return this.el.select('ul.dropdown-menu', true).first();
25938     },
25939     
25940     onClick : function(e)
25941     {
25942         this.fireEvent("click", this, e);
25943     },
25944     
25945     onTriggerPress  : function(e)
25946     {   
25947         if (this.isVisible()) {
25948             this.hide();
25949         } else {
25950             this.show();
25951         }
25952     },
25953     
25954     isVisible : function(){
25955         return !this.hidden;
25956     },
25957     
25958     show : function()
25959     {
25960         this.fireEvent("beforeshow", this);
25961         
25962         this.hidden = false;
25963         this.el.addClass('open');
25964         
25965         Roo.get(document).on("mouseup", this.onMouseUp, this);
25966         
25967         this.fireEvent("show", this);
25968         
25969         
25970     },
25971     
25972     hide : function()
25973     {
25974         this.fireEvent("beforehide", this);
25975         
25976         this.hidden = true;
25977         this.el.removeClass('open');
25978         
25979         Roo.get(document).un("mouseup", this.onMouseUp);
25980         
25981         this.fireEvent("hide", this);
25982     },
25983     
25984     onMouseUp : function()
25985     {
25986         this.hide();
25987     }
25988     
25989 });
25990
25991  
25992  /*
25993  * - LGPL
25994  *
25995  * menu item
25996  * 
25997  */
25998 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25999
26000 /**
26001  * @class Roo.bootstrap.menu.Item
26002  * @extends Roo.bootstrap.Component
26003  * Bootstrap MenuItem class
26004  * @cfg {Boolean} submenu (true | false) default false
26005  * @cfg {String} html text of the item
26006  * @cfg {String} href the link
26007  * @cfg {Boolean} disable (true | false) default false
26008  * @cfg {Boolean} preventDefault (true | false) default true
26009  * @cfg {String} icon Font awesome icon
26010  * @cfg {String} pos Submenu align to (left | right) default right 
26011  * 
26012  * 
26013  * @constructor
26014  * Create a new Item
26015  * @param {Object} config The config object
26016  */
26017
26018
26019 Roo.bootstrap.menu.Item = function(config){
26020     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26021     this.addEvents({
26022         /**
26023          * @event mouseover
26024          * Fires when the mouse is hovering over this menu
26025          * @param {Roo.bootstrap.menu.Item} this
26026          * @param {Roo.EventObject} e
26027          */
26028         mouseover : true,
26029         /**
26030          * @event mouseout
26031          * Fires when the mouse exits this menu
26032          * @param {Roo.bootstrap.menu.Item} this
26033          * @param {Roo.EventObject} e
26034          */
26035         mouseout : true,
26036         // raw events
26037         /**
26038          * @event click
26039          * The raw click event for the entire grid.
26040          * @param {Roo.EventObject} e
26041          */
26042         click : true
26043     });
26044 };
26045
26046 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26047     
26048     submenu : false,
26049     href : '',
26050     html : '',
26051     preventDefault: true,
26052     disable : false,
26053     icon : false,
26054     pos : 'right',
26055     
26056     getAutoCreate : function()
26057     {
26058         var text = [
26059             {
26060                 tag : 'span',
26061                 cls : 'roo-menu-item-text',
26062                 html : this.html
26063             }
26064         ];
26065         
26066         if(this.icon){
26067             text.unshift({
26068                 tag : 'i',
26069                 cls : 'fa ' + this.icon
26070             })
26071         }
26072         
26073         var cfg = {
26074             tag : 'li',
26075             cn : [
26076                 {
26077                     tag : 'a',
26078                     href : this.href || '#',
26079                     cn : text
26080                 }
26081             ]
26082         };
26083         
26084         if(this.disable){
26085             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26086         }
26087         
26088         if(this.submenu){
26089             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26090             
26091             if(this.pos == 'left'){
26092                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26093             }
26094         }
26095         
26096         return cfg;
26097     },
26098     
26099     initEvents : function() 
26100     {
26101         this.el.on('mouseover', this.onMouseOver, this);
26102         this.el.on('mouseout', this.onMouseOut, this);
26103         
26104         this.el.select('a', true).first().on('click', this.onClick, this);
26105         
26106     },
26107     
26108     onClick : function(e)
26109     {
26110         if(this.preventDefault){
26111             e.preventDefault();
26112         }
26113         
26114         this.fireEvent("click", this, e);
26115     },
26116     
26117     onMouseOver : function(e)
26118     {
26119         if(this.submenu && this.pos == 'left'){
26120             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26121         }
26122         
26123         this.fireEvent("mouseover", this, e);
26124     },
26125     
26126     onMouseOut : function(e)
26127     {
26128         this.fireEvent("mouseout", this, e);
26129     }
26130 });
26131
26132  
26133
26134  /*
26135  * - LGPL
26136  *
26137  * menu separator
26138  * 
26139  */
26140 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26141
26142 /**
26143  * @class Roo.bootstrap.menu.Separator
26144  * @extends Roo.bootstrap.Component
26145  * Bootstrap Separator class
26146  * 
26147  * @constructor
26148  * Create a new Separator
26149  * @param {Object} config The config object
26150  */
26151
26152
26153 Roo.bootstrap.menu.Separator = function(config){
26154     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26155 };
26156
26157 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26158     
26159     getAutoCreate : function(){
26160         var cfg = {
26161             tag : 'li',
26162             cls: 'divider'
26163         };
26164         
26165         return cfg;
26166     }
26167    
26168 });
26169
26170  
26171
26172  /*
26173  * - LGPL
26174  *
26175  * Tooltip
26176  * 
26177  */
26178
26179 /**
26180  * @class Roo.bootstrap.Tooltip
26181  * Bootstrap Tooltip class
26182  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26183  * to determine which dom element triggers the tooltip.
26184  * 
26185  * It needs to add support for additional attributes like tooltip-position
26186  * 
26187  * @constructor
26188  * Create a new Toolti
26189  * @param {Object} config The config object
26190  */
26191
26192 Roo.bootstrap.Tooltip = function(config){
26193     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26194     
26195     this.alignment = Roo.bootstrap.Tooltip.alignment;
26196     
26197     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26198         this.alignment = config.alignment;
26199     }
26200     
26201 };
26202
26203 Roo.apply(Roo.bootstrap.Tooltip, {
26204     /**
26205      * @function init initialize tooltip monitoring.
26206      * @static
26207      */
26208     currentEl : false,
26209     currentTip : false,
26210     currentRegion : false,
26211     
26212     //  init : delay?
26213     
26214     init : function()
26215     {
26216         Roo.get(document).on('mouseover', this.enter ,this);
26217         Roo.get(document).on('mouseout', this.leave, this);
26218          
26219         
26220         this.currentTip = new Roo.bootstrap.Tooltip();
26221     },
26222     
26223     enter : function(ev)
26224     {
26225         var dom = ev.getTarget();
26226         
26227         //Roo.log(['enter',dom]);
26228         var el = Roo.fly(dom);
26229         if (this.currentEl) {
26230             //Roo.log(dom);
26231             //Roo.log(this.currentEl);
26232             //Roo.log(this.currentEl.contains(dom));
26233             if (this.currentEl == el) {
26234                 return;
26235             }
26236             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26237                 return;
26238             }
26239
26240         }
26241         
26242         if (this.currentTip.el) {
26243             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26244         }    
26245         //Roo.log(ev);
26246         
26247         if(!el || el.dom == document){
26248             return;
26249         }
26250         
26251         var bindEl = el;
26252         
26253         // you can not look for children, as if el is the body.. then everythign is the child..
26254         if (!el.attr('tooltip')) { //
26255             if (!el.select("[tooltip]").elements.length) {
26256                 return;
26257             }
26258             // is the mouse over this child...?
26259             bindEl = el.select("[tooltip]").first();
26260             var xy = ev.getXY();
26261             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26262                 //Roo.log("not in region.");
26263                 return;
26264             }
26265             //Roo.log("child element over..");
26266             
26267         }
26268         this.currentEl = bindEl;
26269         this.currentTip.bind(bindEl);
26270         this.currentRegion = Roo.lib.Region.getRegion(dom);
26271         this.currentTip.enter();
26272         
26273     },
26274     leave : function(ev)
26275     {
26276         var dom = ev.getTarget();
26277         //Roo.log(['leave',dom]);
26278         if (!this.currentEl) {
26279             return;
26280         }
26281         
26282         
26283         if (dom != this.currentEl.dom) {
26284             return;
26285         }
26286         var xy = ev.getXY();
26287         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26288             return;
26289         }
26290         // only activate leave if mouse cursor is outside... bounding box..
26291         
26292         
26293         
26294         
26295         if (this.currentTip) {
26296             this.currentTip.leave();
26297         }
26298         //Roo.log('clear currentEl');
26299         this.currentEl = false;
26300         
26301         
26302     },
26303     alignment : {
26304         'left' : ['r-l', [-2,0], 'right'],
26305         'right' : ['l-r', [2,0], 'left'],
26306         'bottom' : ['t-b', [0,2], 'top'],
26307         'top' : [ 'b-t', [0,-2], 'bottom']
26308     }
26309     
26310 });
26311
26312
26313 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26314     
26315     
26316     bindEl : false,
26317     
26318     delay : null, // can be { show : 300 , hide: 500}
26319     
26320     timeout : null,
26321     
26322     hoverState : null, //???
26323     
26324     placement : 'bottom', 
26325     
26326     alignment : false,
26327     
26328     getAutoCreate : function(){
26329     
26330         var cfg = {
26331            cls : 'tooltip',
26332            role : 'tooltip',
26333            cn : [
26334                 {
26335                     cls : 'tooltip-arrow'
26336                 },
26337                 {
26338                     cls : 'tooltip-inner'
26339                 }
26340            ]
26341         };
26342         
26343         return cfg;
26344     },
26345     bind : function(el)
26346     {
26347         this.bindEl = el;
26348     },
26349       
26350     
26351     enter : function () {
26352        
26353         if (this.timeout != null) {
26354             clearTimeout(this.timeout);
26355         }
26356         
26357         this.hoverState = 'in';
26358          //Roo.log("enter - show");
26359         if (!this.delay || !this.delay.show) {
26360             this.show();
26361             return;
26362         }
26363         var _t = this;
26364         this.timeout = setTimeout(function () {
26365             if (_t.hoverState == 'in') {
26366                 _t.show();
26367             }
26368         }, this.delay.show);
26369     },
26370     leave : function()
26371     {
26372         clearTimeout(this.timeout);
26373     
26374         this.hoverState = 'out';
26375          if (!this.delay || !this.delay.hide) {
26376             this.hide();
26377             return;
26378         }
26379        
26380         var _t = this;
26381         this.timeout = setTimeout(function () {
26382             //Roo.log("leave - timeout");
26383             
26384             if (_t.hoverState == 'out') {
26385                 _t.hide();
26386                 Roo.bootstrap.Tooltip.currentEl = false;
26387             }
26388         }, delay);
26389     },
26390     
26391     show : function (msg)
26392     {
26393         if (!this.el) {
26394             this.render(document.body);
26395         }
26396         // set content.
26397         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26398         
26399         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26400         
26401         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26402         
26403         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26404         
26405         var placement = typeof this.placement == 'function' ?
26406             this.placement.call(this, this.el, on_el) :
26407             this.placement;
26408             
26409         var autoToken = /\s?auto?\s?/i;
26410         var autoPlace = autoToken.test(placement);
26411         if (autoPlace) {
26412             placement = placement.replace(autoToken, '') || 'top';
26413         }
26414         
26415         //this.el.detach()
26416         //this.el.setXY([0,0]);
26417         this.el.show();
26418         //this.el.dom.style.display='block';
26419         
26420         //this.el.appendTo(on_el);
26421         
26422         var p = this.getPosition();
26423         var box = this.el.getBox();
26424         
26425         if (autoPlace) {
26426             // fixme..
26427         }
26428         
26429         var align = this.alignment[placement];
26430         
26431         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26432         
26433         if(placement == 'top' || placement == 'bottom'){
26434             if(xy[0] < 0){
26435                 placement = 'right';
26436             }
26437             
26438             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26439                 placement = 'left';
26440             }
26441             
26442             var scroll = Roo.select('body', true).first().getScroll();
26443             
26444             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26445                 placement = 'top';
26446             }
26447             
26448             align = this.alignment[placement];
26449         }
26450         
26451         this.el.alignTo(this.bindEl, align[0],align[1]);
26452         //var arrow = this.el.select('.arrow',true).first();
26453         //arrow.set(align[2], 
26454         
26455         this.el.addClass(placement);
26456         
26457         this.el.addClass('in fade');
26458         
26459         this.hoverState = null;
26460         
26461         if (this.el.hasClass('fade')) {
26462             // fade it?
26463         }
26464         
26465     },
26466     hide : function()
26467     {
26468          
26469         if (!this.el) {
26470             return;
26471         }
26472         //this.el.setXY([0,0]);
26473         this.el.removeClass('in');
26474         //this.el.hide();
26475         
26476     }
26477     
26478 });
26479  
26480
26481  /*
26482  * - LGPL
26483  *
26484  * Location Picker
26485  * 
26486  */
26487
26488 /**
26489  * @class Roo.bootstrap.LocationPicker
26490  * @extends Roo.bootstrap.Component
26491  * Bootstrap LocationPicker class
26492  * @cfg {Number} latitude Position when init default 0
26493  * @cfg {Number} longitude Position when init default 0
26494  * @cfg {Number} zoom default 15
26495  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26496  * @cfg {Boolean} mapTypeControl default false
26497  * @cfg {Boolean} disableDoubleClickZoom default false
26498  * @cfg {Boolean} scrollwheel default true
26499  * @cfg {Boolean} streetViewControl default false
26500  * @cfg {Number} radius default 0
26501  * @cfg {String} locationName
26502  * @cfg {Boolean} draggable default true
26503  * @cfg {Boolean} enableAutocomplete default false
26504  * @cfg {Boolean} enableReverseGeocode default true
26505  * @cfg {String} markerTitle
26506  * 
26507  * @constructor
26508  * Create a new LocationPicker
26509  * @param {Object} config The config object
26510  */
26511
26512
26513 Roo.bootstrap.LocationPicker = function(config){
26514     
26515     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26516     
26517     this.addEvents({
26518         /**
26519          * @event initial
26520          * Fires when the picker initialized.
26521          * @param {Roo.bootstrap.LocationPicker} this
26522          * @param {Google Location} location
26523          */
26524         initial : true,
26525         /**
26526          * @event positionchanged
26527          * Fires when the picker position changed.
26528          * @param {Roo.bootstrap.LocationPicker} this
26529          * @param {Google Location} location
26530          */
26531         positionchanged : true,
26532         /**
26533          * @event resize
26534          * Fires when the map resize.
26535          * @param {Roo.bootstrap.LocationPicker} this
26536          */
26537         resize : true,
26538         /**
26539          * @event show
26540          * Fires when the map show.
26541          * @param {Roo.bootstrap.LocationPicker} this
26542          */
26543         show : true,
26544         /**
26545          * @event hide
26546          * Fires when the map hide.
26547          * @param {Roo.bootstrap.LocationPicker} this
26548          */
26549         hide : true,
26550         /**
26551          * @event mapClick
26552          * Fires when click the map.
26553          * @param {Roo.bootstrap.LocationPicker} this
26554          * @param {Map event} e
26555          */
26556         mapClick : true,
26557         /**
26558          * @event mapRightClick
26559          * Fires when right click the map.
26560          * @param {Roo.bootstrap.LocationPicker} this
26561          * @param {Map event} e
26562          */
26563         mapRightClick : true,
26564         /**
26565          * @event markerClick
26566          * Fires when click the marker.
26567          * @param {Roo.bootstrap.LocationPicker} this
26568          * @param {Map event} e
26569          */
26570         markerClick : true,
26571         /**
26572          * @event markerRightClick
26573          * Fires when right click the marker.
26574          * @param {Roo.bootstrap.LocationPicker} this
26575          * @param {Map event} e
26576          */
26577         markerRightClick : true,
26578         /**
26579          * @event OverlayViewDraw
26580          * Fires when OverlayView Draw
26581          * @param {Roo.bootstrap.LocationPicker} this
26582          */
26583         OverlayViewDraw : true,
26584         /**
26585          * @event OverlayViewOnAdd
26586          * Fires when OverlayView Draw
26587          * @param {Roo.bootstrap.LocationPicker} this
26588          */
26589         OverlayViewOnAdd : true,
26590         /**
26591          * @event OverlayViewOnRemove
26592          * Fires when OverlayView Draw
26593          * @param {Roo.bootstrap.LocationPicker} this
26594          */
26595         OverlayViewOnRemove : true,
26596         /**
26597          * @event OverlayViewShow
26598          * Fires when OverlayView Draw
26599          * @param {Roo.bootstrap.LocationPicker} this
26600          * @param {Pixel} cpx
26601          */
26602         OverlayViewShow : true,
26603         /**
26604          * @event OverlayViewHide
26605          * Fires when OverlayView Draw
26606          * @param {Roo.bootstrap.LocationPicker} this
26607          */
26608         OverlayViewHide : true,
26609         /**
26610          * @event loadexception
26611          * Fires when load google lib failed.
26612          * @param {Roo.bootstrap.LocationPicker} this
26613          */
26614         loadexception : true
26615     });
26616         
26617 };
26618
26619 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26620     
26621     gMapContext: false,
26622     
26623     latitude: 0,
26624     longitude: 0,
26625     zoom: 15,
26626     mapTypeId: false,
26627     mapTypeControl: false,
26628     disableDoubleClickZoom: false,
26629     scrollwheel: true,
26630     streetViewControl: false,
26631     radius: 0,
26632     locationName: '',
26633     draggable: true,
26634     enableAutocomplete: false,
26635     enableReverseGeocode: true,
26636     markerTitle: '',
26637     
26638     getAutoCreate: function()
26639     {
26640
26641         var cfg = {
26642             tag: 'div',
26643             cls: 'roo-location-picker'
26644         };
26645         
26646         return cfg
26647     },
26648     
26649     initEvents: function(ct, position)
26650     {       
26651         if(!this.el.getWidth() || this.isApplied()){
26652             return;
26653         }
26654         
26655         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26656         
26657         this.initial();
26658     },
26659     
26660     initial: function()
26661     {
26662         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26663             this.fireEvent('loadexception', this);
26664             return;
26665         }
26666         
26667         if(!this.mapTypeId){
26668             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26669         }
26670         
26671         this.gMapContext = this.GMapContext();
26672         
26673         this.initOverlayView();
26674         
26675         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26676         
26677         var _this = this;
26678                 
26679         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26680             _this.setPosition(_this.gMapContext.marker.position);
26681         });
26682         
26683         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26684             _this.fireEvent('mapClick', this, event);
26685             
26686         });
26687
26688         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26689             _this.fireEvent('mapRightClick', this, event);
26690             
26691         });
26692         
26693         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26694             _this.fireEvent('markerClick', this, event);
26695             
26696         });
26697
26698         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26699             _this.fireEvent('markerRightClick', this, event);
26700             
26701         });
26702         
26703         this.setPosition(this.gMapContext.location);
26704         
26705         this.fireEvent('initial', this, this.gMapContext.location);
26706     },
26707     
26708     initOverlayView: function()
26709     {
26710         var _this = this;
26711         
26712         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26713             
26714             draw: function()
26715             {
26716                 _this.fireEvent('OverlayViewDraw', _this);
26717             },
26718             
26719             onAdd: function()
26720             {
26721                 _this.fireEvent('OverlayViewOnAdd', _this);
26722             },
26723             
26724             onRemove: function()
26725             {
26726                 _this.fireEvent('OverlayViewOnRemove', _this);
26727             },
26728             
26729             show: function(cpx)
26730             {
26731                 _this.fireEvent('OverlayViewShow', _this, cpx);
26732             },
26733             
26734             hide: function()
26735             {
26736                 _this.fireEvent('OverlayViewHide', _this);
26737             }
26738             
26739         });
26740     },
26741     
26742     fromLatLngToContainerPixel: function(event)
26743     {
26744         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26745     },
26746     
26747     isApplied: function() 
26748     {
26749         return this.getGmapContext() == false ? false : true;
26750     },
26751     
26752     getGmapContext: function() 
26753     {
26754         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26755     },
26756     
26757     GMapContext: function() 
26758     {
26759         var position = new google.maps.LatLng(this.latitude, this.longitude);
26760         
26761         var _map = new google.maps.Map(this.el.dom, {
26762             center: position,
26763             zoom: this.zoom,
26764             mapTypeId: this.mapTypeId,
26765             mapTypeControl: this.mapTypeControl,
26766             disableDoubleClickZoom: this.disableDoubleClickZoom,
26767             scrollwheel: this.scrollwheel,
26768             streetViewControl: this.streetViewControl,
26769             locationName: this.locationName,
26770             draggable: this.draggable,
26771             enableAutocomplete: this.enableAutocomplete,
26772             enableReverseGeocode: this.enableReverseGeocode
26773         });
26774         
26775         var _marker = new google.maps.Marker({
26776             position: position,
26777             map: _map,
26778             title: this.markerTitle,
26779             draggable: this.draggable
26780         });
26781         
26782         return {
26783             map: _map,
26784             marker: _marker,
26785             circle: null,
26786             location: position,
26787             radius: this.radius,
26788             locationName: this.locationName,
26789             addressComponents: {
26790                 formatted_address: null,
26791                 addressLine1: null,
26792                 addressLine2: null,
26793                 streetName: null,
26794                 streetNumber: null,
26795                 city: null,
26796                 district: null,
26797                 state: null,
26798                 stateOrProvince: null
26799             },
26800             settings: this,
26801             domContainer: this.el.dom,
26802             geodecoder: new google.maps.Geocoder()
26803         };
26804     },
26805     
26806     drawCircle: function(center, radius, options) 
26807     {
26808         if (this.gMapContext.circle != null) {
26809             this.gMapContext.circle.setMap(null);
26810         }
26811         if (radius > 0) {
26812             radius *= 1;
26813             options = Roo.apply({}, options, {
26814                 strokeColor: "#0000FF",
26815                 strokeOpacity: .35,
26816                 strokeWeight: 2,
26817                 fillColor: "#0000FF",
26818                 fillOpacity: .2
26819             });
26820             
26821             options.map = this.gMapContext.map;
26822             options.radius = radius;
26823             options.center = center;
26824             this.gMapContext.circle = new google.maps.Circle(options);
26825             return this.gMapContext.circle;
26826         }
26827         
26828         return null;
26829     },
26830     
26831     setPosition: function(location) 
26832     {
26833         this.gMapContext.location = location;
26834         this.gMapContext.marker.setPosition(location);
26835         this.gMapContext.map.panTo(location);
26836         this.drawCircle(location, this.gMapContext.radius, {});
26837         
26838         var _this = this;
26839         
26840         if (this.gMapContext.settings.enableReverseGeocode) {
26841             this.gMapContext.geodecoder.geocode({
26842                 latLng: this.gMapContext.location
26843             }, function(results, status) {
26844                 
26845                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26846                     _this.gMapContext.locationName = results[0].formatted_address;
26847                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26848                     
26849                     _this.fireEvent('positionchanged', this, location);
26850                 }
26851             });
26852             
26853             return;
26854         }
26855         
26856         this.fireEvent('positionchanged', this, location);
26857     },
26858     
26859     resize: function()
26860     {
26861         google.maps.event.trigger(this.gMapContext.map, "resize");
26862         
26863         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26864         
26865         this.fireEvent('resize', this);
26866     },
26867     
26868     setPositionByLatLng: function(latitude, longitude)
26869     {
26870         this.setPosition(new google.maps.LatLng(latitude, longitude));
26871     },
26872     
26873     getCurrentPosition: function() 
26874     {
26875         return {
26876             latitude: this.gMapContext.location.lat(),
26877             longitude: this.gMapContext.location.lng()
26878         };
26879     },
26880     
26881     getAddressName: function() 
26882     {
26883         return this.gMapContext.locationName;
26884     },
26885     
26886     getAddressComponents: function() 
26887     {
26888         return this.gMapContext.addressComponents;
26889     },
26890     
26891     address_component_from_google_geocode: function(address_components) 
26892     {
26893         var result = {};
26894         
26895         for (var i = 0; i < address_components.length; i++) {
26896             var component = address_components[i];
26897             if (component.types.indexOf("postal_code") >= 0) {
26898                 result.postalCode = component.short_name;
26899             } else if (component.types.indexOf("street_number") >= 0) {
26900                 result.streetNumber = component.short_name;
26901             } else if (component.types.indexOf("route") >= 0) {
26902                 result.streetName = component.short_name;
26903             } else if (component.types.indexOf("neighborhood") >= 0) {
26904                 result.city = component.short_name;
26905             } else if (component.types.indexOf("locality") >= 0) {
26906                 result.city = component.short_name;
26907             } else if (component.types.indexOf("sublocality") >= 0) {
26908                 result.district = component.short_name;
26909             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26910                 result.stateOrProvince = component.short_name;
26911             } else if (component.types.indexOf("country") >= 0) {
26912                 result.country = component.short_name;
26913             }
26914         }
26915         
26916         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26917         result.addressLine2 = "";
26918         return result;
26919     },
26920     
26921     setZoomLevel: function(zoom)
26922     {
26923         this.gMapContext.map.setZoom(zoom);
26924     },
26925     
26926     show: function()
26927     {
26928         if(!this.el){
26929             return;
26930         }
26931         
26932         this.el.show();
26933         
26934         this.resize();
26935         
26936         this.fireEvent('show', this);
26937     },
26938     
26939     hide: function()
26940     {
26941         if(!this.el){
26942             return;
26943         }
26944         
26945         this.el.hide();
26946         
26947         this.fireEvent('hide', this);
26948     }
26949     
26950 });
26951
26952 Roo.apply(Roo.bootstrap.LocationPicker, {
26953     
26954     OverlayView : function(map, options)
26955     {
26956         options = options || {};
26957         
26958         this.setMap(map);
26959     }
26960     
26961     
26962 });/*
26963  * - LGPL
26964  *
26965  * Alert
26966  * 
26967  */
26968
26969 /**
26970  * @class Roo.bootstrap.Alert
26971  * @extends Roo.bootstrap.Component
26972  * Bootstrap Alert class
26973  * @cfg {String} title The title of alert
26974  * @cfg {String} html The content of alert
26975  * @cfg {String} weight (  success | info | warning | danger )
26976  * @cfg {String} faicon font-awesomeicon
26977  * 
26978  * @constructor
26979  * Create a new alert
26980  * @param {Object} config The config object
26981  */
26982
26983
26984 Roo.bootstrap.Alert = function(config){
26985     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26986     
26987 };
26988
26989 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26990     
26991     title: '',
26992     html: '',
26993     weight: false,
26994     faicon: false,
26995     
26996     getAutoCreate : function()
26997     {
26998         
26999         var cfg = {
27000             tag : 'div',
27001             cls : 'alert',
27002             cn : [
27003                 {
27004                     tag : 'i',
27005                     cls : 'roo-alert-icon'
27006                     
27007                 },
27008                 {
27009                     tag : 'b',
27010                     cls : 'roo-alert-title',
27011                     html : this.title
27012                 },
27013                 {
27014                     tag : 'span',
27015                     cls : 'roo-alert-text',
27016                     html : this.html
27017                 }
27018             ]
27019         };
27020         
27021         if(this.faicon){
27022             cfg.cn[0].cls += ' fa ' + this.faicon;
27023         }
27024         
27025         if(this.weight){
27026             cfg.cls += ' alert-' + this.weight;
27027         }
27028         
27029         return cfg;
27030     },
27031     
27032     initEvents: function() 
27033     {
27034         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27035     },
27036     
27037     setTitle : function(str)
27038     {
27039         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27040     },
27041     
27042     setText : function(str)
27043     {
27044         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27045     },
27046     
27047     setWeight : function(weight)
27048     {
27049         if(this.weight){
27050             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27051         }
27052         
27053         this.weight = weight;
27054         
27055         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27056     },
27057     
27058     setIcon : function(icon)
27059     {
27060         if(this.faicon){
27061             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27062         }
27063         
27064         this.faicon = icon;
27065         
27066         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27067     },
27068     
27069     hide: function() 
27070     {
27071         this.el.hide();   
27072     },
27073     
27074     show: function() 
27075     {  
27076         this.el.show();   
27077     }
27078     
27079 });
27080
27081  
27082 /*
27083 * Licence: LGPL
27084 */
27085
27086 /**
27087  * @class Roo.bootstrap.UploadCropbox
27088  * @extends Roo.bootstrap.Component
27089  * Bootstrap UploadCropbox class
27090  * @cfg {String} emptyText show when image has been loaded
27091  * @cfg {String} rotateNotify show when image too small to rotate
27092  * @cfg {Number} errorTimeout default 3000
27093  * @cfg {Number} minWidth default 300
27094  * @cfg {Number} minHeight default 300
27095  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27096  * @cfg {Boolean} isDocument (true|false) default false
27097  * @cfg {String} url action url
27098  * @cfg {String} paramName default 'imageUpload'
27099  * @cfg {String} method default POST
27100  * @cfg {Boolean} loadMask (true|false) default true
27101  * @cfg {Boolean} loadingText default 'Loading...'
27102  * 
27103  * @constructor
27104  * Create a new UploadCropbox
27105  * @param {Object} config The config object
27106  */
27107
27108 Roo.bootstrap.UploadCropbox = function(config){
27109     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27110     
27111     this.addEvents({
27112         /**
27113          * @event beforeselectfile
27114          * Fire before select file
27115          * @param {Roo.bootstrap.UploadCropbox} this
27116          */
27117         "beforeselectfile" : true,
27118         /**
27119          * @event initial
27120          * Fire after initEvent
27121          * @param {Roo.bootstrap.UploadCropbox} this
27122          */
27123         "initial" : true,
27124         /**
27125          * @event crop
27126          * Fire after initEvent
27127          * @param {Roo.bootstrap.UploadCropbox} this
27128          * @param {String} data
27129          */
27130         "crop" : true,
27131         /**
27132          * @event prepare
27133          * Fire when preparing the file data
27134          * @param {Roo.bootstrap.UploadCropbox} this
27135          * @param {Object} file
27136          */
27137         "prepare" : true,
27138         /**
27139          * @event exception
27140          * Fire when get exception
27141          * @param {Roo.bootstrap.UploadCropbox} this
27142          * @param {XMLHttpRequest} xhr
27143          */
27144         "exception" : true,
27145         /**
27146          * @event beforeloadcanvas
27147          * Fire before load the canvas
27148          * @param {Roo.bootstrap.UploadCropbox} this
27149          * @param {String} src
27150          */
27151         "beforeloadcanvas" : true,
27152         /**
27153          * @event trash
27154          * Fire when trash image
27155          * @param {Roo.bootstrap.UploadCropbox} this
27156          */
27157         "trash" : true,
27158         /**
27159          * @event download
27160          * Fire when download the image
27161          * @param {Roo.bootstrap.UploadCropbox} this
27162          */
27163         "download" : true,
27164         /**
27165          * @event footerbuttonclick
27166          * Fire when footerbuttonclick
27167          * @param {Roo.bootstrap.UploadCropbox} this
27168          * @param {String} type
27169          */
27170         "footerbuttonclick" : true,
27171         /**
27172          * @event resize
27173          * Fire when resize
27174          * @param {Roo.bootstrap.UploadCropbox} this
27175          */
27176         "resize" : true,
27177         /**
27178          * @event rotate
27179          * Fire when rotate the image
27180          * @param {Roo.bootstrap.UploadCropbox} this
27181          * @param {String} pos
27182          */
27183         "rotate" : true,
27184         /**
27185          * @event inspect
27186          * Fire when inspect the file
27187          * @param {Roo.bootstrap.UploadCropbox} this
27188          * @param {Object} file
27189          */
27190         "inspect" : true,
27191         /**
27192          * @event upload
27193          * Fire when xhr upload the file
27194          * @param {Roo.bootstrap.UploadCropbox} this
27195          * @param {Object} data
27196          */
27197         "upload" : true,
27198         /**
27199          * @event arrange
27200          * Fire when arrange the file data
27201          * @param {Roo.bootstrap.UploadCropbox} this
27202          * @param {Object} formData
27203          */
27204         "arrange" : true
27205     });
27206     
27207     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27208 };
27209
27210 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27211     
27212     emptyText : 'Click to upload image',
27213     rotateNotify : 'Image is too small to rotate',
27214     errorTimeout : 3000,
27215     scale : 0,
27216     baseScale : 1,
27217     rotate : 0,
27218     dragable : false,
27219     pinching : false,
27220     mouseX : 0,
27221     mouseY : 0,
27222     cropData : false,
27223     minWidth : 300,
27224     minHeight : 300,
27225     file : false,
27226     exif : {},
27227     baseRotate : 1,
27228     cropType : 'image/jpeg',
27229     buttons : false,
27230     canvasLoaded : false,
27231     isDocument : false,
27232     method : 'POST',
27233     paramName : 'imageUpload',
27234     loadMask : true,
27235     loadingText : 'Loading...',
27236     maskEl : false,
27237     
27238     getAutoCreate : function()
27239     {
27240         var cfg = {
27241             tag : 'div',
27242             cls : 'roo-upload-cropbox',
27243             cn : [
27244                 {
27245                     tag : 'input',
27246                     cls : 'roo-upload-cropbox-selector',
27247                     type : 'file'
27248                 },
27249                 {
27250                     tag : 'div',
27251                     cls : 'roo-upload-cropbox-body',
27252                     style : 'cursor:pointer',
27253                     cn : [
27254                         {
27255                             tag : 'div',
27256                             cls : 'roo-upload-cropbox-preview'
27257                         },
27258                         {
27259                             tag : 'div',
27260                             cls : 'roo-upload-cropbox-thumb'
27261                         },
27262                         {
27263                             tag : 'div',
27264                             cls : 'roo-upload-cropbox-empty-notify',
27265                             html : this.emptyText
27266                         },
27267                         {
27268                             tag : 'div',
27269                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27270                             html : this.rotateNotify
27271                         }
27272                     ]
27273                 },
27274                 {
27275                     tag : 'div',
27276                     cls : 'roo-upload-cropbox-footer',
27277                     cn : {
27278                         tag : 'div',
27279                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27280                         cn : []
27281                     }
27282                 }
27283             ]
27284         };
27285         
27286         return cfg;
27287     },
27288     
27289     onRender : function(ct, position)
27290     {
27291         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27292         
27293         if (this.buttons.length) {
27294             
27295             Roo.each(this.buttons, function(bb) {
27296                 
27297                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27298                 
27299                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27300                 
27301             }, this);
27302         }
27303         
27304         if(this.loadMask){
27305             this.maskEl = this.el;
27306         }
27307     },
27308     
27309     initEvents : function()
27310     {
27311         this.urlAPI = (window.createObjectURL && window) || 
27312                                 (window.URL && URL.revokeObjectURL && URL) || 
27313                                 (window.webkitURL && webkitURL);
27314                         
27315         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27316         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27317         
27318         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27319         this.selectorEl.hide();
27320         
27321         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27322         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27323         
27324         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27325         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27326         this.thumbEl.hide();
27327         
27328         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27329         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27330         
27331         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27332         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27333         this.errorEl.hide();
27334         
27335         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27336         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27337         this.footerEl.hide();
27338         
27339         this.setThumbBoxSize();
27340         
27341         this.bind();
27342         
27343         this.resize();
27344         
27345         this.fireEvent('initial', this);
27346     },
27347
27348     bind : function()
27349     {
27350         var _this = this;
27351         
27352         window.addEventListener("resize", function() { _this.resize(); } );
27353         
27354         this.bodyEl.on('click', this.beforeSelectFile, this);
27355         
27356         if(Roo.isTouch){
27357             this.bodyEl.on('touchstart', this.onTouchStart, this);
27358             this.bodyEl.on('touchmove', this.onTouchMove, this);
27359             this.bodyEl.on('touchend', this.onTouchEnd, this);
27360         }
27361         
27362         if(!Roo.isTouch){
27363             this.bodyEl.on('mousedown', this.onMouseDown, this);
27364             this.bodyEl.on('mousemove', this.onMouseMove, this);
27365             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27366             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27367             Roo.get(document).on('mouseup', this.onMouseUp, this);
27368         }
27369         
27370         this.selectorEl.on('change', this.onFileSelected, this);
27371     },
27372     
27373     reset : function()
27374     {    
27375         this.scale = 0;
27376         this.baseScale = 1;
27377         this.rotate = 0;
27378         this.baseRotate = 1;
27379         this.dragable = false;
27380         this.pinching = false;
27381         this.mouseX = 0;
27382         this.mouseY = 0;
27383         this.cropData = false;
27384         this.notifyEl.dom.innerHTML = this.emptyText;
27385         
27386         this.selectorEl.dom.value = '';
27387         
27388     },
27389     
27390     resize : function()
27391     {
27392         if(this.fireEvent('resize', this) != false){
27393             this.setThumbBoxPosition();
27394             this.setCanvasPosition();
27395         }
27396     },
27397     
27398     onFooterButtonClick : function(e, el, o, type)
27399     {
27400         switch (type) {
27401             case 'rotate-left' :
27402                 this.onRotateLeft(e);
27403                 break;
27404             case 'rotate-right' :
27405                 this.onRotateRight(e);
27406                 break;
27407             case 'picture' :
27408                 this.beforeSelectFile(e);
27409                 break;
27410             case 'trash' :
27411                 this.trash(e);
27412                 break;
27413             case 'crop' :
27414                 this.crop(e);
27415                 break;
27416             case 'download' :
27417                 this.download(e);
27418                 break;
27419             default :
27420                 break;
27421         }
27422         
27423         this.fireEvent('footerbuttonclick', this, type);
27424     },
27425     
27426     beforeSelectFile : function(e)
27427     {
27428         e.preventDefault();
27429         
27430         if(this.fireEvent('beforeselectfile', this) != false){
27431             this.selectorEl.dom.click();
27432         }
27433     },
27434     
27435     onFileSelected : function(e)
27436     {
27437         e.preventDefault();
27438         
27439         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27440             return;
27441         }
27442         
27443         var file = this.selectorEl.dom.files[0];
27444         
27445         if(this.fireEvent('inspect', this, file) != false){
27446             this.prepare(file);
27447         }
27448         
27449     },
27450     
27451     trash : function(e)
27452     {
27453         this.fireEvent('trash', this);
27454     },
27455     
27456     download : function(e)
27457     {
27458         this.fireEvent('download', this);
27459     },
27460     
27461     loadCanvas : function(src)
27462     {   
27463         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27464             
27465             this.reset();
27466             
27467             this.imageEl = document.createElement('img');
27468             
27469             var _this = this;
27470             
27471             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27472             
27473             this.imageEl.src = src;
27474         }
27475     },
27476     
27477     onLoadCanvas : function()
27478     {   
27479         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27480         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27481         
27482         this.bodyEl.un('click', this.beforeSelectFile, this);
27483         
27484         this.notifyEl.hide();
27485         this.thumbEl.show();
27486         this.footerEl.show();
27487         
27488         this.baseRotateLevel();
27489         
27490         if(this.isDocument){
27491             this.setThumbBoxSize();
27492         }
27493         
27494         this.setThumbBoxPosition();
27495         
27496         this.baseScaleLevel();
27497         
27498         this.draw();
27499         
27500         this.resize();
27501         
27502         this.canvasLoaded = true;
27503         
27504         if(this.loadMask){
27505             this.maskEl.unmask();
27506         }
27507         
27508     },
27509     
27510     setCanvasPosition : function()
27511     {   
27512         if(!this.canvasEl){
27513             return;
27514         }
27515         
27516         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27517         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27518         
27519         this.previewEl.setLeft(pw);
27520         this.previewEl.setTop(ph);
27521         
27522     },
27523     
27524     onMouseDown : function(e)
27525     {   
27526         e.stopEvent();
27527         
27528         this.dragable = true;
27529         this.pinching = false;
27530         
27531         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27532             this.dragable = false;
27533             return;
27534         }
27535         
27536         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27537         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27538         
27539     },
27540     
27541     onMouseMove : function(e)
27542     {   
27543         e.stopEvent();
27544         
27545         if(!this.canvasLoaded){
27546             return;
27547         }
27548         
27549         if (!this.dragable){
27550             return;
27551         }
27552         
27553         var minX = Math.ceil(this.thumbEl.getLeft(true));
27554         var minY = Math.ceil(this.thumbEl.getTop(true));
27555         
27556         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27557         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27558         
27559         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27560         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27561         
27562         x = x - this.mouseX;
27563         y = y - this.mouseY;
27564         
27565         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27566         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27567         
27568         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27569         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27570         
27571         this.previewEl.setLeft(bgX);
27572         this.previewEl.setTop(bgY);
27573         
27574         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27575         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27576     },
27577     
27578     onMouseUp : function(e)
27579     {   
27580         e.stopEvent();
27581         
27582         this.dragable = false;
27583     },
27584     
27585     onMouseWheel : function(e)
27586     {   
27587         e.stopEvent();
27588         
27589         this.startScale = this.scale;
27590         
27591         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27592         
27593         if(!this.zoomable()){
27594             this.scale = this.startScale;
27595             return;
27596         }
27597         
27598         this.draw();
27599         
27600         return;
27601     },
27602     
27603     zoomable : function()
27604     {
27605         var minScale = this.thumbEl.getWidth() / this.minWidth;
27606         
27607         if(this.minWidth < this.minHeight){
27608             minScale = this.thumbEl.getHeight() / this.minHeight;
27609         }
27610         
27611         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27612         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27613         
27614         if(
27615                 this.isDocument &&
27616                 (this.rotate == 0 || this.rotate == 180) && 
27617                 (
27618                     width > this.imageEl.OriginWidth || 
27619                     height > this.imageEl.OriginHeight ||
27620                     (width < this.minWidth && height < this.minHeight)
27621                 )
27622         ){
27623             return false;
27624         }
27625         
27626         if(
27627                 this.isDocument &&
27628                 (this.rotate == 90 || this.rotate == 270) && 
27629                 (
27630                     width > this.imageEl.OriginWidth || 
27631                     height > this.imageEl.OriginHeight ||
27632                     (width < this.minHeight && height < this.minWidth)
27633                 )
27634         ){
27635             return false;
27636         }
27637         
27638         if(
27639                 !this.isDocument &&
27640                 (this.rotate == 0 || this.rotate == 180) && 
27641                 (
27642                     width < this.minWidth || 
27643                     width > this.imageEl.OriginWidth || 
27644                     height < this.minHeight || 
27645                     height > this.imageEl.OriginHeight
27646                 )
27647         ){
27648             return false;
27649         }
27650         
27651         if(
27652                 !this.isDocument &&
27653                 (this.rotate == 90 || this.rotate == 270) && 
27654                 (
27655                     width < this.minHeight || 
27656                     width > this.imageEl.OriginWidth || 
27657                     height < this.minWidth || 
27658                     height > this.imageEl.OriginHeight
27659                 )
27660         ){
27661             return false;
27662         }
27663         
27664         return true;
27665         
27666     },
27667     
27668     onRotateLeft : function(e)
27669     {   
27670         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27671             
27672             var minScale = this.thumbEl.getWidth() / this.minWidth;
27673             
27674             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27675             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27676             
27677             this.startScale = this.scale;
27678             
27679             while (this.getScaleLevel() < minScale){
27680             
27681                 this.scale = this.scale + 1;
27682                 
27683                 if(!this.zoomable()){
27684                     break;
27685                 }
27686                 
27687                 if(
27688                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27689                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27690                 ){
27691                     continue;
27692                 }
27693                 
27694                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27695
27696                 this.draw();
27697                 
27698                 return;
27699             }
27700             
27701             this.scale = this.startScale;
27702             
27703             this.onRotateFail();
27704             
27705             return false;
27706         }
27707         
27708         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27709
27710         if(this.isDocument){
27711             this.setThumbBoxSize();
27712             this.setThumbBoxPosition();
27713             this.setCanvasPosition();
27714         }
27715         
27716         this.draw();
27717         
27718         this.fireEvent('rotate', this, 'left');
27719         
27720     },
27721     
27722     onRotateRight : function(e)
27723     {
27724         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27725             
27726             var minScale = this.thumbEl.getWidth() / this.minWidth;
27727         
27728             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27729             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27730             
27731             this.startScale = this.scale;
27732             
27733             while (this.getScaleLevel() < minScale){
27734             
27735                 this.scale = this.scale + 1;
27736                 
27737                 if(!this.zoomable()){
27738                     break;
27739                 }
27740                 
27741                 if(
27742                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27743                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27744                 ){
27745                     continue;
27746                 }
27747                 
27748                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27749
27750                 this.draw();
27751                 
27752                 return;
27753             }
27754             
27755             this.scale = this.startScale;
27756             
27757             this.onRotateFail();
27758             
27759             return false;
27760         }
27761         
27762         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27763
27764         if(this.isDocument){
27765             this.setThumbBoxSize();
27766             this.setThumbBoxPosition();
27767             this.setCanvasPosition();
27768         }
27769         
27770         this.draw();
27771         
27772         this.fireEvent('rotate', this, 'right');
27773     },
27774     
27775     onRotateFail : function()
27776     {
27777         this.errorEl.show(true);
27778         
27779         var _this = this;
27780         
27781         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27782     },
27783     
27784     draw : function()
27785     {
27786         this.previewEl.dom.innerHTML = '';
27787         
27788         var canvasEl = document.createElement("canvas");
27789         
27790         var contextEl = canvasEl.getContext("2d");
27791         
27792         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27793         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27794         var center = this.imageEl.OriginWidth / 2;
27795         
27796         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27797             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27798             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27799             center = this.imageEl.OriginHeight / 2;
27800         }
27801         
27802         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27803         
27804         contextEl.translate(center, center);
27805         contextEl.rotate(this.rotate * Math.PI / 180);
27806
27807         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27808         
27809         this.canvasEl = document.createElement("canvas");
27810         
27811         this.contextEl = this.canvasEl.getContext("2d");
27812         
27813         switch (this.rotate) {
27814             case 0 :
27815                 
27816                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27817                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27818                 
27819                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27820                 
27821                 break;
27822             case 90 : 
27823                 
27824                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27825                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27826                 
27827                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27828                     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);
27829                     break;
27830                 }
27831                 
27832                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27833                 
27834                 break;
27835             case 180 :
27836                 
27837                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27838                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27839                 
27840                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27841                     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);
27842                     break;
27843                 }
27844                 
27845                 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);
27846                 
27847                 break;
27848             case 270 :
27849                 
27850                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27851                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27852         
27853                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27854                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27855                     break;
27856                 }
27857                 
27858                 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);
27859                 
27860                 break;
27861             default : 
27862                 break;
27863         }
27864         
27865         this.previewEl.appendChild(this.canvasEl);
27866         
27867         this.setCanvasPosition();
27868     },
27869     
27870     crop : function()
27871     {
27872         if(!this.canvasLoaded){
27873             return;
27874         }
27875         
27876         var imageCanvas = document.createElement("canvas");
27877         
27878         var imageContext = imageCanvas.getContext("2d");
27879         
27880         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27881         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27882         
27883         var center = imageCanvas.width / 2;
27884         
27885         imageContext.translate(center, center);
27886         
27887         imageContext.rotate(this.rotate * Math.PI / 180);
27888         
27889         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27890         
27891         var canvas = document.createElement("canvas");
27892         
27893         var context = canvas.getContext("2d");
27894                 
27895         canvas.width = this.minWidth;
27896         canvas.height = this.minHeight;
27897
27898         switch (this.rotate) {
27899             case 0 :
27900                 
27901                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27902                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27903                 
27904                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27905                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27906                 
27907                 var targetWidth = this.minWidth - 2 * x;
27908                 var targetHeight = this.minHeight - 2 * y;
27909                 
27910                 var scale = 1;
27911                 
27912                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27913                     scale = targetWidth / width;
27914                 }
27915                 
27916                 if(x > 0 && y == 0){
27917                     scale = targetHeight / height;
27918                 }
27919                 
27920                 if(x > 0 && y > 0){
27921                     scale = targetWidth / width;
27922                     
27923                     if(width < height){
27924                         scale = targetHeight / height;
27925                     }
27926                 }
27927                 
27928                 context.scale(scale, scale);
27929                 
27930                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27931                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27932
27933                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27934                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27935
27936                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27937                 
27938                 break;
27939             case 90 : 
27940                 
27941                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27942                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27943                 
27944                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27945                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27946                 
27947                 var targetWidth = this.minWidth - 2 * x;
27948                 var targetHeight = this.minHeight - 2 * y;
27949                 
27950                 var scale = 1;
27951                 
27952                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27953                     scale = targetWidth / width;
27954                 }
27955                 
27956                 if(x > 0 && y == 0){
27957                     scale = targetHeight / height;
27958                 }
27959                 
27960                 if(x > 0 && y > 0){
27961                     scale = targetWidth / width;
27962                     
27963                     if(width < height){
27964                         scale = targetHeight / height;
27965                     }
27966                 }
27967                 
27968                 context.scale(scale, scale);
27969                 
27970                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27971                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27972
27973                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27974                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27975                 
27976                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27977                 
27978                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27979                 
27980                 break;
27981             case 180 :
27982                 
27983                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27984                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27985                 
27986                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27987                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27988                 
27989                 var targetWidth = this.minWidth - 2 * x;
27990                 var targetHeight = this.minHeight - 2 * y;
27991                 
27992                 var scale = 1;
27993                 
27994                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27995                     scale = targetWidth / width;
27996                 }
27997                 
27998                 if(x > 0 && y == 0){
27999                     scale = targetHeight / height;
28000                 }
28001                 
28002                 if(x > 0 && y > 0){
28003                     scale = targetWidth / width;
28004                     
28005                     if(width < height){
28006                         scale = targetHeight / height;
28007                     }
28008                 }
28009                 
28010                 context.scale(scale, scale);
28011                 
28012                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28013                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28014
28015                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28016                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28017
28018                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28019                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28020                 
28021                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28022                 
28023                 break;
28024             case 270 :
28025                 
28026                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28027                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28028                 
28029                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28030                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28031                 
28032                 var targetWidth = this.minWidth - 2 * x;
28033                 var targetHeight = this.minHeight - 2 * y;
28034                 
28035                 var scale = 1;
28036                 
28037                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28038                     scale = targetWidth / width;
28039                 }
28040                 
28041                 if(x > 0 && y == 0){
28042                     scale = targetHeight / height;
28043                 }
28044                 
28045                 if(x > 0 && y > 0){
28046                     scale = targetWidth / width;
28047                     
28048                     if(width < height){
28049                         scale = targetHeight / height;
28050                     }
28051                 }
28052                 
28053                 context.scale(scale, scale);
28054                 
28055                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28056                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28057
28058                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28059                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28060                 
28061                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28062                 
28063                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28064                 
28065                 break;
28066             default : 
28067                 break;
28068         }
28069         
28070         this.cropData = canvas.toDataURL(this.cropType);
28071         
28072         if(this.fireEvent('crop', this, this.cropData) !== false){
28073             this.process(this.file, this.cropData);
28074         }
28075         
28076         return;
28077         
28078     },
28079     
28080     setThumbBoxSize : function()
28081     {
28082         var width, height;
28083         
28084         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28085             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28086             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28087             
28088             this.minWidth = width;
28089             this.minHeight = height;
28090             
28091             if(this.rotate == 90 || this.rotate == 270){
28092                 this.minWidth = height;
28093                 this.minHeight = width;
28094             }
28095         }
28096         
28097         height = 300;
28098         width = Math.ceil(this.minWidth * height / this.minHeight);
28099         
28100         if(this.minWidth > this.minHeight){
28101             width = 300;
28102             height = Math.ceil(this.minHeight * width / this.minWidth);
28103         }
28104         
28105         this.thumbEl.setStyle({
28106             width : width + 'px',
28107             height : height + 'px'
28108         });
28109
28110         return;
28111             
28112     },
28113     
28114     setThumbBoxPosition : function()
28115     {
28116         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28117         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28118         
28119         this.thumbEl.setLeft(x);
28120         this.thumbEl.setTop(y);
28121         
28122     },
28123     
28124     baseRotateLevel : function()
28125     {
28126         this.baseRotate = 1;
28127         
28128         if(
28129                 typeof(this.exif) != 'undefined' &&
28130                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28131                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28132         ){
28133             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28134         }
28135         
28136         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28137         
28138     },
28139     
28140     baseScaleLevel : function()
28141     {
28142         var width, height;
28143         
28144         if(this.isDocument){
28145             
28146             if(this.baseRotate == 6 || this.baseRotate == 8){
28147             
28148                 height = this.thumbEl.getHeight();
28149                 this.baseScale = height / this.imageEl.OriginWidth;
28150
28151                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28152                     width = this.thumbEl.getWidth();
28153                     this.baseScale = width / this.imageEl.OriginHeight;
28154                 }
28155
28156                 return;
28157             }
28158
28159             height = this.thumbEl.getHeight();
28160             this.baseScale = height / this.imageEl.OriginHeight;
28161
28162             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28163                 width = this.thumbEl.getWidth();
28164                 this.baseScale = width / this.imageEl.OriginWidth;
28165             }
28166
28167             return;
28168         }
28169         
28170         if(this.baseRotate == 6 || this.baseRotate == 8){
28171             
28172             width = this.thumbEl.getHeight();
28173             this.baseScale = width / this.imageEl.OriginHeight;
28174             
28175             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28176                 height = this.thumbEl.getWidth();
28177                 this.baseScale = height / this.imageEl.OriginHeight;
28178             }
28179             
28180             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28181                 height = this.thumbEl.getWidth();
28182                 this.baseScale = height / this.imageEl.OriginHeight;
28183                 
28184                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28185                     width = this.thumbEl.getHeight();
28186                     this.baseScale = width / this.imageEl.OriginWidth;
28187                 }
28188             }
28189             
28190             return;
28191         }
28192         
28193         width = this.thumbEl.getWidth();
28194         this.baseScale = width / this.imageEl.OriginWidth;
28195         
28196         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28197             height = this.thumbEl.getHeight();
28198             this.baseScale = height / this.imageEl.OriginHeight;
28199         }
28200         
28201         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28202             
28203             height = this.thumbEl.getHeight();
28204             this.baseScale = height / this.imageEl.OriginHeight;
28205             
28206             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28207                 width = this.thumbEl.getWidth();
28208                 this.baseScale = width / this.imageEl.OriginWidth;
28209             }
28210             
28211         }
28212         
28213         return;
28214     },
28215     
28216     getScaleLevel : function()
28217     {
28218         return this.baseScale * Math.pow(1.1, this.scale);
28219     },
28220     
28221     onTouchStart : function(e)
28222     {
28223         if(!this.canvasLoaded){
28224             this.beforeSelectFile(e);
28225             return;
28226         }
28227         
28228         var touches = e.browserEvent.touches;
28229         
28230         if(!touches){
28231             return;
28232         }
28233         
28234         if(touches.length == 1){
28235             this.onMouseDown(e);
28236             return;
28237         }
28238         
28239         if(touches.length != 2){
28240             return;
28241         }
28242         
28243         var coords = [];
28244         
28245         for(var i = 0, finger; finger = touches[i]; i++){
28246             coords.push(finger.pageX, finger.pageY);
28247         }
28248         
28249         var x = Math.pow(coords[0] - coords[2], 2);
28250         var y = Math.pow(coords[1] - coords[3], 2);
28251         
28252         this.startDistance = Math.sqrt(x + y);
28253         
28254         this.startScale = this.scale;
28255         
28256         this.pinching = true;
28257         this.dragable = false;
28258         
28259     },
28260     
28261     onTouchMove : function(e)
28262     {
28263         if(!this.pinching && !this.dragable){
28264             return;
28265         }
28266         
28267         var touches = e.browserEvent.touches;
28268         
28269         if(!touches){
28270             return;
28271         }
28272         
28273         if(this.dragable){
28274             this.onMouseMove(e);
28275             return;
28276         }
28277         
28278         var coords = [];
28279         
28280         for(var i = 0, finger; finger = touches[i]; i++){
28281             coords.push(finger.pageX, finger.pageY);
28282         }
28283         
28284         var x = Math.pow(coords[0] - coords[2], 2);
28285         var y = Math.pow(coords[1] - coords[3], 2);
28286         
28287         this.endDistance = Math.sqrt(x + y);
28288         
28289         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28290         
28291         if(!this.zoomable()){
28292             this.scale = this.startScale;
28293             return;
28294         }
28295         
28296         this.draw();
28297         
28298     },
28299     
28300     onTouchEnd : function(e)
28301     {
28302         this.pinching = false;
28303         this.dragable = false;
28304         
28305     },
28306     
28307     process : function(file, crop)
28308     {
28309         if(this.loadMask){
28310             this.maskEl.mask(this.loadingText);
28311         }
28312         
28313         this.xhr = new XMLHttpRequest();
28314         
28315         file.xhr = this.xhr;
28316
28317         this.xhr.open(this.method, this.url, true);
28318         
28319         var headers = {
28320             "Accept": "application/json",
28321             "Cache-Control": "no-cache",
28322             "X-Requested-With": "XMLHttpRequest"
28323         };
28324         
28325         for (var headerName in headers) {
28326             var headerValue = headers[headerName];
28327             if (headerValue) {
28328                 this.xhr.setRequestHeader(headerName, headerValue);
28329             }
28330         }
28331         
28332         var _this = this;
28333         
28334         this.xhr.onload = function()
28335         {
28336             _this.xhrOnLoad(_this.xhr);
28337         }
28338         
28339         this.xhr.onerror = function()
28340         {
28341             _this.xhrOnError(_this.xhr);
28342         }
28343         
28344         var formData = new FormData();
28345
28346         formData.append('returnHTML', 'NO');
28347         
28348         if(crop){
28349             formData.append('crop', crop);
28350         }
28351         
28352         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28353             formData.append(this.paramName, file, file.name);
28354         }
28355         
28356         if(typeof(file.filename) != 'undefined'){
28357             formData.append('filename', file.filename);
28358         }
28359         
28360         if(typeof(file.mimetype) != 'undefined'){
28361             formData.append('mimetype', file.mimetype);
28362         }
28363         
28364         if(this.fireEvent('arrange', this, formData) != false){
28365             this.xhr.send(formData);
28366         };
28367     },
28368     
28369     xhrOnLoad : function(xhr)
28370     {
28371         if(this.loadMask){
28372             this.maskEl.unmask();
28373         }
28374         
28375         if (xhr.readyState !== 4) {
28376             this.fireEvent('exception', this, xhr);
28377             return;
28378         }
28379
28380         var response = Roo.decode(xhr.responseText);
28381         
28382         if(!response.success){
28383             this.fireEvent('exception', this, xhr);
28384             return;
28385         }
28386         
28387         var response = Roo.decode(xhr.responseText);
28388         
28389         this.fireEvent('upload', this, response);
28390         
28391     },
28392     
28393     xhrOnError : function()
28394     {
28395         if(this.loadMask){
28396             this.maskEl.unmask();
28397         }
28398         
28399         Roo.log('xhr on error');
28400         
28401         var response = Roo.decode(xhr.responseText);
28402           
28403         Roo.log(response);
28404         
28405     },
28406     
28407     prepare : function(file)
28408     {   
28409         if(this.loadMask){
28410             this.maskEl.mask(this.loadingText);
28411         }
28412         
28413         this.file = false;
28414         this.exif = {};
28415         
28416         if(typeof(file) === 'string'){
28417             this.loadCanvas(file);
28418             return;
28419         }
28420         
28421         if(!file || !this.urlAPI){
28422             return;
28423         }
28424         
28425         this.file = file;
28426         this.cropType = file.type;
28427         
28428         var _this = this;
28429         
28430         if(this.fireEvent('prepare', this, this.file) != false){
28431             
28432             var reader = new FileReader();
28433             
28434             reader.onload = function (e) {
28435                 if (e.target.error) {
28436                     Roo.log(e.target.error);
28437                     return;
28438                 }
28439                 
28440                 var buffer = e.target.result,
28441                     dataView = new DataView(buffer),
28442                     offset = 2,
28443                     maxOffset = dataView.byteLength - 4,
28444                     markerBytes,
28445                     markerLength;
28446                 
28447                 if (dataView.getUint16(0) === 0xffd8) {
28448                     while (offset < maxOffset) {
28449                         markerBytes = dataView.getUint16(offset);
28450                         
28451                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28452                             markerLength = dataView.getUint16(offset + 2) + 2;
28453                             if (offset + markerLength > dataView.byteLength) {
28454                                 Roo.log('Invalid meta data: Invalid segment size.');
28455                                 break;
28456                             }
28457                             
28458                             if(markerBytes == 0xffe1){
28459                                 _this.parseExifData(
28460                                     dataView,
28461                                     offset,
28462                                     markerLength
28463                                 );
28464                             }
28465                             
28466                             offset += markerLength;
28467                             
28468                             continue;
28469                         }
28470                         
28471                         break;
28472                     }
28473                     
28474                 }
28475                 
28476                 var url = _this.urlAPI.createObjectURL(_this.file);
28477                 
28478                 _this.loadCanvas(url);
28479                 
28480                 return;
28481             }
28482             
28483             reader.readAsArrayBuffer(this.file);
28484             
28485         }
28486         
28487     },
28488     
28489     parseExifData : function(dataView, offset, length)
28490     {
28491         var tiffOffset = offset + 10,
28492             littleEndian,
28493             dirOffset;
28494     
28495         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28496             // No Exif data, might be XMP data instead
28497             return;
28498         }
28499         
28500         // Check for the ASCII code for "Exif" (0x45786966):
28501         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28502             // No Exif data, might be XMP data instead
28503             return;
28504         }
28505         if (tiffOffset + 8 > dataView.byteLength) {
28506             Roo.log('Invalid Exif data: Invalid segment size.');
28507             return;
28508         }
28509         // Check for the two null bytes:
28510         if (dataView.getUint16(offset + 8) !== 0x0000) {
28511             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28512             return;
28513         }
28514         // Check the byte alignment:
28515         switch (dataView.getUint16(tiffOffset)) {
28516         case 0x4949:
28517             littleEndian = true;
28518             break;
28519         case 0x4D4D:
28520             littleEndian = false;
28521             break;
28522         default:
28523             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28524             return;
28525         }
28526         // Check for the TIFF tag marker (0x002A):
28527         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28528             Roo.log('Invalid Exif data: Missing TIFF marker.');
28529             return;
28530         }
28531         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28532         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28533         
28534         this.parseExifTags(
28535             dataView,
28536             tiffOffset,
28537             tiffOffset + dirOffset,
28538             littleEndian
28539         );
28540     },
28541     
28542     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28543     {
28544         var tagsNumber,
28545             dirEndOffset,
28546             i;
28547         if (dirOffset + 6 > dataView.byteLength) {
28548             Roo.log('Invalid Exif data: Invalid directory offset.');
28549             return;
28550         }
28551         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28552         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28553         if (dirEndOffset + 4 > dataView.byteLength) {
28554             Roo.log('Invalid Exif data: Invalid directory size.');
28555             return;
28556         }
28557         for (i = 0; i < tagsNumber; i += 1) {
28558             this.parseExifTag(
28559                 dataView,
28560                 tiffOffset,
28561                 dirOffset + 2 + 12 * i, // tag offset
28562                 littleEndian
28563             );
28564         }
28565         // Return the offset to the next directory:
28566         return dataView.getUint32(dirEndOffset, littleEndian);
28567     },
28568     
28569     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28570     {
28571         var tag = dataView.getUint16(offset, littleEndian);
28572         
28573         this.exif[tag] = this.getExifValue(
28574             dataView,
28575             tiffOffset,
28576             offset,
28577             dataView.getUint16(offset + 2, littleEndian), // tag type
28578             dataView.getUint32(offset + 4, littleEndian), // tag length
28579             littleEndian
28580         );
28581     },
28582     
28583     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28584     {
28585         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28586             tagSize,
28587             dataOffset,
28588             values,
28589             i,
28590             str,
28591             c;
28592     
28593         if (!tagType) {
28594             Roo.log('Invalid Exif data: Invalid tag type.');
28595             return;
28596         }
28597         
28598         tagSize = tagType.size * length;
28599         // Determine if the value is contained in the dataOffset bytes,
28600         // or if the value at the dataOffset is a pointer to the actual data:
28601         dataOffset = tagSize > 4 ?
28602                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28603         if (dataOffset + tagSize > dataView.byteLength) {
28604             Roo.log('Invalid Exif data: Invalid data offset.');
28605             return;
28606         }
28607         if (length === 1) {
28608             return tagType.getValue(dataView, dataOffset, littleEndian);
28609         }
28610         values = [];
28611         for (i = 0; i < length; i += 1) {
28612             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28613         }
28614         
28615         if (tagType.ascii) {
28616             str = '';
28617             // Concatenate the chars:
28618             for (i = 0; i < values.length; i += 1) {
28619                 c = values[i];
28620                 // Ignore the terminating NULL byte(s):
28621                 if (c === '\u0000') {
28622                     break;
28623                 }
28624                 str += c;
28625             }
28626             return str;
28627         }
28628         return values;
28629     }
28630     
28631 });
28632
28633 Roo.apply(Roo.bootstrap.UploadCropbox, {
28634     tags : {
28635         'Orientation': 0x0112
28636     },
28637     
28638     Orientation: {
28639             1: 0, //'top-left',
28640 //            2: 'top-right',
28641             3: 180, //'bottom-right',
28642 //            4: 'bottom-left',
28643 //            5: 'left-top',
28644             6: 90, //'right-top',
28645 //            7: 'right-bottom',
28646             8: 270 //'left-bottom'
28647     },
28648     
28649     exifTagTypes : {
28650         // byte, 8-bit unsigned int:
28651         1: {
28652             getValue: function (dataView, dataOffset) {
28653                 return dataView.getUint8(dataOffset);
28654             },
28655             size: 1
28656         },
28657         // ascii, 8-bit byte:
28658         2: {
28659             getValue: function (dataView, dataOffset) {
28660                 return String.fromCharCode(dataView.getUint8(dataOffset));
28661             },
28662             size: 1,
28663             ascii: true
28664         },
28665         // short, 16 bit int:
28666         3: {
28667             getValue: function (dataView, dataOffset, littleEndian) {
28668                 return dataView.getUint16(dataOffset, littleEndian);
28669             },
28670             size: 2
28671         },
28672         // long, 32 bit int:
28673         4: {
28674             getValue: function (dataView, dataOffset, littleEndian) {
28675                 return dataView.getUint32(dataOffset, littleEndian);
28676             },
28677             size: 4
28678         },
28679         // rational = two long values, first is numerator, second is denominator:
28680         5: {
28681             getValue: function (dataView, dataOffset, littleEndian) {
28682                 return dataView.getUint32(dataOffset, littleEndian) /
28683                     dataView.getUint32(dataOffset + 4, littleEndian);
28684             },
28685             size: 8
28686         },
28687         // slong, 32 bit signed int:
28688         9: {
28689             getValue: function (dataView, dataOffset, littleEndian) {
28690                 return dataView.getInt32(dataOffset, littleEndian);
28691             },
28692             size: 4
28693         },
28694         // srational, two slongs, first is numerator, second is denominator:
28695         10: {
28696             getValue: function (dataView, dataOffset, littleEndian) {
28697                 return dataView.getInt32(dataOffset, littleEndian) /
28698                     dataView.getInt32(dataOffset + 4, littleEndian);
28699             },
28700             size: 8
28701         }
28702     },
28703     
28704     footer : {
28705         STANDARD : [
28706             {
28707                 tag : 'div',
28708                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28709                 action : 'rotate-left',
28710                 cn : [
28711                     {
28712                         tag : 'button',
28713                         cls : 'btn btn-default',
28714                         html : '<i class="fa fa-undo"></i>'
28715                     }
28716                 ]
28717             },
28718             {
28719                 tag : 'div',
28720                 cls : 'btn-group roo-upload-cropbox-picture',
28721                 action : 'picture',
28722                 cn : [
28723                     {
28724                         tag : 'button',
28725                         cls : 'btn btn-default',
28726                         html : '<i class="fa fa-picture-o"></i>'
28727                     }
28728                 ]
28729             },
28730             {
28731                 tag : 'div',
28732                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28733                 action : 'rotate-right',
28734                 cn : [
28735                     {
28736                         tag : 'button',
28737                         cls : 'btn btn-default',
28738                         html : '<i class="fa fa-repeat"></i>'
28739                     }
28740                 ]
28741             }
28742         ],
28743         DOCUMENT : [
28744             {
28745                 tag : 'div',
28746                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28747                 action : 'rotate-left',
28748                 cn : [
28749                     {
28750                         tag : 'button',
28751                         cls : 'btn btn-default',
28752                         html : '<i class="fa fa-undo"></i>'
28753                     }
28754                 ]
28755             },
28756             {
28757                 tag : 'div',
28758                 cls : 'btn-group roo-upload-cropbox-download',
28759                 action : 'download',
28760                 cn : [
28761                     {
28762                         tag : 'button',
28763                         cls : 'btn btn-default',
28764                         html : '<i class="fa fa-download"></i>'
28765                     }
28766                 ]
28767             },
28768             {
28769                 tag : 'div',
28770                 cls : 'btn-group roo-upload-cropbox-crop',
28771                 action : 'crop',
28772                 cn : [
28773                     {
28774                         tag : 'button',
28775                         cls : 'btn btn-default',
28776                         html : '<i class="fa fa-crop"></i>'
28777                     }
28778                 ]
28779             },
28780             {
28781                 tag : 'div',
28782                 cls : 'btn-group roo-upload-cropbox-trash',
28783                 action : 'trash',
28784                 cn : [
28785                     {
28786                         tag : 'button',
28787                         cls : 'btn btn-default',
28788                         html : '<i class="fa fa-trash"></i>'
28789                     }
28790                 ]
28791             },
28792             {
28793                 tag : 'div',
28794                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28795                 action : 'rotate-right',
28796                 cn : [
28797                     {
28798                         tag : 'button',
28799                         cls : 'btn btn-default',
28800                         html : '<i class="fa fa-repeat"></i>'
28801                     }
28802                 ]
28803             }
28804         ],
28805         ROTATOR : [
28806             {
28807                 tag : 'div',
28808                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28809                 action : 'rotate-left',
28810                 cn : [
28811                     {
28812                         tag : 'button',
28813                         cls : 'btn btn-default',
28814                         html : '<i class="fa fa-undo"></i>'
28815                     }
28816                 ]
28817             },
28818             {
28819                 tag : 'div',
28820                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28821                 action : 'rotate-right',
28822                 cn : [
28823                     {
28824                         tag : 'button',
28825                         cls : 'btn btn-default',
28826                         html : '<i class="fa fa-repeat"></i>'
28827                     }
28828                 ]
28829             }
28830         ]
28831     }
28832 });
28833
28834 /*
28835 * Licence: LGPL
28836 */
28837
28838 /**
28839  * @class Roo.bootstrap.DocumentManager
28840  * @extends Roo.bootstrap.Component
28841  * Bootstrap DocumentManager class
28842  * @cfg {String} paramName default 'imageUpload'
28843  * @cfg {String} toolTipName default 'filename'
28844  * @cfg {String} method default POST
28845  * @cfg {String} url action url
28846  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28847  * @cfg {Boolean} multiple multiple upload default true
28848  * @cfg {Number} thumbSize default 300
28849  * @cfg {String} fieldLabel
28850  * @cfg {Number} labelWidth default 4
28851  * @cfg {String} labelAlign (left|top) default left
28852  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28853 * @cfg {Number} labellg set the width of label (1-12)
28854  * @cfg {Number} labelmd set the width of label (1-12)
28855  * @cfg {Number} labelsm set the width of label (1-12)
28856  * @cfg {Number} labelxs set the width of label (1-12)
28857  * 
28858  * @constructor
28859  * Create a new DocumentManager
28860  * @param {Object} config The config object
28861  */
28862
28863 Roo.bootstrap.DocumentManager = function(config){
28864     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28865     
28866     this.files = [];
28867     this.delegates = [];
28868     
28869     this.addEvents({
28870         /**
28871          * @event initial
28872          * Fire when initial the DocumentManager
28873          * @param {Roo.bootstrap.DocumentManager} this
28874          */
28875         "initial" : true,
28876         /**
28877          * @event inspect
28878          * inspect selected file
28879          * @param {Roo.bootstrap.DocumentManager} this
28880          * @param {File} file
28881          */
28882         "inspect" : true,
28883         /**
28884          * @event exception
28885          * Fire when xhr load exception
28886          * @param {Roo.bootstrap.DocumentManager} this
28887          * @param {XMLHttpRequest} xhr
28888          */
28889         "exception" : true,
28890         /**
28891          * @event afterupload
28892          * Fire when xhr load exception
28893          * @param {Roo.bootstrap.DocumentManager} this
28894          * @param {XMLHttpRequest} xhr
28895          */
28896         "afterupload" : true,
28897         /**
28898          * @event prepare
28899          * prepare the form data
28900          * @param {Roo.bootstrap.DocumentManager} this
28901          * @param {Object} formData
28902          */
28903         "prepare" : true,
28904         /**
28905          * @event remove
28906          * Fire when remove the file
28907          * @param {Roo.bootstrap.DocumentManager} this
28908          * @param {Object} file
28909          */
28910         "remove" : true,
28911         /**
28912          * @event refresh
28913          * Fire after refresh the file
28914          * @param {Roo.bootstrap.DocumentManager} this
28915          */
28916         "refresh" : true,
28917         /**
28918          * @event click
28919          * Fire after click the image
28920          * @param {Roo.bootstrap.DocumentManager} this
28921          * @param {Object} file
28922          */
28923         "click" : true,
28924         /**
28925          * @event edit
28926          * Fire when upload a image and editable set to true
28927          * @param {Roo.bootstrap.DocumentManager} this
28928          * @param {Object} file
28929          */
28930         "edit" : true,
28931         /**
28932          * @event beforeselectfile
28933          * Fire before select file
28934          * @param {Roo.bootstrap.DocumentManager} this
28935          */
28936         "beforeselectfile" : true,
28937         /**
28938          * @event process
28939          * Fire before process file
28940          * @param {Roo.bootstrap.DocumentManager} this
28941          * @param {Object} file
28942          */
28943         "process" : true,
28944         /**
28945          * @event previewrendered
28946          * Fire when preview rendered
28947          * @param {Roo.bootstrap.DocumentManager} this
28948          * @param {Object} file
28949          */
28950         "previewrendered" : true,
28951         /**
28952          */
28953         "previewResize" : true
28954         
28955     });
28956 };
28957
28958 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28959     
28960     boxes : 0,
28961     inputName : '',
28962     thumbSize : 300,
28963     multiple : true,
28964     files : false,
28965     method : 'POST',
28966     url : '',
28967     paramName : 'imageUpload',
28968     toolTipName : 'filename',
28969     fieldLabel : '',
28970     labelWidth : 4,
28971     labelAlign : 'left',
28972     editable : true,
28973     delegates : false,
28974     xhr : false, 
28975     
28976     labellg : 0,
28977     labelmd : 0,
28978     labelsm : 0,
28979     labelxs : 0,
28980     
28981     getAutoCreate : function()
28982     {   
28983         var managerWidget = {
28984             tag : 'div',
28985             cls : 'roo-document-manager',
28986             cn : [
28987                 {
28988                     tag : 'input',
28989                     cls : 'roo-document-manager-selector',
28990                     type : 'file'
28991                 },
28992                 {
28993                     tag : 'div',
28994                     cls : 'roo-document-manager-uploader',
28995                     cn : [
28996                         {
28997                             tag : 'div',
28998                             cls : 'roo-document-manager-upload-btn',
28999                             html : '<i class="fa fa-plus"></i>'
29000                         }
29001                     ]
29002                     
29003                 }
29004             ]
29005         };
29006         
29007         var content = [
29008             {
29009                 tag : 'div',
29010                 cls : 'column col-md-12',
29011                 cn : managerWidget
29012             }
29013         ];
29014         
29015         if(this.fieldLabel.length){
29016             
29017             content = [
29018                 {
29019                     tag : 'div',
29020                     cls : 'column col-md-12',
29021                     html : this.fieldLabel
29022                 },
29023                 {
29024                     tag : 'div',
29025                     cls : 'column col-md-12',
29026                     cn : managerWidget
29027                 }
29028             ];
29029
29030             if(this.labelAlign == 'left'){
29031                 content = [
29032                     {
29033                         tag : 'div',
29034                         cls : 'column',
29035                         html : this.fieldLabel
29036                     },
29037                     {
29038                         tag : 'div',
29039                         cls : 'column',
29040                         cn : managerWidget
29041                     }
29042                 ];
29043                 
29044                 if(this.labelWidth > 12){
29045                     content[0].style = "width: " + this.labelWidth + 'px';
29046                 }
29047
29048                 if(this.labelWidth < 13 && this.labelmd == 0){
29049                     this.labelmd = this.labelWidth;
29050                 }
29051
29052                 if(this.labellg > 0){
29053                     content[0].cls += ' col-lg-' + this.labellg;
29054                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29055                 }
29056
29057                 if(this.labelmd > 0){
29058                     content[0].cls += ' col-md-' + this.labelmd;
29059                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29060                 }
29061
29062                 if(this.labelsm > 0){
29063                     content[0].cls += ' col-sm-' + this.labelsm;
29064                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29065                 }
29066
29067                 if(this.labelxs > 0){
29068                     content[0].cls += ' col-xs-' + this.labelxs;
29069                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29070                 }
29071                 
29072             }
29073         }
29074         
29075         var cfg = {
29076             tag : 'div',
29077             cls : 'row clearfix',
29078             cn : content
29079         };
29080         
29081         return cfg;
29082         
29083     },
29084     
29085     initEvents : function()
29086     {
29087         this.managerEl = this.el.select('.roo-document-manager', true).first();
29088         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29089         
29090         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29091         this.selectorEl.hide();
29092         
29093         if(this.multiple){
29094             this.selectorEl.attr('multiple', 'multiple');
29095         }
29096         
29097         this.selectorEl.on('change', this.onFileSelected, this);
29098         
29099         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29100         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29101         
29102         this.uploader.on('click', this.onUploaderClick, this);
29103         
29104         this.renderProgressDialog();
29105         
29106         var _this = this;
29107         
29108         window.addEventListener("resize", function() { _this.refresh(); } );
29109         
29110         this.fireEvent('initial', this);
29111     },
29112     
29113     renderProgressDialog : function()
29114     {
29115         var _this = this;
29116         
29117         this.progressDialog = new Roo.bootstrap.Modal({
29118             cls : 'roo-document-manager-progress-dialog',
29119             allow_close : false,
29120             title : '',
29121             buttons : [
29122                 {
29123                     name  :'cancel',
29124                     weight : 'danger',
29125                     html : 'Cancel'
29126                 }
29127             ], 
29128             listeners : { 
29129                 btnclick : function() {
29130                     _this.uploadCancel();
29131                     this.hide();
29132                 }
29133             }
29134         });
29135          
29136         this.progressDialog.render(Roo.get(document.body));
29137          
29138         this.progress = new Roo.bootstrap.Progress({
29139             cls : 'roo-document-manager-progress',
29140             active : true,
29141             striped : true
29142         });
29143         
29144         this.progress.render(this.progressDialog.getChildContainer());
29145         
29146         this.progressBar = new Roo.bootstrap.ProgressBar({
29147             cls : 'roo-document-manager-progress-bar',
29148             aria_valuenow : 0,
29149             aria_valuemin : 0,
29150             aria_valuemax : 12,
29151             panel : 'success'
29152         });
29153         
29154         this.progressBar.render(this.progress.getChildContainer());
29155     },
29156     
29157     onUploaderClick : function(e)
29158     {
29159         e.preventDefault();
29160      
29161         if(this.fireEvent('beforeselectfile', this) != false){
29162             this.selectorEl.dom.click();
29163         }
29164         
29165     },
29166     
29167     onFileSelected : function(e)
29168     {
29169         e.preventDefault();
29170         
29171         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29172             return;
29173         }
29174         
29175         Roo.each(this.selectorEl.dom.files, function(file){
29176             if(this.fireEvent('inspect', this, file) != false){
29177                 this.files.push(file);
29178             }
29179         }, this);
29180         
29181         this.queue();
29182         
29183     },
29184     
29185     queue : function()
29186     {
29187         this.selectorEl.dom.value = '';
29188         
29189         if(!this.files || !this.files.length){
29190             return;
29191         }
29192         
29193         if(this.boxes > 0 && this.files.length > this.boxes){
29194             this.files = this.files.slice(0, this.boxes);
29195         }
29196         
29197         this.uploader.show();
29198         
29199         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29200             this.uploader.hide();
29201         }
29202         
29203         var _this = this;
29204         
29205         var files = [];
29206         
29207         var docs = [];
29208         
29209         Roo.each(this.files, function(file){
29210             
29211             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29212                 var f = this.renderPreview(file);
29213                 files.push(f);
29214                 return;
29215             }
29216             
29217             if(file.type.indexOf('image') != -1){
29218                 this.delegates.push(
29219                     (function(){
29220                         _this.process(file);
29221                     }).createDelegate(this)
29222                 );
29223         
29224                 return;
29225             }
29226             
29227             docs.push(
29228                 (function(){
29229                     _this.process(file);
29230                 }).createDelegate(this)
29231             );
29232             
29233         }, this);
29234         
29235         this.files = files;
29236         
29237         this.delegates = this.delegates.concat(docs);
29238         
29239         if(!this.delegates.length){
29240             this.refresh();
29241             return;
29242         }
29243         
29244         this.progressBar.aria_valuemax = this.delegates.length;
29245         
29246         this.arrange();
29247         
29248         return;
29249     },
29250     
29251     arrange : function()
29252     {
29253         if(!this.delegates.length){
29254             this.progressDialog.hide();
29255             this.refresh();
29256             return;
29257         }
29258         
29259         var delegate = this.delegates.shift();
29260         
29261         this.progressDialog.show();
29262         
29263         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29264         
29265         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29266         
29267         delegate();
29268     },
29269     
29270     refresh : function()
29271     {
29272         this.uploader.show();
29273         
29274         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29275             this.uploader.hide();
29276         }
29277         
29278         Roo.isTouch ? this.closable(false) : this.closable(true);
29279         
29280         this.fireEvent('refresh', this);
29281     },
29282     
29283     onRemove : function(e, el, o)
29284     {
29285         e.preventDefault();
29286         
29287         this.fireEvent('remove', this, o);
29288         
29289     },
29290     
29291     remove : function(o)
29292     {
29293         var files = [];
29294         
29295         Roo.each(this.files, function(file){
29296             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29297                 files.push(file);
29298                 return;
29299             }
29300
29301             o.target.remove();
29302
29303         }, this);
29304         
29305         this.files = files;
29306         
29307         this.refresh();
29308     },
29309     
29310     clear : function()
29311     {
29312         Roo.each(this.files, function(file){
29313             if(!file.target){
29314                 return;
29315             }
29316             
29317             file.target.remove();
29318
29319         }, this);
29320         
29321         this.files = [];
29322         
29323         this.refresh();
29324     },
29325     
29326     onClick : function(e, el, o)
29327     {
29328         e.preventDefault();
29329         
29330         this.fireEvent('click', this, o);
29331         
29332     },
29333     
29334     closable : function(closable)
29335     {
29336         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29337             
29338             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29339             
29340             if(closable){
29341                 el.show();
29342                 return;
29343             }
29344             
29345             el.hide();
29346             
29347         }, this);
29348     },
29349     
29350     xhrOnLoad : function(xhr)
29351     {
29352         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29353             el.remove();
29354         }, this);
29355         
29356         if (xhr.readyState !== 4) {
29357             this.arrange();
29358             this.fireEvent('exception', this, xhr);
29359             return;
29360         }
29361
29362         var response = Roo.decode(xhr.responseText);
29363         
29364         if(!response.success){
29365             this.arrange();
29366             this.fireEvent('exception', this, xhr);
29367             return;
29368         }
29369         
29370         var file = this.renderPreview(response.data);
29371         
29372         this.files.push(file);
29373         
29374         this.arrange();
29375         
29376         this.fireEvent('afterupload', this, xhr);
29377         
29378     },
29379     
29380     xhrOnError : function(xhr)
29381     {
29382         Roo.log('xhr on error');
29383         
29384         var response = Roo.decode(xhr.responseText);
29385           
29386         Roo.log(response);
29387         
29388         this.arrange();
29389     },
29390     
29391     process : function(file)
29392     {
29393         if(this.fireEvent('process', this, file) !== false){
29394             if(this.editable && file.type.indexOf('image') != -1){
29395                 this.fireEvent('edit', this, file);
29396                 return;
29397             }
29398
29399             this.uploadStart(file, false);
29400
29401             return;
29402         }
29403         
29404     },
29405     
29406     uploadStart : function(file, crop)
29407     {
29408         this.xhr = new XMLHttpRequest();
29409         
29410         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29411             this.arrange();
29412             return;
29413         }
29414         
29415         file.xhr = this.xhr;
29416             
29417         this.managerEl.createChild({
29418             tag : 'div',
29419             cls : 'roo-document-manager-loading',
29420             cn : [
29421                 {
29422                     tag : 'div',
29423                     tooltip : file.name,
29424                     cls : 'roo-document-manager-thumb',
29425                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29426                 }
29427             ]
29428
29429         });
29430
29431         this.xhr.open(this.method, this.url, true);
29432         
29433         var headers = {
29434             "Accept": "application/json",
29435             "Cache-Control": "no-cache",
29436             "X-Requested-With": "XMLHttpRequest"
29437         };
29438         
29439         for (var headerName in headers) {
29440             var headerValue = headers[headerName];
29441             if (headerValue) {
29442                 this.xhr.setRequestHeader(headerName, headerValue);
29443             }
29444         }
29445         
29446         var _this = this;
29447         
29448         this.xhr.onload = function()
29449         {
29450             _this.xhrOnLoad(_this.xhr);
29451         }
29452         
29453         this.xhr.onerror = function()
29454         {
29455             _this.xhrOnError(_this.xhr);
29456         }
29457         
29458         var formData = new FormData();
29459
29460         formData.append('returnHTML', 'NO');
29461         
29462         if(crop){
29463             formData.append('crop', crop);
29464         }
29465         
29466         formData.append(this.paramName, file, file.name);
29467         
29468         var options = {
29469             file : file, 
29470             manually : false
29471         };
29472         
29473         if(this.fireEvent('prepare', this, formData, options) != false){
29474             
29475             if(options.manually){
29476                 return;
29477             }
29478             
29479             this.xhr.send(formData);
29480             return;
29481         };
29482         
29483         this.uploadCancel();
29484     },
29485     
29486     uploadCancel : function()
29487     {
29488         if (this.xhr) {
29489             this.xhr.abort();
29490         }
29491         
29492         this.delegates = [];
29493         
29494         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29495             el.remove();
29496         }, this);
29497         
29498         this.arrange();
29499     },
29500     
29501     renderPreview : function(file)
29502     {
29503         if(typeof(file.target) != 'undefined' && file.target){
29504             return file;
29505         }
29506         
29507         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29508         
29509         var previewEl = this.managerEl.createChild({
29510             tag : 'div',
29511             cls : 'roo-document-manager-preview',
29512             cn : [
29513                 {
29514                     tag : 'div',
29515                     tooltip : file[this.toolTipName],
29516                     cls : 'roo-document-manager-thumb',
29517                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29518                 },
29519                 {
29520                     tag : 'button',
29521                     cls : 'close',
29522                     html : '<i class="fa fa-times-circle"></i>'
29523                 }
29524             ]
29525         });
29526
29527         var close = previewEl.select('button.close', true).first();
29528
29529         close.on('click', this.onRemove, this, file);
29530
29531         file.target = previewEl;
29532
29533         var image = previewEl.select('img', true).first();
29534         
29535         var _this = this;
29536         
29537         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29538         
29539         image.on('click', this.onClick, this, file);
29540         
29541         this.fireEvent('previewrendered', this, file);
29542         
29543         return file;
29544         
29545     },
29546     
29547     onPreviewLoad : function(file, image)
29548     {
29549         if(typeof(file.target) == 'undefined' || !file.target){
29550             return;
29551         }
29552         
29553         var width = image.dom.naturalWidth || image.dom.width;
29554         var height = image.dom.naturalHeight || image.dom.height;
29555         
29556         if(!this.previewResize) {
29557             return;
29558         }
29559         
29560         if(width > height){
29561             file.target.addClass('wide');
29562             return;
29563         }
29564         
29565         file.target.addClass('tall');
29566         return;
29567         
29568     },
29569     
29570     uploadFromSource : function(file, crop)
29571     {
29572         this.xhr = new XMLHttpRequest();
29573         
29574         this.managerEl.createChild({
29575             tag : 'div',
29576             cls : 'roo-document-manager-loading',
29577             cn : [
29578                 {
29579                     tag : 'div',
29580                     tooltip : file.name,
29581                     cls : 'roo-document-manager-thumb',
29582                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29583                 }
29584             ]
29585
29586         });
29587
29588         this.xhr.open(this.method, this.url, true);
29589         
29590         var headers = {
29591             "Accept": "application/json",
29592             "Cache-Control": "no-cache",
29593             "X-Requested-With": "XMLHttpRequest"
29594         };
29595         
29596         for (var headerName in headers) {
29597             var headerValue = headers[headerName];
29598             if (headerValue) {
29599                 this.xhr.setRequestHeader(headerName, headerValue);
29600             }
29601         }
29602         
29603         var _this = this;
29604         
29605         this.xhr.onload = function()
29606         {
29607             _this.xhrOnLoad(_this.xhr);
29608         }
29609         
29610         this.xhr.onerror = function()
29611         {
29612             _this.xhrOnError(_this.xhr);
29613         }
29614         
29615         var formData = new FormData();
29616
29617         formData.append('returnHTML', 'NO');
29618         
29619         formData.append('crop', crop);
29620         
29621         if(typeof(file.filename) != 'undefined'){
29622             formData.append('filename', file.filename);
29623         }
29624         
29625         if(typeof(file.mimetype) != 'undefined'){
29626             formData.append('mimetype', file.mimetype);
29627         }
29628         
29629         Roo.log(formData);
29630         
29631         if(this.fireEvent('prepare', this, formData) != false){
29632             this.xhr.send(formData);
29633         };
29634     }
29635 });
29636
29637 /*
29638 * Licence: LGPL
29639 */
29640
29641 /**
29642  * @class Roo.bootstrap.DocumentViewer
29643  * @extends Roo.bootstrap.Component
29644  * Bootstrap DocumentViewer class
29645  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29646  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29647  * 
29648  * @constructor
29649  * Create a new DocumentViewer
29650  * @param {Object} config The config object
29651  */
29652
29653 Roo.bootstrap.DocumentViewer = function(config){
29654     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29655     
29656     this.addEvents({
29657         /**
29658          * @event initial
29659          * Fire after initEvent
29660          * @param {Roo.bootstrap.DocumentViewer} this
29661          */
29662         "initial" : true,
29663         /**
29664          * @event click
29665          * Fire after click
29666          * @param {Roo.bootstrap.DocumentViewer} this
29667          */
29668         "click" : true,
29669         /**
29670          * @event download
29671          * Fire after download button
29672          * @param {Roo.bootstrap.DocumentViewer} this
29673          */
29674         "download" : true,
29675         /**
29676          * @event trash
29677          * Fire after trash button
29678          * @param {Roo.bootstrap.DocumentViewer} this
29679          */
29680         "trash" : true
29681         
29682     });
29683 };
29684
29685 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29686     
29687     showDownload : true,
29688     
29689     showTrash : true,
29690     
29691     getAutoCreate : function()
29692     {
29693         var cfg = {
29694             tag : 'div',
29695             cls : 'roo-document-viewer',
29696             cn : [
29697                 {
29698                     tag : 'div',
29699                     cls : 'roo-document-viewer-body',
29700                     cn : [
29701                         {
29702                             tag : 'div',
29703                             cls : 'roo-document-viewer-thumb',
29704                             cn : [
29705                                 {
29706                                     tag : 'img',
29707                                     cls : 'roo-document-viewer-image'
29708                                 }
29709                             ]
29710                         }
29711                     ]
29712                 },
29713                 {
29714                     tag : 'div',
29715                     cls : 'roo-document-viewer-footer',
29716                     cn : {
29717                         tag : 'div',
29718                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29719                         cn : [
29720                             {
29721                                 tag : 'div',
29722                                 cls : 'btn-group roo-document-viewer-download',
29723                                 cn : [
29724                                     {
29725                                         tag : 'button',
29726                                         cls : 'btn btn-default',
29727                                         html : '<i class="fa fa-download"></i>'
29728                                     }
29729                                 ]
29730                             },
29731                             {
29732                                 tag : 'div',
29733                                 cls : 'btn-group roo-document-viewer-trash',
29734                                 cn : [
29735                                     {
29736                                         tag : 'button',
29737                                         cls : 'btn btn-default',
29738                                         html : '<i class="fa fa-trash"></i>'
29739                                     }
29740                                 ]
29741                             }
29742                         ]
29743                     }
29744                 }
29745             ]
29746         };
29747         
29748         return cfg;
29749     },
29750     
29751     initEvents : function()
29752     {
29753         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29754         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29755         
29756         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29757         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29758         
29759         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29760         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29761         
29762         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29763         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29764         
29765         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29766         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29767         
29768         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29769         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29770         
29771         this.bodyEl.on('click', this.onClick, this);
29772         this.downloadBtn.on('click', this.onDownload, this);
29773         this.trashBtn.on('click', this.onTrash, this);
29774         
29775         this.downloadBtn.hide();
29776         this.trashBtn.hide();
29777         
29778         if(this.showDownload){
29779             this.downloadBtn.show();
29780         }
29781         
29782         if(this.showTrash){
29783             this.trashBtn.show();
29784         }
29785         
29786         if(!this.showDownload && !this.showTrash) {
29787             this.footerEl.hide();
29788         }
29789         
29790     },
29791     
29792     initial : function()
29793     {
29794         this.fireEvent('initial', this);
29795         
29796     },
29797     
29798     onClick : function(e)
29799     {
29800         e.preventDefault();
29801         
29802         this.fireEvent('click', this);
29803     },
29804     
29805     onDownload : function(e)
29806     {
29807         e.preventDefault();
29808         
29809         this.fireEvent('download', this);
29810     },
29811     
29812     onTrash : function(e)
29813     {
29814         e.preventDefault();
29815         
29816         this.fireEvent('trash', this);
29817     }
29818     
29819 });
29820 /*
29821  * - LGPL
29822  *
29823  * nav progress bar
29824  * 
29825  */
29826
29827 /**
29828  * @class Roo.bootstrap.NavProgressBar
29829  * @extends Roo.bootstrap.Component
29830  * Bootstrap NavProgressBar class
29831  * 
29832  * @constructor
29833  * Create a new nav progress bar
29834  * @param {Object} config The config object
29835  */
29836
29837 Roo.bootstrap.NavProgressBar = function(config){
29838     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29839
29840     this.bullets = this.bullets || [];
29841    
29842 //    Roo.bootstrap.NavProgressBar.register(this);
29843      this.addEvents({
29844         /**
29845              * @event changed
29846              * Fires when the active item changes
29847              * @param {Roo.bootstrap.NavProgressBar} this
29848              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29849              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29850          */
29851         'changed': true
29852      });
29853     
29854 };
29855
29856 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29857     
29858     bullets : [],
29859     barItems : [],
29860     
29861     getAutoCreate : function()
29862     {
29863         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29864         
29865         cfg = {
29866             tag : 'div',
29867             cls : 'roo-navigation-bar-group',
29868             cn : [
29869                 {
29870                     tag : 'div',
29871                     cls : 'roo-navigation-top-bar'
29872                 },
29873                 {
29874                     tag : 'div',
29875                     cls : 'roo-navigation-bullets-bar',
29876                     cn : [
29877                         {
29878                             tag : 'ul',
29879                             cls : 'roo-navigation-bar'
29880                         }
29881                     ]
29882                 },
29883                 
29884                 {
29885                     tag : 'div',
29886                     cls : 'roo-navigation-bottom-bar'
29887                 }
29888             ]
29889             
29890         };
29891         
29892         return cfg;
29893         
29894     },
29895     
29896     initEvents: function() 
29897     {
29898         
29899     },
29900     
29901     onRender : function(ct, position) 
29902     {
29903         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29904         
29905         if(this.bullets.length){
29906             Roo.each(this.bullets, function(b){
29907                this.addItem(b);
29908             }, this);
29909         }
29910         
29911         this.format();
29912         
29913     },
29914     
29915     addItem : function(cfg)
29916     {
29917         var item = new Roo.bootstrap.NavProgressItem(cfg);
29918         
29919         item.parentId = this.id;
29920         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29921         
29922         if(cfg.html){
29923             var top = new Roo.bootstrap.Element({
29924                 tag : 'div',
29925                 cls : 'roo-navigation-bar-text'
29926             });
29927             
29928             var bottom = new Roo.bootstrap.Element({
29929                 tag : 'div',
29930                 cls : 'roo-navigation-bar-text'
29931             });
29932             
29933             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29934             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29935             
29936             var topText = new Roo.bootstrap.Element({
29937                 tag : 'span',
29938                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29939             });
29940             
29941             var bottomText = new Roo.bootstrap.Element({
29942                 tag : 'span',
29943                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29944             });
29945             
29946             topText.onRender(top.el, null);
29947             bottomText.onRender(bottom.el, null);
29948             
29949             item.topEl = top;
29950             item.bottomEl = bottom;
29951         }
29952         
29953         this.barItems.push(item);
29954         
29955         return item;
29956     },
29957     
29958     getActive : function()
29959     {
29960         var active = false;
29961         
29962         Roo.each(this.barItems, function(v){
29963             
29964             if (!v.isActive()) {
29965                 return;
29966             }
29967             
29968             active = v;
29969             return false;
29970             
29971         });
29972         
29973         return active;
29974     },
29975     
29976     setActiveItem : function(item)
29977     {
29978         var prev = false;
29979         
29980         Roo.each(this.barItems, function(v){
29981             if (v.rid == item.rid) {
29982                 return ;
29983             }
29984             
29985             if (v.isActive()) {
29986                 v.setActive(false);
29987                 prev = v;
29988             }
29989         });
29990
29991         item.setActive(true);
29992         
29993         this.fireEvent('changed', this, item, prev);
29994     },
29995     
29996     getBarItem: function(rid)
29997     {
29998         var ret = false;
29999         
30000         Roo.each(this.barItems, function(e) {
30001             if (e.rid != rid) {
30002                 return;
30003             }
30004             
30005             ret =  e;
30006             return false;
30007         });
30008         
30009         return ret;
30010     },
30011     
30012     indexOfItem : function(item)
30013     {
30014         var index = false;
30015         
30016         Roo.each(this.barItems, function(v, i){
30017             
30018             if (v.rid != item.rid) {
30019                 return;
30020             }
30021             
30022             index = i;
30023             return false
30024         });
30025         
30026         return index;
30027     },
30028     
30029     setActiveNext : function()
30030     {
30031         var i = this.indexOfItem(this.getActive());
30032         
30033         if (i > this.barItems.length) {
30034             return;
30035         }
30036         
30037         this.setActiveItem(this.barItems[i+1]);
30038     },
30039     
30040     setActivePrev : function()
30041     {
30042         var i = this.indexOfItem(this.getActive());
30043         
30044         if (i  < 1) {
30045             return;
30046         }
30047         
30048         this.setActiveItem(this.barItems[i-1]);
30049     },
30050     
30051     format : function()
30052     {
30053         if(!this.barItems.length){
30054             return;
30055         }
30056      
30057         var width = 100 / this.barItems.length;
30058         
30059         Roo.each(this.barItems, function(i){
30060             i.el.setStyle('width', width + '%');
30061             i.topEl.el.setStyle('width', width + '%');
30062             i.bottomEl.el.setStyle('width', width + '%');
30063         }, this);
30064         
30065     }
30066     
30067 });
30068 /*
30069  * - LGPL
30070  *
30071  * Nav Progress Item
30072  * 
30073  */
30074
30075 /**
30076  * @class Roo.bootstrap.NavProgressItem
30077  * @extends Roo.bootstrap.Component
30078  * Bootstrap NavProgressItem class
30079  * @cfg {String} rid the reference id
30080  * @cfg {Boolean} active (true|false) Is item active default false
30081  * @cfg {Boolean} disabled (true|false) Is item active default false
30082  * @cfg {String} html
30083  * @cfg {String} position (top|bottom) text position default bottom
30084  * @cfg {String} icon show icon instead of number
30085  * 
30086  * @constructor
30087  * Create a new NavProgressItem
30088  * @param {Object} config The config object
30089  */
30090 Roo.bootstrap.NavProgressItem = function(config){
30091     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30092     this.addEvents({
30093         // raw events
30094         /**
30095          * @event click
30096          * The raw click event for the entire grid.
30097          * @param {Roo.bootstrap.NavProgressItem} this
30098          * @param {Roo.EventObject} e
30099          */
30100         "click" : true
30101     });
30102    
30103 };
30104
30105 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30106     
30107     rid : '',
30108     active : false,
30109     disabled : false,
30110     html : '',
30111     position : 'bottom',
30112     icon : false,
30113     
30114     getAutoCreate : function()
30115     {
30116         var iconCls = 'roo-navigation-bar-item-icon';
30117         
30118         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30119         
30120         var cfg = {
30121             tag: 'li',
30122             cls: 'roo-navigation-bar-item',
30123             cn : [
30124                 {
30125                     tag : 'i',
30126                     cls : iconCls
30127                 }
30128             ]
30129         };
30130         
30131         if(this.active){
30132             cfg.cls += ' active';
30133         }
30134         if(this.disabled){
30135             cfg.cls += ' disabled';
30136         }
30137         
30138         return cfg;
30139     },
30140     
30141     disable : function()
30142     {
30143         this.setDisabled(true);
30144     },
30145     
30146     enable : function()
30147     {
30148         this.setDisabled(false);
30149     },
30150     
30151     initEvents: function() 
30152     {
30153         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30154         
30155         this.iconEl.on('click', this.onClick, this);
30156     },
30157     
30158     onClick : function(e)
30159     {
30160         e.preventDefault();
30161         
30162         if(this.disabled){
30163             return;
30164         }
30165         
30166         if(this.fireEvent('click', this, e) === false){
30167             return;
30168         };
30169         
30170         this.parent().setActiveItem(this);
30171     },
30172     
30173     isActive: function () 
30174     {
30175         return this.active;
30176     },
30177     
30178     setActive : function(state)
30179     {
30180         if(this.active == state){
30181             return;
30182         }
30183         
30184         this.active = state;
30185         
30186         if (state) {
30187             this.el.addClass('active');
30188             return;
30189         }
30190         
30191         this.el.removeClass('active');
30192         
30193         return;
30194     },
30195     
30196     setDisabled : function(state)
30197     {
30198         if(this.disabled == state){
30199             return;
30200         }
30201         
30202         this.disabled = state;
30203         
30204         if (state) {
30205             this.el.addClass('disabled');
30206             return;
30207         }
30208         
30209         this.el.removeClass('disabled');
30210     },
30211     
30212     tooltipEl : function()
30213     {
30214         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30215     }
30216 });
30217  
30218
30219  /*
30220  * - LGPL
30221  *
30222  * FieldLabel
30223  * 
30224  */
30225
30226 /**
30227  * @class Roo.bootstrap.FieldLabel
30228  * @extends Roo.bootstrap.Component
30229  * Bootstrap FieldLabel class
30230  * @cfg {String} html contents of the element
30231  * @cfg {String} tag tag of the element default label
30232  * @cfg {String} cls class of the element
30233  * @cfg {String} target label target 
30234  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30235  * @cfg {String} invalidClass default "text-warning"
30236  * @cfg {String} validClass default "text-success"
30237  * @cfg {String} iconTooltip default "This field is required"
30238  * @cfg {String} indicatorpos (left|right) default left
30239  * 
30240  * @constructor
30241  * Create a new FieldLabel
30242  * @param {Object} config The config object
30243  */
30244
30245 Roo.bootstrap.FieldLabel = function(config){
30246     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30247     
30248     this.addEvents({
30249             /**
30250              * @event invalid
30251              * Fires after the field has been marked as invalid.
30252              * @param {Roo.form.FieldLabel} this
30253              * @param {String} msg The validation message
30254              */
30255             invalid : true,
30256             /**
30257              * @event valid
30258              * Fires after the field has been validated with no errors.
30259              * @param {Roo.form.FieldLabel} this
30260              */
30261             valid : true
30262         });
30263 };
30264
30265 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30266     
30267     tag: 'label',
30268     cls: '',
30269     html: '',
30270     target: '',
30271     allowBlank : true,
30272     invalidClass : 'has-warning',
30273     validClass : 'has-success',
30274     iconTooltip : 'This field is required',
30275     indicatorpos : 'left',
30276     
30277     getAutoCreate : function(){
30278         
30279         var cls = "";
30280         if (!this.allowBlank) {
30281             cls  = "visible";
30282         }
30283         
30284         var cfg = {
30285             tag : this.tag,
30286             cls : 'roo-bootstrap-field-label ' + this.cls,
30287             for : this.target,
30288             cn : [
30289                 {
30290                     tag : 'i',
30291                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30292                     tooltip : this.iconTooltip
30293                 },
30294                 {
30295                     tag : 'span',
30296                     html : this.html
30297                 }
30298             ] 
30299         };
30300         
30301         if(this.indicatorpos == 'right'){
30302             var cfg = {
30303                 tag : this.tag,
30304                 cls : 'roo-bootstrap-field-label ' + this.cls,
30305                 for : this.target,
30306                 cn : [
30307                     {
30308                         tag : 'span',
30309                         html : this.html
30310                     },
30311                     {
30312                         tag : 'i',
30313                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30314                         tooltip : this.iconTooltip
30315                     }
30316                 ] 
30317             };
30318         }
30319         
30320         return cfg;
30321     },
30322     
30323     initEvents: function() 
30324     {
30325         Roo.bootstrap.Element.superclass.initEvents.call(this);
30326         
30327         this.indicator = this.indicatorEl();
30328         
30329         if(this.indicator){
30330             this.indicator.removeClass('visible');
30331             this.indicator.addClass('invisible');
30332         }
30333         
30334         Roo.bootstrap.FieldLabel.register(this);
30335     },
30336     
30337     indicatorEl : function()
30338     {
30339         var indicator = this.el.select('i.roo-required-indicator',true).first();
30340         
30341         if(!indicator){
30342             return false;
30343         }
30344         
30345         return indicator;
30346         
30347     },
30348     
30349     /**
30350      * Mark this field as valid
30351      */
30352     markValid : function()
30353     {
30354         if(this.indicator){
30355             this.indicator.removeClass('visible');
30356             this.indicator.addClass('invisible');
30357         }
30358         
30359         this.el.removeClass(this.invalidClass);
30360         
30361         this.el.addClass(this.validClass);
30362         
30363         this.fireEvent('valid', this);
30364     },
30365     
30366     /**
30367      * Mark this field as invalid
30368      * @param {String} msg The validation message
30369      */
30370     markInvalid : function(msg)
30371     {
30372         if(this.indicator){
30373             this.indicator.removeClass('invisible');
30374             this.indicator.addClass('visible');
30375         }
30376         
30377         this.el.removeClass(this.validClass);
30378         
30379         this.el.addClass(this.invalidClass);
30380         
30381         this.fireEvent('invalid', this, msg);
30382     }
30383     
30384    
30385 });
30386
30387 Roo.apply(Roo.bootstrap.FieldLabel, {
30388     
30389     groups: {},
30390     
30391      /**
30392     * register a FieldLabel Group
30393     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30394     */
30395     register : function(label)
30396     {
30397         if(this.groups.hasOwnProperty(label.target)){
30398             return;
30399         }
30400      
30401         this.groups[label.target] = label;
30402         
30403     },
30404     /**
30405     * fetch a FieldLabel Group based on the target
30406     * @param {string} target
30407     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30408     */
30409     get: function(target) {
30410         if (typeof(this.groups[target]) == 'undefined') {
30411             return false;
30412         }
30413         
30414         return this.groups[target] ;
30415     }
30416 });
30417
30418  
30419
30420  /*
30421  * - LGPL
30422  *
30423  * page DateSplitField.
30424  * 
30425  */
30426
30427
30428 /**
30429  * @class Roo.bootstrap.DateSplitField
30430  * @extends Roo.bootstrap.Component
30431  * Bootstrap DateSplitField class
30432  * @cfg {string} fieldLabel - the label associated
30433  * @cfg {Number} labelWidth set the width of label (0-12)
30434  * @cfg {String} labelAlign (top|left)
30435  * @cfg {Boolean} dayAllowBlank (true|false) default false
30436  * @cfg {Boolean} monthAllowBlank (true|false) default false
30437  * @cfg {Boolean} yearAllowBlank (true|false) default false
30438  * @cfg {string} dayPlaceholder 
30439  * @cfg {string} monthPlaceholder
30440  * @cfg {string} yearPlaceholder
30441  * @cfg {string} dayFormat default 'd'
30442  * @cfg {string} monthFormat default 'm'
30443  * @cfg {string} yearFormat default 'Y'
30444  * @cfg {Number} labellg set the width of label (1-12)
30445  * @cfg {Number} labelmd set the width of label (1-12)
30446  * @cfg {Number} labelsm set the width of label (1-12)
30447  * @cfg {Number} labelxs set the width of label (1-12)
30448
30449  *     
30450  * @constructor
30451  * Create a new DateSplitField
30452  * @param {Object} config The config object
30453  */
30454
30455 Roo.bootstrap.DateSplitField = function(config){
30456     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30457     
30458     this.addEvents({
30459         // raw events
30460          /**
30461          * @event years
30462          * getting the data of years
30463          * @param {Roo.bootstrap.DateSplitField} this
30464          * @param {Object} years
30465          */
30466         "years" : true,
30467         /**
30468          * @event days
30469          * getting the data of days
30470          * @param {Roo.bootstrap.DateSplitField} this
30471          * @param {Object} days
30472          */
30473         "days" : true,
30474         /**
30475          * @event invalid
30476          * Fires after the field has been marked as invalid.
30477          * @param {Roo.form.Field} this
30478          * @param {String} msg The validation message
30479          */
30480         invalid : true,
30481        /**
30482          * @event valid
30483          * Fires after the field has been validated with no errors.
30484          * @param {Roo.form.Field} this
30485          */
30486         valid : true
30487     });
30488 };
30489
30490 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30491     
30492     fieldLabel : '',
30493     labelAlign : 'top',
30494     labelWidth : 3,
30495     dayAllowBlank : false,
30496     monthAllowBlank : false,
30497     yearAllowBlank : false,
30498     dayPlaceholder : '',
30499     monthPlaceholder : '',
30500     yearPlaceholder : '',
30501     dayFormat : 'd',
30502     monthFormat : 'm',
30503     yearFormat : 'Y',
30504     isFormField : true,
30505     labellg : 0,
30506     labelmd : 0,
30507     labelsm : 0,
30508     labelxs : 0,
30509     
30510     getAutoCreate : function()
30511     {
30512         var cfg = {
30513             tag : 'div',
30514             cls : 'row roo-date-split-field-group',
30515             cn : [
30516                 {
30517                     tag : 'input',
30518                     type : 'hidden',
30519                     cls : 'form-hidden-field roo-date-split-field-group-value',
30520                     name : this.name
30521                 }
30522             ]
30523         };
30524         
30525         var labelCls = 'col-md-12';
30526         var contentCls = 'col-md-4';
30527         
30528         if(this.fieldLabel){
30529             
30530             var label = {
30531                 tag : 'div',
30532                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30533                 cn : [
30534                     {
30535                         tag : 'label',
30536                         html : this.fieldLabel
30537                     }
30538                 ]
30539             };
30540             
30541             if(this.labelAlign == 'left'){
30542             
30543                 if(this.labelWidth > 12){
30544                     label.style = "width: " + this.labelWidth + 'px';
30545                 }
30546
30547                 if(this.labelWidth < 13 && this.labelmd == 0){
30548                     this.labelmd = this.labelWidth;
30549                 }
30550
30551                 if(this.labellg > 0){
30552                     labelCls = ' col-lg-' + this.labellg;
30553                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30554                 }
30555
30556                 if(this.labelmd > 0){
30557                     labelCls = ' col-md-' + this.labelmd;
30558                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30559                 }
30560
30561                 if(this.labelsm > 0){
30562                     labelCls = ' col-sm-' + this.labelsm;
30563                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30564                 }
30565
30566                 if(this.labelxs > 0){
30567                     labelCls = ' col-xs-' + this.labelxs;
30568                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30569                 }
30570             }
30571             
30572             label.cls += ' ' + labelCls;
30573             
30574             cfg.cn.push(label);
30575         }
30576         
30577         Roo.each(['day', 'month', 'year'], function(t){
30578             cfg.cn.push({
30579                 tag : 'div',
30580                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30581             });
30582         }, this);
30583         
30584         return cfg;
30585     },
30586     
30587     inputEl: function ()
30588     {
30589         return this.el.select('.roo-date-split-field-group-value', true).first();
30590     },
30591     
30592     onRender : function(ct, position) 
30593     {
30594         var _this = this;
30595         
30596         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30597         
30598         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30599         
30600         this.dayField = new Roo.bootstrap.ComboBox({
30601             allowBlank : this.dayAllowBlank,
30602             alwaysQuery : true,
30603             displayField : 'value',
30604             editable : false,
30605             fieldLabel : '',
30606             forceSelection : true,
30607             mode : 'local',
30608             placeholder : this.dayPlaceholder,
30609             selectOnFocus : true,
30610             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30611             triggerAction : 'all',
30612             typeAhead : true,
30613             valueField : 'value',
30614             store : new Roo.data.SimpleStore({
30615                 data : (function() {    
30616                     var days = [];
30617                     _this.fireEvent('days', _this, days);
30618                     return days;
30619                 })(),
30620                 fields : [ 'value' ]
30621             }),
30622             listeners : {
30623                 select : function (_self, record, index)
30624                 {
30625                     _this.setValue(_this.getValue());
30626                 }
30627             }
30628         });
30629
30630         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30631         
30632         this.monthField = new Roo.bootstrap.MonthField({
30633             after : '<i class=\"fa fa-calendar\"></i>',
30634             allowBlank : this.monthAllowBlank,
30635             placeholder : this.monthPlaceholder,
30636             readOnly : true,
30637             listeners : {
30638                 render : function (_self)
30639                 {
30640                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30641                         e.preventDefault();
30642                         _self.focus();
30643                     });
30644                 },
30645                 select : function (_self, oldvalue, newvalue)
30646                 {
30647                     _this.setValue(_this.getValue());
30648                 }
30649             }
30650         });
30651         
30652         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30653         
30654         this.yearField = new Roo.bootstrap.ComboBox({
30655             allowBlank : this.yearAllowBlank,
30656             alwaysQuery : true,
30657             displayField : 'value',
30658             editable : false,
30659             fieldLabel : '',
30660             forceSelection : true,
30661             mode : 'local',
30662             placeholder : this.yearPlaceholder,
30663             selectOnFocus : true,
30664             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30665             triggerAction : 'all',
30666             typeAhead : true,
30667             valueField : 'value',
30668             store : new Roo.data.SimpleStore({
30669                 data : (function() {
30670                     var years = [];
30671                     _this.fireEvent('years', _this, years);
30672                     return years;
30673                 })(),
30674                 fields : [ 'value' ]
30675             }),
30676             listeners : {
30677                 select : function (_self, record, index)
30678                 {
30679                     _this.setValue(_this.getValue());
30680                 }
30681             }
30682         });
30683
30684         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30685     },
30686     
30687     setValue : function(v, format)
30688     {
30689         this.inputEl.dom.value = v;
30690         
30691         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30692         
30693         var d = Date.parseDate(v, f);
30694         
30695         if(!d){
30696             this.validate();
30697             return;
30698         }
30699         
30700         this.setDay(d.format(this.dayFormat));
30701         this.setMonth(d.format(this.monthFormat));
30702         this.setYear(d.format(this.yearFormat));
30703         
30704         this.validate();
30705         
30706         return;
30707     },
30708     
30709     setDay : function(v)
30710     {
30711         this.dayField.setValue(v);
30712         this.inputEl.dom.value = this.getValue();
30713         this.validate();
30714         return;
30715     },
30716     
30717     setMonth : function(v)
30718     {
30719         this.monthField.setValue(v, true);
30720         this.inputEl.dom.value = this.getValue();
30721         this.validate();
30722         return;
30723     },
30724     
30725     setYear : function(v)
30726     {
30727         this.yearField.setValue(v);
30728         this.inputEl.dom.value = this.getValue();
30729         this.validate();
30730         return;
30731     },
30732     
30733     getDay : function()
30734     {
30735         return this.dayField.getValue();
30736     },
30737     
30738     getMonth : function()
30739     {
30740         return this.monthField.getValue();
30741     },
30742     
30743     getYear : function()
30744     {
30745         return this.yearField.getValue();
30746     },
30747     
30748     getValue : function()
30749     {
30750         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30751         
30752         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30753         
30754         return date;
30755     },
30756     
30757     reset : function()
30758     {
30759         this.setDay('');
30760         this.setMonth('');
30761         this.setYear('');
30762         this.inputEl.dom.value = '';
30763         this.validate();
30764         return;
30765     },
30766     
30767     validate : function()
30768     {
30769         var d = this.dayField.validate();
30770         var m = this.monthField.validate();
30771         var y = this.yearField.validate();
30772         
30773         var valid = true;
30774         
30775         if(
30776                 (!this.dayAllowBlank && !d) ||
30777                 (!this.monthAllowBlank && !m) ||
30778                 (!this.yearAllowBlank && !y)
30779         ){
30780             valid = false;
30781         }
30782         
30783         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30784             return valid;
30785         }
30786         
30787         if(valid){
30788             this.markValid();
30789             return valid;
30790         }
30791         
30792         this.markInvalid();
30793         
30794         return valid;
30795     },
30796     
30797     markValid : function()
30798     {
30799         
30800         var label = this.el.select('label', true).first();
30801         var icon = this.el.select('i.fa-star', true).first();
30802
30803         if(label && icon){
30804             icon.remove();
30805         }
30806         
30807         this.fireEvent('valid', this);
30808     },
30809     
30810      /**
30811      * Mark this field as invalid
30812      * @param {String} msg The validation message
30813      */
30814     markInvalid : function(msg)
30815     {
30816         
30817         var label = this.el.select('label', true).first();
30818         var icon = this.el.select('i.fa-star', true).first();
30819
30820         if(label && !icon){
30821             this.el.select('.roo-date-split-field-label', true).createChild({
30822                 tag : 'i',
30823                 cls : 'text-danger fa fa-lg fa-star',
30824                 tooltip : 'This field is required',
30825                 style : 'margin-right:5px;'
30826             }, label, true);
30827         }
30828         
30829         this.fireEvent('invalid', this, msg);
30830     },
30831     
30832     clearInvalid : function()
30833     {
30834         var label = this.el.select('label', true).first();
30835         var icon = this.el.select('i.fa-star', true).first();
30836
30837         if(label && icon){
30838             icon.remove();
30839         }
30840         
30841         this.fireEvent('valid', this);
30842     },
30843     
30844     getName: function()
30845     {
30846         return this.name;
30847     }
30848     
30849 });
30850
30851  /**
30852  *
30853  * This is based on 
30854  * http://masonry.desandro.com
30855  *
30856  * The idea is to render all the bricks based on vertical width...
30857  *
30858  * The original code extends 'outlayer' - we might need to use that....
30859  * 
30860  */
30861
30862
30863 /**
30864  * @class Roo.bootstrap.LayoutMasonry
30865  * @extends Roo.bootstrap.Component
30866  * Bootstrap Layout Masonry class
30867  * 
30868  * @constructor
30869  * Create a new Element
30870  * @param {Object} config The config object
30871  */
30872
30873 Roo.bootstrap.LayoutMasonry = function(config){
30874     
30875     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30876     
30877     this.bricks = [];
30878     
30879     Roo.bootstrap.LayoutMasonry.register(this);
30880     
30881     this.addEvents({
30882         // raw events
30883         /**
30884          * @event layout
30885          * Fire after layout the items
30886          * @param {Roo.bootstrap.LayoutMasonry} this
30887          * @param {Roo.EventObject} e
30888          */
30889         "layout" : true
30890     });
30891     
30892 };
30893
30894 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30895     
30896     /**
30897      * @cfg {Boolean} isLayoutInstant = no animation?
30898      */   
30899     isLayoutInstant : false, // needed?
30900    
30901     /**
30902      * @cfg {Number} boxWidth  width of the columns
30903      */   
30904     boxWidth : 450,
30905     
30906       /**
30907      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30908      */   
30909     boxHeight : 0,
30910     
30911     /**
30912      * @cfg {Number} padWidth padding below box..
30913      */   
30914     padWidth : 10, 
30915     
30916     /**
30917      * @cfg {Number} gutter gutter width..
30918      */   
30919     gutter : 10,
30920     
30921      /**
30922      * @cfg {Number} maxCols maximum number of columns
30923      */   
30924     
30925     maxCols: 0,
30926     
30927     /**
30928      * @cfg {Boolean} isAutoInitial defalut true
30929      */   
30930     isAutoInitial : true, 
30931     
30932     containerWidth: 0,
30933     
30934     /**
30935      * @cfg {Boolean} isHorizontal defalut false
30936      */   
30937     isHorizontal : false, 
30938
30939     currentSize : null,
30940     
30941     tag: 'div',
30942     
30943     cls: '',
30944     
30945     bricks: null, //CompositeElement
30946     
30947     cols : 1,
30948     
30949     _isLayoutInited : false,
30950     
30951 //    isAlternative : false, // only use for vertical layout...
30952     
30953     /**
30954      * @cfg {Number} alternativePadWidth padding below box..
30955      */   
30956     alternativePadWidth : 50,
30957     
30958     selectedBrick : [],
30959     
30960     getAutoCreate : function(){
30961         
30962         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30963         
30964         var cfg = {
30965             tag: this.tag,
30966             cls: 'blog-masonary-wrapper ' + this.cls,
30967             cn : {
30968                 cls : 'mas-boxes masonary'
30969             }
30970         };
30971         
30972         return cfg;
30973     },
30974     
30975     getChildContainer: function( )
30976     {
30977         if (this.boxesEl) {
30978             return this.boxesEl;
30979         }
30980         
30981         this.boxesEl = this.el.select('.mas-boxes').first();
30982         
30983         return this.boxesEl;
30984     },
30985     
30986     
30987     initEvents : function()
30988     {
30989         var _this = this;
30990         
30991         if(this.isAutoInitial){
30992             Roo.log('hook children rendered');
30993             this.on('childrenrendered', function() {
30994                 Roo.log('children rendered');
30995                 _this.initial();
30996             } ,this);
30997         }
30998     },
30999     
31000     initial : function()
31001     {
31002         this.selectedBrick = [];
31003         
31004         this.currentSize = this.el.getBox(true);
31005         
31006         Roo.EventManager.onWindowResize(this.resize, this); 
31007
31008         if(!this.isAutoInitial){
31009             this.layout();
31010             return;
31011         }
31012         
31013         this.layout();
31014         
31015         return;
31016         //this.layout.defer(500,this);
31017         
31018     },
31019     
31020     resize : function()
31021     {
31022         var cs = this.el.getBox(true);
31023         
31024         if (
31025                 this.currentSize.width == cs.width && 
31026                 this.currentSize.x == cs.x && 
31027                 this.currentSize.height == cs.height && 
31028                 this.currentSize.y == cs.y 
31029         ) {
31030             Roo.log("no change in with or X or Y");
31031             return;
31032         }
31033         
31034         this.currentSize = cs;
31035         
31036         this.layout();
31037         
31038     },
31039     
31040     layout : function()
31041     {   
31042         this._resetLayout();
31043         
31044         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31045         
31046         this.layoutItems( isInstant );
31047       
31048         this._isLayoutInited = true;
31049         
31050         this.fireEvent('layout', this);
31051         
31052     },
31053     
31054     _resetLayout : function()
31055     {
31056         if(this.isHorizontal){
31057             this.horizontalMeasureColumns();
31058             return;
31059         }
31060         
31061         this.verticalMeasureColumns();
31062         
31063     },
31064     
31065     verticalMeasureColumns : function()
31066     {
31067         this.getContainerWidth();
31068         
31069 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31070 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31071 //            return;
31072 //        }
31073         
31074         var boxWidth = this.boxWidth + this.padWidth;
31075         
31076         if(this.containerWidth < this.boxWidth){
31077             boxWidth = this.containerWidth
31078         }
31079         
31080         var containerWidth = this.containerWidth;
31081         
31082         var cols = Math.floor(containerWidth / boxWidth);
31083         
31084         this.cols = Math.max( cols, 1 );
31085         
31086         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31087         
31088         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31089         
31090         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31091         
31092         this.colWidth = boxWidth + avail - this.padWidth;
31093         
31094         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31095         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31096     },
31097     
31098     horizontalMeasureColumns : function()
31099     {
31100         this.getContainerWidth();
31101         
31102         var boxWidth = this.boxWidth;
31103         
31104         if(this.containerWidth < boxWidth){
31105             boxWidth = this.containerWidth;
31106         }
31107         
31108         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31109         
31110         this.el.setHeight(boxWidth);
31111         
31112     },
31113     
31114     getContainerWidth : function()
31115     {
31116         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31117     },
31118     
31119     layoutItems : function( isInstant )
31120     {
31121         Roo.log(this.bricks);
31122         
31123         var items = Roo.apply([], this.bricks);
31124         
31125         if(this.isHorizontal){
31126             this._horizontalLayoutItems( items , isInstant );
31127             return;
31128         }
31129         
31130 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31131 //            this._verticalAlternativeLayoutItems( items , isInstant );
31132 //            return;
31133 //        }
31134         
31135         this._verticalLayoutItems( items , isInstant );
31136         
31137     },
31138     
31139     _verticalLayoutItems : function ( items , isInstant)
31140     {
31141         if ( !items || !items.length ) {
31142             return;
31143         }
31144         
31145         var standard = [
31146             ['xs', 'xs', 'xs', 'tall'],
31147             ['xs', 'xs', 'tall'],
31148             ['xs', 'xs', 'sm'],
31149             ['xs', 'xs', 'xs'],
31150             ['xs', 'tall'],
31151             ['xs', 'sm'],
31152             ['xs', 'xs'],
31153             ['xs'],
31154             
31155             ['sm', 'xs', 'xs'],
31156             ['sm', 'xs'],
31157             ['sm'],
31158             
31159             ['tall', 'xs', 'xs', 'xs'],
31160             ['tall', 'xs', 'xs'],
31161             ['tall', 'xs'],
31162             ['tall']
31163             
31164         ];
31165         
31166         var queue = [];
31167         
31168         var boxes = [];
31169         
31170         var box = [];
31171         
31172         Roo.each(items, function(item, k){
31173             
31174             switch (item.size) {
31175                 // these layouts take up a full box,
31176                 case 'md' :
31177                 case 'md-left' :
31178                 case 'md-right' :
31179                 case 'wide' :
31180                     
31181                     if(box.length){
31182                         boxes.push(box);
31183                         box = [];
31184                     }
31185                     
31186                     boxes.push([item]);
31187                     
31188                     break;
31189                     
31190                 case 'xs' :
31191                 case 'sm' :
31192                 case 'tall' :
31193                     
31194                     box.push(item);
31195                     
31196                     break;
31197                 default :
31198                     break;
31199                     
31200             }
31201             
31202         }, this);
31203         
31204         if(box.length){
31205             boxes.push(box);
31206             box = [];
31207         }
31208         
31209         var filterPattern = function(box, length)
31210         {
31211             if(!box.length){
31212                 return;
31213             }
31214             
31215             var match = false;
31216             
31217             var pattern = box.slice(0, length);
31218             
31219             var format = [];
31220             
31221             Roo.each(pattern, function(i){
31222                 format.push(i.size);
31223             }, this);
31224             
31225             Roo.each(standard, function(s){
31226                 
31227                 if(String(s) != String(format)){
31228                     return;
31229                 }
31230                 
31231                 match = true;
31232                 return false;
31233                 
31234             }, this);
31235             
31236             if(!match && length == 1){
31237                 return;
31238             }
31239             
31240             if(!match){
31241                 filterPattern(box, length - 1);
31242                 return;
31243             }
31244                 
31245             queue.push(pattern);
31246
31247             box = box.slice(length, box.length);
31248
31249             filterPattern(box, 4);
31250
31251             return;
31252             
31253         }
31254         
31255         Roo.each(boxes, function(box, k){
31256             
31257             if(!box.length){
31258                 return;
31259             }
31260             
31261             if(box.length == 1){
31262                 queue.push(box);
31263                 return;
31264             }
31265             
31266             filterPattern(box, 4);
31267             
31268         }, this);
31269         
31270         this._processVerticalLayoutQueue( queue, isInstant );
31271         
31272     },
31273     
31274 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31275 //    {
31276 //        if ( !items || !items.length ) {
31277 //            return;
31278 //        }
31279 //
31280 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31281 //        
31282 //    },
31283     
31284     _horizontalLayoutItems : function ( items , isInstant)
31285     {
31286         if ( !items || !items.length || items.length < 3) {
31287             return;
31288         }
31289         
31290         items.reverse();
31291         
31292         var eItems = items.slice(0, 3);
31293         
31294         items = items.slice(3, items.length);
31295         
31296         var standard = [
31297             ['xs', 'xs', 'xs', 'wide'],
31298             ['xs', 'xs', 'wide'],
31299             ['xs', 'xs', 'sm'],
31300             ['xs', 'xs', 'xs'],
31301             ['xs', 'wide'],
31302             ['xs', 'sm'],
31303             ['xs', 'xs'],
31304             ['xs'],
31305             
31306             ['sm', 'xs', 'xs'],
31307             ['sm', 'xs'],
31308             ['sm'],
31309             
31310             ['wide', 'xs', 'xs', 'xs'],
31311             ['wide', 'xs', 'xs'],
31312             ['wide', 'xs'],
31313             ['wide'],
31314             
31315             ['wide-thin']
31316         ];
31317         
31318         var queue = [];
31319         
31320         var boxes = [];
31321         
31322         var box = [];
31323         
31324         Roo.each(items, function(item, k){
31325             
31326             switch (item.size) {
31327                 case 'md' :
31328                 case 'md-left' :
31329                 case 'md-right' :
31330                 case 'tall' :
31331                     
31332                     if(box.length){
31333                         boxes.push(box);
31334                         box = [];
31335                     }
31336                     
31337                     boxes.push([item]);
31338                     
31339                     break;
31340                     
31341                 case 'xs' :
31342                 case 'sm' :
31343                 case 'wide' :
31344                 case 'wide-thin' :
31345                     
31346                     box.push(item);
31347                     
31348                     break;
31349                 default :
31350                     break;
31351                     
31352             }
31353             
31354         }, this);
31355         
31356         if(box.length){
31357             boxes.push(box);
31358             box = [];
31359         }
31360         
31361         var filterPattern = function(box, length)
31362         {
31363             if(!box.length){
31364                 return;
31365             }
31366             
31367             var match = false;
31368             
31369             var pattern = box.slice(0, length);
31370             
31371             var format = [];
31372             
31373             Roo.each(pattern, function(i){
31374                 format.push(i.size);
31375             }, this);
31376             
31377             Roo.each(standard, function(s){
31378                 
31379                 if(String(s) != String(format)){
31380                     return;
31381                 }
31382                 
31383                 match = true;
31384                 return false;
31385                 
31386             }, this);
31387             
31388             if(!match && length == 1){
31389                 return;
31390             }
31391             
31392             if(!match){
31393                 filterPattern(box, length - 1);
31394                 return;
31395             }
31396                 
31397             queue.push(pattern);
31398
31399             box = box.slice(length, box.length);
31400
31401             filterPattern(box, 4);
31402
31403             return;
31404             
31405         }
31406         
31407         Roo.each(boxes, function(box, k){
31408             
31409             if(!box.length){
31410                 return;
31411             }
31412             
31413             if(box.length == 1){
31414                 queue.push(box);
31415                 return;
31416             }
31417             
31418             filterPattern(box, 4);
31419             
31420         }, this);
31421         
31422         
31423         var prune = [];
31424         
31425         var pos = this.el.getBox(true);
31426         
31427         var minX = pos.x;
31428         
31429         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31430         
31431         var hit_end = false;
31432         
31433         Roo.each(queue, function(box){
31434             
31435             if(hit_end){
31436                 
31437                 Roo.each(box, function(b){
31438                 
31439                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31440                     b.el.hide();
31441
31442                 }, this);
31443
31444                 return;
31445             }
31446             
31447             var mx = 0;
31448             
31449             Roo.each(box, function(b){
31450                 
31451                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31452                 b.el.show();
31453
31454                 mx = Math.max(mx, b.x);
31455                 
31456             }, this);
31457             
31458             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31459             
31460             if(maxX < minX){
31461                 
31462                 Roo.each(box, function(b){
31463                 
31464                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31465                     b.el.hide();
31466                     
31467                 }, this);
31468                 
31469                 hit_end = true;
31470                 
31471                 return;
31472             }
31473             
31474             prune.push(box);
31475             
31476         }, this);
31477         
31478         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31479     },
31480     
31481     /** Sets position of item in DOM
31482     * @param {Element} item
31483     * @param {Number} x - horizontal position
31484     * @param {Number} y - vertical position
31485     * @param {Boolean} isInstant - disables transitions
31486     */
31487     _processVerticalLayoutQueue : function( queue, isInstant )
31488     {
31489         var pos = this.el.getBox(true);
31490         var x = pos.x;
31491         var y = pos.y;
31492         var maxY = [];
31493         
31494         for (var i = 0; i < this.cols; i++){
31495             maxY[i] = pos.y;
31496         }
31497         
31498         Roo.each(queue, function(box, k){
31499             
31500             var col = k % this.cols;
31501             
31502             Roo.each(box, function(b,kk){
31503                 
31504                 b.el.position('absolute');
31505                 
31506                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31507                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31508                 
31509                 if(b.size == 'md-left' || b.size == 'md-right'){
31510                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31511                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31512                 }
31513                 
31514                 b.el.setWidth(width);
31515                 b.el.setHeight(height);
31516                 // iframe?
31517                 b.el.select('iframe',true).setSize(width,height);
31518                 
31519             }, this);
31520             
31521             for (var i = 0; i < this.cols; i++){
31522                 
31523                 if(maxY[i] < maxY[col]){
31524                     col = i;
31525                     continue;
31526                 }
31527                 
31528                 col = Math.min(col, i);
31529                 
31530             }
31531             
31532             x = pos.x + col * (this.colWidth + this.padWidth);
31533             
31534             y = maxY[col];
31535             
31536             var positions = [];
31537             
31538             switch (box.length){
31539                 case 1 :
31540                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31541                     break;
31542                 case 2 :
31543                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31544                     break;
31545                 case 3 :
31546                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31547                     break;
31548                 case 4 :
31549                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31550                     break;
31551                 default :
31552                     break;
31553             }
31554             
31555             Roo.each(box, function(b,kk){
31556                 
31557                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31558                 
31559                 var sz = b.el.getSize();
31560                 
31561                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31562                 
31563             }, this);
31564             
31565         }, this);
31566         
31567         var mY = 0;
31568         
31569         for (var i = 0; i < this.cols; i++){
31570             mY = Math.max(mY, maxY[i]);
31571         }
31572         
31573         this.el.setHeight(mY - pos.y);
31574         
31575     },
31576     
31577 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31578 //    {
31579 //        var pos = this.el.getBox(true);
31580 //        var x = pos.x;
31581 //        var y = pos.y;
31582 //        var maxX = pos.right;
31583 //        
31584 //        var maxHeight = 0;
31585 //        
31586 //        Roo.each(items, function(item, k){
31587 //            
31588 //            var c = k % 2;
31589 //            
31590 //            item.el.position('absolute');
31591 //                
31592 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31593 //
31594 //            item.el.setWidth(width);
31595 //
31596 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31597 //
31598 //            item.el.setHeight(height);
31599 //            
31600 //            if(c == 0){
31601 //                item.el.setXY([x, y], isInstant ? false : true);
31602 //            } else {
31603 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31604 //            }
31605 //            
31606 //            y = y + height + this.alternativePadWidth;
31607 //            
31608 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31609 //            
31610 //        }, this);
31611 //        
31612 //        this.el.setHeight(maxHeight);
31613 //        
31614 //    },
31615     
31616     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31617     {
31618         var pos = this.el.getBox(true);
31619         
31620         var minX = pos.x;
31621         var minY = pos.y;
31622         
31623         var maxX = pos.right;
31624         
31625         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31626         
31627         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31628         
31629         Roo.each(queue, function(box, k){
31630             
31631             Roo.each(box, function(b, kk){
31632                 
31633                 b.el.position('absolute');
31634                 
31635                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31636                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31637                 
31638                 if(b.size == 'md-left' || b.size == 'md-right'){
31639                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31640                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31641                 }
31642                 
31643                 b.el.setWidth(width);
31644                 b.el.setHeight(height);
31645                 
31646             }, this);
31647             
31648             if(!box.length){
31649                 return;
31650             }
31651             
31652             var positions = [];
31653             
31654             switch (box.length){
31655                 case 1 :
31656                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31657                     break;
31658                 case 2 :
31659                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31660                     break;
31661                 case 3 :
31662                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31663                     break;
31664                 case 4 :
31665                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31666                     break;
31667                 default :
31668                     break;
31669             }
31670             
31671             Roo.each(box, function(b,kk){
31672                 
31673                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31674                 
31675                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31676                 
31677             }, this);
31678             
31679         }, this);
31680         
31681     },
31682     
31683     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31684     {
31685         Roo.each(eItems, function(b,k){
31686             
31687             b.size = (k == 0) ? 'sm' : 'xs';
31688             b.x = (k == 0) ? 2 : 1;
31689             b.y = (k == 0) ? 2 : 1;
31690             
31691             b.el.position('absolute');
31692             
31693             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31694                 
31695             b.el.setWidth(width);
31696             
31697             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31698             
31699             b.el.setHeight(height);
31700             
31701         }, this);
31702
31703         var positions = [];
31704         
31705         positions.push({
31706             x : maxX - this.unitWidth * 2 - this.gutter,
31707             y : minY
31708         });
31709         
31710         positions.push({
31711             x : maxX - this.unitWidth,
31712             y : minY + (this.unitWidth + this.gutter) * 2
31713         });
31714         
31715         positions.push({
31716             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31717             y : minY
31718         });
31719         
31720         Roo.each(eItems, function(b,k){
31721             
31722             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31723
31724         }, this);
31725         
31726     },
31727     
31728     getVerticalOneBoxColPositions : function(x, y, box)
31729     {
31730         var pos = [];
31731         
31732         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31733         
31734         if(box[0].size == 'md-left'){
31735             rand = 0;
31736         }
31737         
31738         if(box[0].size == 'md-right'){
31739             rand = 1;
31740         }
31741         
31742         pos.push({
31743             x : x + (this.unitWidth + this.gutter) * rand,
31744             y : y
31745         });
31746         
31747         return pos;
31748     },
31749     
31750     getVerticalTwoBoxColPositions : function(x, y, box)
31751     {
31752         var pos = [];
31753         
31754         if(box[0].size == 'xs'){
31755             
31756             pos.push({
31757                 x : x,
31758                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31759             });
31760
31761             pos.push({
31762                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31763                 y : y
31764             });
31765             
31766             return pos;
31767             
31768         }
31769         
31770         pos.push({
31771             x : x,
31772             y : y
31773         });
31774
31775         pos.push({
31776             x : x + (this.unitWidth + this.gutter) * 2,
31777             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31778         });
31779         
31780         return pos;
31781         
31782     },
31783     
31784     getVerticalThreeBoxColPositions : function(x, y, box)
31785     {
31786         var pos = [];
31787         
31788         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31789             
31790             pos.push({
31791                 x : x,
31792                 y : y
31793             });
31794
31795             pos.push({
31796                 x : x + (this.unitWidth + this.gutter) * 1,
31797                 y : y
31798             });
31799             
31800             pos.push({
31801                 x : x + (this.unitWidth + this.gutter) * 2,
31802                 y : y
31803             });
31804             
31805             return pos;
31806             
31807         }
31808         
31809         if(box[0].size == 'xs' && box[1].size == 'xs'){
31810             
31811             pos.push({
31812                 x : x,
31813                 y : y
31814             });
31815
31816             pos.push({
31817                 x : x,
31818                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31819             });
31820             
31821             pos.push({
31822                 x : x + (this.unitWidth + this.gutter) * 1,
31823                 y : y
31824             });
31825             
31826             return pos;
31827             
31828         }
31829         
31830         pos.push({
31831             x : x,
31832             y : y
31833         });
31834
31835         pos.push({
31836             x : x + (this.unitWidth + this.gutter) * 2,
31837             y : y
31838         });
31839
31840         pos.push({
31841             x : x + (this.unitWidth + this.gutter) * 2,
31842             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31843         });
31844             
31845         return pos;
31846         
31847     },
31848     
31849     getVerticalFourBoxColPositions : function(x, y, box)
31850     {
31851         var pos = [];
31852         
31853         if(box[0].size == 'xs'){
31854             
31855             pos.push({
31856                 x : x,
31857                 y : y
31858             });
31859
31860             pos.push({
31861                 x : x,
31862                 y : y + (this.unitHeight + this.gutter) * 1
31863             });
31864             
31865             pos.push({
31866                 x : x,
31867                 y : y + (this.unitHeight + this.gutter) * 2
31868             });
31869             
31870             pos.push({
31871                 x : x + (this.unitWidth + this.gutter) * 1,
31872                 y : y
31873             });
31874             
31875             return pos;
31876             
31877         }
31878         
31879         pos.push({
31880             x : x,
31881             y : y
31882         });
31883
31884         pos.push({
31885             x : x + (this.unitWidth + this.gutter) * 2,
31886             y : y
31887         });
31888
31889         pos.push({
31890             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31891             y : y + (this.unitHeight + this.gutter) * 1
31892         });
31893
31894         pos.push({
31895             x : x + (this.unitWidth + this.gutter) * 2,
31896             y : y + (this.unitWidth + this.gutter) * 2
31897         });
31898
31899         return pos;
31900         
31901     },
31902     
31903     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31904     {
31905         var pos = [];
31906         
31907         if(box[0].size == 'md-left'){
31908             pos.push({
31909                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31910                 y : minY
31911             });
31912             
31913             return pos;
31914         }
31915         
31916         if(box[0].size == 'md-right'){
31917             pos.push({
31918                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31919                 y : minY + (this.unitWidth + this.gutter) * 1
31920             });
31921             
31922             return pos;
31923         }
31924         
31925         var rand = Math.floor(Math.random() * (4 - box[0].y));
31926         
31927         pos.push({
31928             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31929             y : minY + (this.unitWidth + this.gutter) * rand
31930         });
31931         
31932         return pos;
31933         
31934     },
31935     
31936     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31937     {
31938         var pos = [];
31939         
31940         if(box[0].size == 'xs'){
31941             
31942             pos.push({
31943                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31944                 y : minY
31945             });
31946
31947             pos.push({
31948                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31949                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31950             });
31951             
31952             return pos;
31953             
31954         }
31955         
31956         pos.push({
31957             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31958             y : minY
31959         });
31960
31961         pos.push({
31962             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31963             y : minY + (this.unitWidth + this.gutter) * 2
31964         });
31965         
31966         return pos;
31967         
31968     },
31969     
31970     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31971     {
31972         var pos = [];
31973         
31974         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31975             
31976             pos.push({
31977                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31978                 y : minY
31979             });
31980
31981             pos.push({
31982                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31983                 y : minY + (this.unitWidth + this.gutter) * 1
31984             });
31985             
31986             pos.push({
31987                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31988                 y : minY + (this.unitWidth + this.gutter) * 2
31989             });
31990             
31991             return pos;
31992             
31993         }
31994         
31995         if(box[0].size == 'xs' && box[1].size == 'xs'){
31996             
31997             pos.push({
31998                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31999                 y : minY
32000             });
32001
32002             pos.push({
32003                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32004                 y : minY
32005             });
32006             
32007             pos.push({
32008                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32009                 y : minY + (this.unitWidth + this.gutter) * 1
32010             });
32011             
32012             return pos;
32013             
32014         }
32015         
32016         pos.push({
32017             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32018             y : minY
32019         });
32020
32021         pos.push({
32022             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32023             y : minY + (this.unitWidth + this.gutter) * 2
32024         });
32025
32026         pos.push({
32027             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32028             y : minY + (this.unitWidth + this.gutter) * 2
32029         });
32030             
32031         return pos;
32032         
32033     },
32034     
32035     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32036     {
32037         var pos = [];
32038         
32039         if(box[0].size == 'xs'){
32040             
32041             pos.push({
32042                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32043                 y : minY
32044             });
32045
32046             pos.push({
32047                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32048                 y : minY
32049             });
32050             
32051             pos.push({
32052                 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),
32053                 y : minY
32054             });
32055             
32056             pos.push({
32057                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32058                 y : minY + (this.unitWidth + this.gutter) * 1
32059             });
32060             
32061             return pos;
32062             
32063         }
32064         
32065         pos.push({
32066             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32067             y : minY
32068         });
32069         
32070         pos.push({
32071             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32072             y : minY + (this.unitWidth + this.gutter) * 2
32073         });
32074         
32075         pos.push({
32076             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32077             y : minY + (this.unitWidth + this.gutter) * 2
32078         });
32079         
32080         pos.push({
32081             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),
32082             y : minY + (this.unitWidth + this.gutter) * 2
32083         });
32084
32085         return pos;
32086         
32087     },
32088     
32089     /**
32090     * remove a Masonry Brick
32091     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32092     */
32093     removeBrick : function(brick_id)
32094     {
32095         if (!brick_id) {
32096             return;
32097         }
32098         
32099         for (var i = 0; i<this.bricks.length; i++) {
32100             if (this.bricks[i].id == brick_id) {
32101                 this.bricks.splice(i,1);
32102                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32103                 this.initial();
32104             }
32105         }
32106     },
32107     
32108     /**
32109     * adds a Masonry Brick
32110     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32111     */
32112     addBrick : function(cfg)
32113     {
32114         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32115         //this.register(cn);
32116         cn.parentId = this.id;
32117         cn.render(this.el);
32118         return cn;
32119     },
32120     
32121     /**
32122     * register a Masonry Brick
32123     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32124     */
32125     
32126     register : function(brick)
32127     {
32128         this.bricks.push(brick);
32129         brick.masonryId = this.id;
32130     },
32131     
32132     /**
32133     * clear all the Masonry Brick
32134     */
32135     clearAll : function()
32136     {
32137         this.bricks = [];
32138         //this.getChildContainer().dom.innerHTML = "";
32139         this.el.dom.innerHTML = '';
32140     },
32141     
32142     getSelected : function()
32143     {
32144         if (!this.selectedBrick) {
32145             return false;
32146         }
32147         
32148         return this.selectedBrick;
32149     }
32150 });
32151
32152 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32153     
32154     groups: {},
32155      /**
32156     * register a Masonry Layout
32157     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32158     */
32159     
32160     register : function(layout)
32161     {
32162         this.groups[layout.id] = layout;
32163     },
32164     /**
32165     * fetch a  Masonry Layout based on the masonry layout ID
32166     * @param {string} the masonry layout to add
32167     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32168     */
32169     
32170     get: function(layout_id) {
32171         if (typeof(this.groups[layout_id]) == 'undefined') {
32172             return false;
32173         }
32174         return this.groups[layout_id] ;
32175     }
32176     
32177     
32178     
32179 });
32180
32181  
32182
32183  /**
32184  *
32185  * This is based on 
32186  * http://masonry.desandro.com
32187  *
32188  * The idea is to render all the bricks based on vertical width...
32189  *
32190  * The original code extends 'outlayer' - we might need to use that....
32191  * 
32192  */
32193
32194
32195 /**
32196  * @class Roo.bootstrap.LayoutMasonryAuto
32197  * @extends Roo.bootstrap.Component
32198  * Bootstrap Layout Masonry class
32199  * 
32200  * @constructor
32201  * Create a new Element
32202  * @param {Object} config The config object
32203  */
32204
32205 Roo.bootstrap.LayoutMasonryAuto = function(config){
32206     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32207 };
32208
32209 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32210     
32211       /**
32212      * @cfg {Boolean} isFitWidth  - resize the width..
32213      */   
32214     isFitWidth : false,  // options..
32215     /**
32216      * @cfg {Boolean} isOriginLeft = left align?
32217      */   
32218     isOriginLeft : true,
32219     /**
32220      * @cfg {Boolean} isOriginTop = top align?
32221      */   
32222     isOriginTop : false,
32223     /**
32224      * @cfg {Boolean} isLayoutInstant = no animation?
32225      */   
32226     isLayoutInstant : false, // needed?
32227     /**
32228      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32229      */   
32230     isResizingContainer : true,
32231     /**
32232      * @cfg {Number} columnWidth  width of the columns 
32233      */   
32234     
32235     columnWidth : 0,
32236     
32237     /**
32238      * @cfg {Number} maxCols maximum number of columns
32239      */   
32240     
32241     maxCols: 0,
32242     /**
32243      * @cfg {Number} padHeight padding below box..
32244      */   
32245     
32246     padHeight : 10, 
32247     
32248     /**
32249      * @cfg {Boolean} isAutoInitial defalut true
32250      */   
32251     
32252     isAutoInitial : true, 
32253     
32254     // private?
32255     gutter : 0,
32256     
32257     containerWidth: 0,
32258     initialColumnWidth : 0,
32259     currentSize : null,
32260     
32261     colYs : null, // array.
32262     maxY : 0,
32263     padWidth: 10,
32264     
32265     
32266     tag: 'div',
32267     cls: '',
32268     bricks: null, //CompositeElement
32269     cols : 0, // array?
32270     // element : null, // wrapped now this.el
32271     _isLayoutInited : null, 
32272     
32273     
32274     getAutoCreate : function(){
32275         
32276         var cfg = {
32277             tag: this.tag,
32278             cls: 'blog-masonary-wrapper ' + this.cls,
32279             cn : {
32280                 cls : 'mas-boxes masonary'
32281             }
32282         };
32283         
32284         return cfg;
32285     },
32286     
32287     getChildContainer: function( )
32288     {
32289         if (this.boxesEl) {
32290             return this.boxesEl;
32291         }
32292         
32293         this.boxesEl = this.el.select('.mas-boxes').first();
32294         
32295         return this.boxesEl;
32296     },
32297     
32298     
32299     initEvents : function()
32300     {
32301         var _this = this;
32302         
32303         if(this.isAutoInitial){
32304             Roo.log('hook children rendered');
32305             this.on('childrenrendered', function() {
32306                 Roo.log('children rendered');
32307                 _this.initial();
32308             } ,this);
32309         }
32310         
32311     },
32312     
32313     initial : function()
32314     {
32315         this.reloadItems();
32316
32317         this.currentSize = this.el.getBox(true);
32318
32319         /// was window resize... - let's see if this works..
32320         Roo.EventManager.onWindowResize(this.resize, this); 
32321
32322         if(!this.isAutoInitial){
32323             this.layout();
32324             return;
32325         }
32326         
32327         this.layout.defer(500,this);
32328     },
32329     
32330     reloadItems: function()
32331     {
32332         this.bricks = this.el.select('.masonry-brick', true);
32333         
32334         this.bricks.each(function(b) {
32335             //Roo.log(b.getSize());
32336             if (!b.attr('originalwidth')) {
32337                 b.attr('originalwidth',  b.getSize().width);
32338             }
32339             
32340         });
32341         
32342         Roo.log(this.bricks.elements.length);
32343     },
32344     
32345     resize : function()
32346     {
32347         Roo.log('resize');
32348         var cs = this.el.getBox(true);
32349         
32350         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32351             Roo.log("no change in with or X");
32352             return;
32353         }
32354         this.currentSize = cs;
32355         this.layout();
32356     },
32357     
32358     layout : function()
32359     {
32360          Roo.log('layout');
32361         this._resetLayout();
32362         //this._manageStamps();
32363       
32364         // don't animate first layout
32365         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32366         this.layoutItems( isInstant );
32367       
32368         // flag for initalized
32369         this._isLayoutInited = true;
32370     },
32371     
32372     layoutItems : function( isInstant )
32373     {
32374         //var items = this._getItemsForLayout( this.items );
32375         // original code supports filtering layout items.. we just ignore it..
32376         
32377         this._layoutItems( this.bricks , isInstant );
32378       
32379         this._postLayout();
32380     },
32381     _layoutItems : function ( items , isInstant)
32382     {
32383        //this.fireEvent( 'layout', this, items );
32384     
32385
32386         if ( !items || !items.elements.length ) {
32387           // no items, emit event with empty array
32388             return;
32389         }
32390
32391         var queue = [];
32392         items.each(function(item) {
32393             Roo.log("layout item");
32394             Roo.log(item);
32395             // get x/y object from method
32396             var position = this._getItemLayoutPosition( item );
32397             // enqueue
32398             position.item = item;
32399             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32400             queue.push( position );
32401         }, this);
32402       
32403         this._processLayoutQueue( queue );
32404     },
32405     /** Sets position of item in DOM
32406     * @param {Element} item
32407     * @param {Number} x - horizontal position
32408     * @param {Number} y - vertical position
32409     * @param {Boolean} isInstant - disables transitions
32410     */
32411     _processLayoutQueue : function( queue )
32412     {
32413         for ( var i=0, len = queue.length; i < len; i++ ) {
32414             var obj = queue[i];
32415             obj.item.position('absolute');
32416             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32417         }
32418     },
32419       
32420     
32421     /**
32422     * Any logic you want to do after each layout,
32423     * i.e. size the container
32424     */
32425     _postLayout : function()
32426     {
32427         this.resizeContainer();
32428     },
32429     
32430     resizeContainer : function()
32431     {
32432         if ( !this.isResizingContainer ) {
32433             return;
32434         }
32435         var size = this._getContainerSize();
32436         if ( size ) {
32437             this.el.setSize(size.width,size.height);
32438             this.boxesEl.setSize(size.width,size.height);
32439         }
32440     },
32441     
32442     
32443     
32444     _resetLayout : function()
32445     {
32446         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32447         this.colWidth = this.el.getWidth();
32448         //this.gutter = this.el.getWidth(); 
32449         
32450         this.measureColumns();
32451
32452         // reset column Y
32453         var i = this.cols;
32454         this.colYs = [];
32455         while (i--) {
32456             this.colYs.push( 0 );
32457         }
32458     
32459         this.maxY = 0;
32460     },
32461
32462     measureColumns : function()
32463     {
32464         this.getContainerWidth();
32465       // if columnWidth is 0, default to outerWidth of first item
32466         if ( !this.columnWidth ) {
32467             var firstItem = this.bricks.first();
32468             Roo.log(firstItem);
32469             this.columnWidth  = this.containerWidth;
32470             if (firstItem && firstItem.attr('originalwidth') ) {
32471                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32472             }
32473             // columnWidth fall back to item of first element
32474             Roo.log("set column width?");
32475                         this.initialColumnWidth = this.columnWidth  ;
32476
32477             // if first elem has no width, default to size of container
32478             
32479         }
32480         
32481         
32482         if (this.initialColumnWidth) {
32483             this.columnWidth = this.initialColumnWidth;
32484         }
32485         
32486         
32487             
32488         // column width is fixed at the top - however if container width get's smaller we should
32489         // reduce it...
32490         
32491         // this bit calcs how man columns..
32492             
32493         var columnWidth = this.columnWidth += this.gutter;
32494       
32495         // calculate columns
32496         var containerWidth = this.containerWidth + this.gutter;
32497         
32498         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32499         // fix rounding errors, typically with gutters
32500         var excess = columnWidth - containerWidth % columnWidth;
32501         
32502         
32503         // if overshoot is less than a pixel, round up, otherwise floor it
32504         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32505         cols = Math[ mathMethod ]( cols );
32506         this.cols = Math.max( cols, 1 );
32507         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32508         
32509          // padding positioning..
32510         var totalColWidth = this.cols * this.columnWidth;
32511         var padavail = this.containerWidth - totalColWidth;
32512         // so for 2 columns - we need 3 'pads'
32513         
32514         var padNeeded = (1+this.cols) * this.padWidth;
32515         
32516         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32517         
32518         this.columnWidth += padExtra
32519         //this.padWidth = Math.floor(padavail /  ( this.cols));
32520         
32521         // adjust colum width so that padding is fixed??
32522         
32523         // we have 3 columns ... total = width * 3
32524         // we have X left over... that should be used by 
32525         
32526         //if (this.expandC) {
32527             
32528         //}
32529         
32530         
32531         
32532     },
32533     
32534     getContainerWidth : function()
32535     {
32536        /* // container is parent if fit width
32537         var container = this.isFitWidth ? this.element.parentNode : this.element;
32538         // check that this.size and size are there
32539         // IE8 triggers resize on body size change, so they might not be
32540         
32541         var size = getSize( container );  //FIXME
32542         this.containerWidth = size && size.innerWidth; //FIXME
32543         */
32544          
32545         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32546         
32547     },
32548     
32549     _getItemLayoutPosition : function( item )  // what is item?
32550     {
32551         // we resize the item to our columnWidth..
32552       
32553         item.setWidth(this.columnWidth);
32554         item.autoBoxAdjust  = false;
32555         
32556         var sz = item.getSize();
32557  
32558         // how many columns does this brick span
32559         var remainder = this.containerWidth % this.columnWidth;
32560         
32561         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32562         // round if off by 1 pixel, otherwise use ceil
32563         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32564         colSpan = Math.min( colSpan, this.cols );
32565         
32566         // normally this should be '1' as we dont' currently allow multi width columns..
32567         
32568         var colGroup = this._getColGroup( colSpan );
32569         // get the minimum Y value from the columns
32570         var minimumY = Math.min.apply( Math, colGroup );
32571         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32572         
32573         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32574          
32575         // position the brick
32576         var position = {
32577             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32578             y: this.currentSize.y + minimumY + this.padHeight
32579         };
32580         
32581         Roo.log(position);
32582         // apply setHeight to necessary columns
32583         var setHeight = minimumY + sz.height + this.padHeight;
32584         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32585         
32586         var setSpan = this.cols + 1 - colGroup.length;
32587         for ( var i = 0; i < setSpan; i++ ) {
32588           this.colYs[ shortColIndex + i ] = setHeight ;
32589         }
32590       
32591         return position;
32592     },
32593     
32594     /**
32595      * @param {Number} colSpan - number of columns the element spans
32596      * @returns {Array} colGroup
32597      */
32598     _getColGroup : function( colSpan )
32599     {
32600         if ( colSpan < 2 ) {
32601           // if brick spans only one column, use all the column Ys
32602           return this.colYs;
32603         }
32604       
32605         var colGroup = [];
32606         // how many different places could this brick fit horizontally
32607         var groupCount = this.cols + 1 - colSpan;
32608         // for each group potential horizontal position
32609         for ( var i = 0; i < groupCount; i++ ) {
32610           // make an array of colY values for that one group
32611           var groupColYs = this.colYs.slice( i, i + colSpan );
32612           // and get the max value of the array
32613           colGroup[i] = Math.max.apply( Math, groupColYs );
32614         }
32615         return colGroup;
32616     },
32617     /*
32618     _manageStamp : function( stamp )
32619     {
32620         var stampSize =  stamp.getSize();
32621         var offset = stamp.getBox();
32622         // get the columns that this stamp affects
32623         var firstX = this.isOriginLeft ? offset.x : offset.right;
32624         var lastX = firstX + stampSize.width;
32625         var firstCol = Math.floor( firstX / this.columnWidth );
32626         firstCol = Math.max( 0, firstCol );
32627         
32628         var lastCol = Math.floor( lastX / this.columnWidth );
32629         // lastCol should not go over if multiple of columnWidth #425
32630         lastCol -= lastX % this.columnWidth ? 0 : 1;
32631         lastCol = Math.min( this.cols - 1, lastCol );
32632         
32633         // set colYs to bottom of the stamp
32634         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32635             stampSize.height;
32636             
32637         for ( var i = firstCol; i <= lastCol; i++ ) {
32638           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32639         }
32640     },
32641     */
32642     
32643     _getContainerSize : function()
32644     {
32645         this.maxY = Math.max.apply( Math, this.colYs );
32646         var size = {
32647             height: this.maxY
32648         };
32649       
32650         if ( this.isFitWidth ) {
32651             size.width = this._getContainerFitWidth();
32652         }
32653       
32654         return size;
32655     },
32656     
32657     _getContainerFitWidth : function()
32658     {
32659         var unusedCols = 0;
32660         // count unused columns
32661         var i = this.cols;
32662         while ( --i ) {
32663           if ( this.colYs[i] !== 0 ) {
32664             break;
32665           }
32666           unusedCols++;
32667         }
32668         // fit container to columns that have been used
32669         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32670     },
32671     
32672     needsResizeLayout : function()
32673     {
32674         var previousWidth = this.containerWidth;
32675         this.getContainerWidth();
32676         return previousWidth !== this.containerWidth;
32677     }
32678  
32679 });
32680
32681  
32682
32683  /*
32684  * - LGPL
32685  *
32686  * element
32687  * 
32688  */
32689
32690 /**
32691  * @class Roo.bootstrap.MasonryBrick
32692  * @extends Roo.bootstrap.Component
32693  * Bootstrap MasonryBrick class
32694  * 
32695  * @constructor
32696  * Create a new MasonryBrick
32697  * @param {Object} config The config object
32698  */
32699
32700 Roo.bootstrap.MasonryBrick = function(config){
32701     
32702     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32703     
32704     Roo.bootstrap.MasonryBrick.register(this);
32705     
32706     this.addEvents({
32707         // raw events
32708         /**
32709          * @event click
32710          * When a MasonryBrick is clcik
32711          * @param {Roo.bootstrap.MasonryBrick} this
32712          * @param {Roo.EventObject} e
32713          */
32714         "click" : true
32715     });
32716 };
32717
32718 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32719     
32720     /**
32721      * @cfg {String} title
32722      */   
32723     title : '',
32724     /**
32725      * @cfg {String} html
32726      */   
32727     html : '',
32728     /**
32729      * @cfg {String} bgimage
32730      */   
32731     bgimage : '',
32732     /**
32733      * @cfg {String} videourl
32734      */   
32735     videourl : '',
32736     /**
32737      * @cfg {String} cls
32738      */   
32739     cls : '',
32740     /**
32741      * @cfg {String} href
32742      */   
32743     href : '',
32744     /**
32745      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32746      */   
32747     size : 'xs',
32748     
32749     /**
32750      * @cfg {String} placetitle (center|bottom)
32751      */   
32752     placetitle : '',
32753     
32754     /**
32755      * @cfg {Boolean} isFitContainer defalut true
32756      */   
32757     isFitContainer : true, 
32758     
32759     /**
32760      * @cfg {Boolean} preventDefault defalut false
32761      */   
32762     preventDefault : false, 
32763     
32764     /**
32765      * @cfg {Boolean} inverse defalut false
32766      */   
32767     maskInverse : false, 
32768     
32769     getAutoCreate : function()
32770     {
32771         if(!this.isFitContainer){
32772             return this.getSplitAutoCreate();
32773         }
32774         
32775         var cls = 'masonry-brick masonry-brick-full';
32776         
32777         if(this.href.length){
32778             cls += ' masonry-brick-link';
32779         }
32780         
32781         if(this.bgimage.length){
32782             cls += ' masonry-brick-image';
32783         }
32784         
32785         if(this.maskInverse){
32786             cls += ' mask-inverse';
32787         }
32788         
32789         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32790             cls += ' enable-mask';
32791         }
32792         
32793         if(this.size){
32794             cls += ' masonry-' + this.size + '-brick';
32795         }
32796         
32797         if(this.placetitle.length){
32798             
32799             switch (this.placetitle) {
32800                 case 'center' :
32801                     cls += ' masonry-center-title';
32802                     break;
32803                 case 'bottom' :
32804                     cls += ' masonry-bottom-title';
32805                     break;
32806                 default:
32807                     break;
32808             }
32809             
32810         } else {
32811             if(!this.html.length && !this.bgimage.length){
32812                 cls += ' masonry-center-title';
32813             }
32814
32815             if(!this.html.length && this.bgimage.length){
32816                 cls += ' masonry-bottom-title';
32817             }
32818         }
32819         
32820         if(this.cls){
32821             cls += ' ' + this.cls;
32822         }
32823         
32824         var cfg = {
32825             tag: (this.href.length) ? 'a' : 'div',
32826             cls: cls,
32827             cn: [
32828                 {
32829                     tag: 'div',
32830                     cls: 'masonry-brick-mask'
32831                 },
32832                 {
32833                     tag: 'div',
32834                     cls: 'masonry-brick-paragraph',
32835                     cn: []
32836                 }
32837             ]
32838         };
32839         
32840         if(this.href.length){
32841             cfg.href = this.href;
32842         }
32843         
32844         var cn = cfg.cn[1].cn;
32845         
32846         if(this.title.length){
32847             cn.push({
32848                 tag: 'h4',
32849                 cls: 'masonry-brick-title',
32850                 html: this.title
32851             });
32852         }
32853         
32854         if(this.html.length){
32855             cn.push({
32856                 tag: 'p',
32857                 cls: 'masonry-brick-text',
32858                 html: this.html
32859             });
32860         }
32861         
32862         if (!this.title.length && !this.html.length) {
32863             cfg.cn[1].cls += ' hide';
32864         }
32865         
32866         if(this.bgimage.length){
32867             cfg.cn.push({
32868                 tag: 'img',
32869                 cls: 'masonry-brick-image-view',
32870                 src: this.bgimage
32871             });
32872         }
32873         
32874         if(this.videourl.length){
32875             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32876             // youtube support only?
32877             cfg.cn.push({
32878                 tag: 'iframe',
32879                 cls: 'masonry-brick-image-view',
32880                 src: vurl,
32881                 frameborder : 0,
32882                 allowfullscreen : true
32883             });
32884         }
32885         
32886         return cfg;
32887         
32888     },
32889     
32890     getSplitAutoCreate : function()
32891     {
32892         var cls = 'masonry-brick masonry-brick-split';
32893         
32894         if(this.href.length){
32895             cls += ' masonry-brick-link';
32896         }
32897         
32898         if(this.bgimage.length){
32899             cls += ' masonry-brick-image';
32900         }
32901         
32902         if(this.size){
32903             cls += ' masonry-' + this.size + '-brick';
32904         }
32905         
32906         switch (this.placetitle) {
32907             case 'center' :
32908                 cls += ' masonry-center-title';
32909                 break;
32910             case 'bottom' :
32911                 cls += ' masonry-bottom-title';
32912                 break;
32913             default:
32914                 if(!this.bgimage.length){
32915                     cls += ' masonry-center-title';
32916                 }
32917
32918                 if(this.bgimage.length){
32919                     cls += ' masonry-bottom-title';
32920                 }
32921                 break;
32922         }
32923         
32924         if(this.cls){
32925             cls += ' ' + this.cls;
32926         }
32927         
32928         var cfg = {
32929             tag: (this.href.length) ? 'a' : 'div',
32930             cls: cls,
32931             cn: [
32932                 {
32933                     tag: 'div',
32934                     cls: 'masonry-brick-split-head',
32935                     cn: [
32936                         {
32937                             tag: 'div',
32938                             cls: 'masonry-brick-paragraph',
32939                             cn: []
32940                         }
32941                     ]
32942                 },
32943                 {
32944                     tag: 'div',
32945                     cls: 'masonry-brick-split-body',
32946                     cn: []
32947                 }
32948             ]
32949         };
32950         
32951         if(this.href.length){
32952             cfg.href = this.href;
32953         }
32954         
32955         if(this.title.length){
32956             cfg.cn[0].cn[0].cn.push({
32957                 tag: 'h4',
32958                 cls: 'masonry-brick-title',
32959                 html: this.title
32960             });
32961         }
32962         
32963         if(this.html.length){
32964             cfg.cn[1].cn.push({
32965                 tag: 'p',
32966                 cls: 'masonry-brick-text',
32967                 html: this.html
32968             });
32969         }
32970
32971         if(this.bgimage.length){
32972             cfg.cn[0].cn.push({
32973                 tag: 'img',
32974                 cls: 'masonry-brick-image-view',
32975                 src: this.bgimage
32976             });
32977         }
32978         
32979         if(this.videourl.length){
32980             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32981             // youtube support only?
32982             cfg.cn[0].cn.cn.push({
32983                 tag: 'iframe',
32984                 cls: 'masonry-brick-image-view',
32985                 src: vurl,
32986                 frameborder : 0,
32987                 allowfullscreen : true
32988             });
32989         }
32990         
32991         return cfg;
32992     },
32993     
32994     initEvents: function() 
32995     {
32996         switch (this.size) {
32997             case 'xs' :
32998                 this.x = 1;
32999                 this.y = 1;
33000                 break;
33001             case 'sm' :
33002                 this.x = 2;
33003                 this.y = 2;
33004                 break;
33005             case 'md' :
33006             case 'md-left' :
33007             case 'md-right' :
33008                 this.x = 3;
33009                 this.y = 3;
33010                 break;
33011             case 'tall' :
33012                 this.x = 2;
33013                 this.y = 3;
33014                 break;
33015             case 'wide' :
33016                 this.x = 3;
33017                 this.y = 2;
33018                 break;
33019             case 'wide-thin' :
33020                 this.x = 3;
33021                 this.y = 1;
33022                 break;
33023                         
33024             default :
33025                 break;
33026         }
33027         
33028         if(Roo.isTouch){
33029             this.el.on('touchstart', this.onTouchStart, this);
33030             this.el.on('touchmove', this.onTouchMove, this);
33031             this.el.on('touchend', this.onTouchEnd, this);
33032             this.el.on('contextmenu', this.onContextMenu, this);
33033         } else {
33034             this.el.on('mouseenter'  ,this.enter, this);
33035             this.el.on('mouseleave', this.leave, this);
33036             this.el.on('click', this.onClick, this);
33037         }
33038         
33039         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33040             this.parent().bricks.push(this);   
33041         }
33042         
33043     },
33044     
33045     onClick: function(e, el)
33046     {
33047         var time = this.endTimer - this.startTimer;
33048         // Roo.log(e.preventDefault());
33049         if(Roo.isTouch){
33050             if(time > 1000){
33051                 e.preventDefault();
33052                 return;
33053             }
33054         }
33055         
33056         if(!this.preventDefault){
33057             return;
33058         }
33059         
33060         e.preventDefault();
33061         
33062         if (this.activeClass != '') {
33063             this.selectBrick();
33064         }
33065         
33066         this.fireEvent('click', this, e);
33067     },
33068     
33069     enter: function(e, el)
33070     {
33071         e.preventDefault();
33072         
33073         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33074             return;
33075         }
33076         
33077         if(this.bgimage.length && this.html.length){
33078             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33079         }
33080     },
33081     
33082     leave: function(e, el)
33083     {
33084         e.preventDefault();
33085         
33086         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33087             return;
33088         }
33089         
33090         if(this.bgimage.length && this.html.length){
33091             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33092         }
33093     },
33094     
33095     onTouchStart: function(e, el)
33096     {
33097 //        e.preventDefault();
33098         
33099         this.touchmoved = false;
33100         
33101         if(!this.isFitContainer){
33102             return;
33103         }
33104         
33105         if(!this.bgimage.length || !this.html.length){
33106             return;
33107         }
33108         
33109         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33110         
33111         this.timer = new Date().getTime();
33112         
33113     },
33114     
33115     onTouchMove: function(e, el)
33116     {
33117         this.touchmoved = true;
33118     },
33119     
33120     onContextMenu : function(e,el)
33121     {
33122         e.preventDefault();
33123         e.stopPropagation();
33124         return false;
33125     },
33126     
33127     onTouchEnd: function(e, el)
33128     {
33129 //        e.preventDefault();
33130         
33131         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33132         
33133             this.leave(e,el);
33134             
33135             return;
33136         }
33137         
33138         if(!this.bgimage.length || !this.html.length){
33139             
33140             if(this.href.length){
33141                 window.location.href = this.href;
33142             }
33143             
33144             return;
33145         }
33146         
33147         if(!this.isFitContainer){
33148             return;
33149         }
33150         
33151         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33152         
33153         window.location.href = this.href;
33154     },
33155     
33156     //selection on single brick only
33157     selectBrick : function() {
33158         
33159         if (!this.parentId) {
33160             return;
33161         }
33162         
33163         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33164         var index = m.selectedBrick.indexOf(this.id);
33165         
33166         if ( index > -1) {
33167             m.selectedBrick.splice(index,1);
33168             this.el.removeClass(this.activeClass);
33169             return;
33170         }
33171         
33172         for(var i = 0; i < m.selectedBrick.length; i++) {
33173             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33174             b.el.removeClass(b.activeClass);
33175         }
33176         
33177         m.selectedBrick = [];
33178         
33179         m.selectedBrick.push(this.id);
33180         this.el.addClass(this.activeClass);
33181         return;
33182     },
33183     
33184     isSelected : function(){
33185         return this.el.hasClass(this.activeClass);
33186         
33187     }
33188 });
33189
33190 Roo.apply(Roo.bootstrap.MasonryBrick, {
33191     
33192     //groups: {},
33193     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33194      /**
33195     * register a Masonry Brick
33196     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33197     */
33198     
33199     register : function(brick)
33200     {
33201         //this.groups[brick.id] = brick;
33202         this.groups.add(brick.id, brick);
33203     },
33204     /**
33205     * fetch a  masonry brick based on the masonry brick ID
33206     * @param {string} the masonry brick to add
33207     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33208     */
33209     
33210     get: function(brick_id) 
33211     {
33212         // if (typeof(this.groups[brick_id]) == 'undefined') {
33213         //     return false;
33214         // }
33215         // return this.groups[brick_id] ;
33216         
33217         if(this.groups.key(brick_id)) {
33218             return this.groups.key(brick_id);
33219         }
33220         
33221         return false;
33222     }
33223     
33224     
33225     
33226 });
33227
33228  /*
33229  * - LGPL
33230  *
33231  * element
33232  * 
33233  */
33234
33235 /**
33236  * @class Roo.bootstrap.Brick
33237  * @extends Roo.bootstrap.Component
33238  * Bootstrap Brick class
33239  * 
33240  * @constructor
33241  * Create a new Brick
33242  * @param {Object} config The config object
33243  */
33244
33245 Roo.bootstrap.Brick = function(config){
33246     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33247     
33248     this.addEvents({
33249         // raw events
33250         /**
33251          * @event click
33252          * When a Brick is click
33253          * @param {Roo.bootstrap.Brick} this
33254          * @param {Roo.EventObject} e
33255          */
33256         "click" : true
33257     });
33258 };
33259
33260 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33261     
33262     /**
33263      * @cfg {String} title
33264      */   
33265     title : '',
33266     /**
33267      * @cfg {String} html
33268      */   
33269     html : '',
33270     /**
33271      * @cfg {String} bgimage
33272      */   
33273     bgimage : '',
33274     /**
33275      * @cfg {String} cls
33276      */   
33277     cls : '',
33278     /**
33279      * @cfg {String} href
33280      */   
33281     href : '',
33282     /**
33283      * @cfg {String} video
33284      */   
33285     video : '',
33286     /**
33287      * @cfg {Boolean} square
33288      */   
33289     square : true,
33290     
33291     getAutoCreate : function()
33292     {
33293         var cls = 'roo-brick';
33294         
33295         if(this.href.length){
33296             cls += ' roo-brick-link';
33297         }
33298         
33299         if(this.bgimage.length){
33300             cls += ' roo-brick-image';
33301         }
33302         
33303         if(!this.html.length && !this.bgimage.length){
33304             cls += ' roo-brick-center-title';
33305         }
33306         
33307         if(!this.html.length && this.bgimage.length){
33308             cls += ' roo-brick-bottom-title';
33309         }
33310         
33311         if(this.cls){
33312             cls += ' ' + this.cls;
33313         }
33314         
33315         var cfg = {
33316             tag: (this.href.length) ? 'a' : 'div',
33317             cls: cls,
33318             cn: [
33319                 {
33320                     tag: 'div',
33321                     cls: 'roo-brick-paragraph',
33322                     cn: []
33323                 }
33324             ]
33325         };
33326         
33327         if(this.href.length){
33328             cfg.href = this.href;
33329         }
33330         
33331         var cn = cfg.cn[0].cn;
33332         
33333         if(this.title.length){
33334             cn.push({
33335                 tag: 'h4',
33336                 cls: 'roo-brick-title',
33337                 html: this.title
33338             });
33339         }
33340         
33341         if(this.html.length){
33342             cn.push({
33343                 tag: 'p',
33344                 cls: 'roo-brick-text',
33345                 html: this.html
33346             });
33347         } else {
33348             cn.cls += ' hide';
33349         }
33350         
33351         if(this.bgimage.length){
33352             cfg.cn.push({
33353                 tag: 'img',
33354                 cls: 'roo-brick-image-view',
33355                 src: this.bgimage
33356             });
33357         }
33358         
33359         return cfg;
33360     },
33361     
33362     initEvents: function() 
33363     {
33364         if(this.title.length || this.html.length){
33365             this.el.on('mouseenter'  ,this.enter, this);
33366             this.el.on('mouseleave', this.leave, this);
33367         }
33368         
33369         Roo.EventManager.onWindowResize(this.resize, this); 
33370         
33371         if(this.bgimage.length){
33372             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33373             this.imageEl.on('load', this.onImageLoad, this);
33374             return;
33375         }
33376         
33377         this.resize();
33378     },
33379     
33380     onImageLoad : function()
33381     {
33382         this.resize();
33383     },
33384     
33385     resize : function()
33386     {
33387         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33388         
33389         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33390         
33391         if(this.bgimage.length){
33392             var image = this.el.select('.roo-brick-image-view', true).first();
33393             
33394             image.setWidth(paragraph.getWidth());
33395             
33396             if(this.square){
33397                 image.setHeight(paragraph.getWidth());
33398             }
33399             
33400             this.el.setHeight(image.getHeight());
33401             paragraph.setHeight(image.getHeight());
33402             
33403         }
33404         
33405     },
33406     
33407     enter: function(e, el)
33408     {
33409         e.preventDefault();
33410         
33411         if(this.bgimage.length){
33412             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33413             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33414         }
33415     },
33416     
33417     leave: function(e, el)
33418     {
33419         e.preventDefault();
33420         
33421         if(this.bgimage.length){
33422             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33423             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33424         }
33425     }
33426     
33427 });
33428
33429  
33430
33431  /*
33432  * - LGPL
33433  *
33434  * Number field 
33435  */
33436
33437 /**
33438  * @class Roo.bootstrap.NumberField
33439  * @extends Roo.bootstrap.Input
33440  * Bootstrap NumberField class
33441  * 
33442  * 
33443  * 
33444  * 
33445  * @constructor
33446  * Create a new NumberField
33447  * @param {Object} config The config object
33448  */
33449
33450 Roo.bootstrap.NumberField = function(config){
33451     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33452 };
33453
33454 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33455     
33456     /**
33457      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33458      */
33459     allowDecimals : true,
33460     /**
33461      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33462      */
33463     decimalSeparator : ".",
33464     /**
33465      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33466      */
33467     decimalPrecision : 2,
33468     /**
33469      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33470      */
33471     allowNegative : true,
33472     
33473     /**
33474      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33475      */
33476     allowZero: true,
33477     /**
33478      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33479      */
33480     minValue : Number.NEGATIVE_INFINITY,
33481     /**
33482      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33483      */
33484     maxValue : Number.MAX_VALUE,
33485     /**
33486      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33487      */
33488     minText : "The minimum value for this field is {0}",
33489     /**
33490      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33491      */
33492     maxText : "The maximum value for this field is {0}",
33493     /**
33494      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33495      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33496      */
33497     nanText : "{0} is not a valid number",
33498     /**
33499      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33500      */
33501     thousandsDelimiter : false,
33502     /**
33503      * @cfg {String} valueAlign alignment of value
33504      */
33505     valueAlign : "left",
33506
33507     getAutoCreate : function()
33508     {
33509         var hiddenInput = {
33510             tag: 'input',
33511             type: 'hidden',
33512             id: Roo.id(),
33513             cls: 'hidden-number-input'
33514         };
33515         
33516         if (this.name) {
33517             hiddenInput.name = this.name;
33518         }
33519         
33520         this.name = '';
33521         
33522         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33523         
33524         this.name = hiddenInput.name;
33525         
33526         if(cfg.cn.length > 0) {
33527             cfg.cn.push(hiddenInput);
33528         }
33529         
33530         return cfg;
33531     },
33532
33533     // private
33534     initEvents : function()
33535     {   
33536         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33537         
33538         var allowed = "0123456789";
33539         
33540         if(this.allowDecimals){
33541             allowed += this.decimalSeparator;
33542         }
33543         
33544         if(this.allowNegative){
33545             allowed += "-";
33546         }
33547         
33548         if(this.thousandsDelimiter) {
33549             allowed += ",";
33550         }
33551         
33552         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33553         
33554         var keyPress = function(e){
33555             
33556             var k = e.getKey();
33557             
33558             var c = e.getCharCode();
33559             
33560             if(
33561                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33562                     allowed.indexOf(String.fromCharCode(c)) === -1
33563             ){
33564                 e.stopEvent();
33565                 return;
33566             }
33567             
33568             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33569                 return;
33570             }
33571             
33572             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33573                 e.stopEvent();
33574             }
33575         };
33576         
33577         this.el.on("keypress", keyPress, this);
33578     },
33579     
33580     validateValue : function(value)
33581     {
33582         
33583         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33584             return false;
33585         }
33586         
33587         var num = this.parseValue(value);
33588         
33589         if(isNaN(num)){
33590             this.markInvalid(String.format(this.nanText, value));
33591             return false;
33592         }
33593         
33594         if(num < this.minValue){
33595             this.markInvalid(String.format(this.minText, this.minValue));
33596             return false;
33597         }
33598         
33599         if(num > this.maxValue){
33600             this.markInvalid(String.format(this.maxText, this.maxValue));
33601             return false;
33602         }
33603         
33604         return true;
33605     },
33606
33607     getValue : function()
33608     {
33609         var v = this.hiddenEl().getValue();
33610         
33611         return this.fixPrecision(this.parseValue(v));
33612     },
33613
33614     parseValue : function(value)
33615     {
33616         if(this.thousandsDelimiter) {
33617             value += "";
33618             r = new RegExp(",", "g");
33619             value = value.replace(r, "");
33620         }
33621         
33622         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33623         return isNaN(value) ? '' : value;
33624     },
33625
33626     fixPrecision : function(value)
33627     {
33628         if(this.thousandsDelimiter) {
33629             value += "";
33630             r = new RegExp(",", "g");
33631             value = value.replace(r, "");
33632         }
33633         
33634         var nan = isNaN(value);
33635         
33636         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33637             return nan ? '' : value;
33638         }
33639         return parseFloat(value).toFixed(this.decimalPrecision);
33640     },
33641
33642     setValue : function(v)
33643     {
33644         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33645         
33646         this.value = v;
33647         
33648         if(this.rendered){
33649             
33650             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33651             
33652             this.inputEl().dom.value = (v == '') ? '' :
33653                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33654             
33655             if(!this.allowZero && v === '0') {
33656                 this.hiddenEl().dom.value = '';
33657                 this.inputEl().dom.value = '';
33658             }
33659             
33660             this.validate();
33661         }
33662     },
33663
33664     decimalPrecisionFcn : function(v)
33665     {
33666         return Math.floor(v);
33667     },
33668
33669     beforeBlur : function()
33670     {
33671         var v = this.parseValue(this.getRawValue());
33672         
33673         if(v || v === 0 || v === ''){
33674             this.setValue(v);
33675         }
33676     },
33677     
33678     hiddenEl : function()
33679     {
33680         return this.el.select('input.hidden-number-input',true).first();
33681     }
33682     
33683 });
33684
33685  
33686
33687 /*
33688 * Licence: LGPL
33689 */
33690
33691 /**
33692  * @class Roo.bootstrap.DocumentSlider
33693  * @extends Roo.bootstrap.Component
33694  * Bootstrap DocumentSlider class
33695  * 
33696  * @constructor
33697  * Create a new DocumentViewer
33698  * @param {Object} config The config object
33699  */
33700
33701 Roo.bootstrap.DocumentSlider = function(config){
33702     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33703     
33704     this.files = [];
33705     
33706     this.addEvents({
33707         /**
33708          * @event initial
33709          * Fire after initEvent
33710          * @param {Roo.bootstrap.DocumentSlider} this
33711          */
33712         "initial" : true,
33713         /**
33714          * @event update
33715          * Fire after update
33716          * @param {Roo.bootstrap.DocumentSlider} this
33717          */
33718         "update" : true,
33719         /**
33720          * @event click
33721          * Fire after click
33722          * @param {Roo.bootstrap.DocumentSlider} this
33723          */
33724         "click" : true
33725     });
33726 };
33727
33728 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33729     
33730     files : false,
33731     
33732     indicator : 0,
33733     
33734     getAutoCreate : function()
33735     {
33736         var cfg = {
33737             tag : 'div',
33738             cls : 'roo-document-slider',
33739             cn : [
33740                 {
33741                     tag : 'div',
33742                     cls : 'roo-document-slider-header',
33743                     cn : [
33744                         {
33745                             tag : 'div',
33746                             cls : 'roo-document-slider-header-title'
33747                         }
33748                     ]
33749                 },
33750                 {
33751                     tag : 'div',
33752                     cls : 'roo-document-slider-body',
33753                     cn : [
33754                         {
33755                             tag : 'div',
33756                             cls : 'roo-document-slider-prev',
33757                             cn : [
33758                                 {
33759                                     tag : 'i',
33760                                     cls : 'fa fa-chevron-left'
33761                                 }
33762                             ]
33763                         },
33764                         {
33765                             tag : 'div',
33766                             cls : 'roo-document-slider-thumb',
33767                             cn : [
33768                                 {
33769                                     tag : 'img',
33770                                     cls : 'roo-document-slider-image'
33771                                 }
33772                             ]
33773                         },
33774                         {
33775                             tag : 'div',
33776                             cls : 'roo-document-slider-next',
33777                             cn : [
33778                                 {
33779                                     tag : 'i',
33780                                     cls : 'fa fa-chevron-right'
33781                                 }
33782                             ]
33783                         }
33784                     ]
33785                 }
33786             ]
33787         };
33788         
33789         return cfg;
33790     },
33791     
33792     initEvents : function()
33793     {
33794         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33795         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33796         
33797         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33798         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33799         
33800         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33801         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33802         
33803         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33804         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33805         
33806         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33807         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33808         
33809         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33810         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33811         
33812         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33813         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33814         
33815         this.thumbEl.on('click', this.onClick, this);
33816         
33817         this.prevIndicator.on('click', this.prev, this);
33818         
33819         this.nextIndicator.on('click', this.next, this);
33820         
33821     },
33822     
33823     initial : function()
33824     {
33825         if(this.files.length){
33826             this.indicator = 1;
33827             this.update()
33828         }
33829         
33830         this.fireEvent('initial', this);
33831     },
33832     
33833     update : function()
33834     {
33835         this.imageEl.attr('src', this.files[this.indicator - 1]);
33836         
33837         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33838         
33839         this.prevIndicator.show();
33840         
33841         if(this.indicator == 1){
33842             this.prevIndicator.hide();
33843         }
33844         
33845         this.nextIndicator.show();
33846         
33847         if(this.indicator == this.files.length){
33848             this.nextIndicator.hide();
33849         }
33850         
33851         this.thumbEl.scrollTo('top');
33852         
33853         this.fireEvent('update', this);
33854     },
33855     
33856     onClick : function(e)
33857     {
33858         e.preventDefault();
33859         
33860         this.fireEvent('click', this);
33861     },
33862     
33863     prev : function(e)
33864     {
33865         e.preventDefault();
33866         
33867         this.indicator = Math.max(1, this.indicator - 1);
33868         
33869         this.update();
33870     },
33871     
33872     next : function(e)
33873     {
33874         e.preventDefault();
33875         
33876         this.indicator = Math.min(this.files.length, this.indicator + 1);
33877         
33878         this.update();
33879     }
33880 });
33881 /*
33882  * - LGPL
33883  *
33884  * RadioSet
33885  *
33886  *
33887  */
33888
33889 /**
33890  * @class Roo.bootstrap.RadioSet
33891  * @extends Roo.bootstrap.Input
33892  * Bootstrap RadioSet class
33893  * @cfg {String} indicatorpos (left|right) default left
33894  * @cfg {Boolean} inline (true|false) inline the element (default true)
33895  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33896  * @constructor
33897  * Create a new RadioSet
33898  * @param {Object} config The config object
33899  */
33900
33901 Roo.bootstrap.RadioSet = function(config){
33902     
33903     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33904     
33905     this.radioes = [];
33906     
33907     Roo.bootstrap.RadioSet.register(this);
33908     
33909     this.addEvents({
33910         /**
33911         * @event check
33912         * Fires when the element is checked or unchecked.
33913         * @param {Roo.bootstrap.RadioSet} this This radio
33914         * @param {Roo.bootstrap.Radio} item The checked item
33915         */
33916        check : true,
33917        /**
33918         * @event click
33919         * Fires when the element is click.
33920         * @param {Roo.bootstrap.RadioSet} this This radio set
33921         * @param {Roo.bootstrap.Radio} item The checked item
33922         * @param {Roo.EventObject} e The event object
33923         */
33924        click : true
33925     });
33926     
33927 };
33928
33929 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33930
33931     radioes : false,
33932     
33933     inline : true,
33934     
33935     weight : '',
33936     
33937     indicatorpos : 'left',
33938     
33939     getAutoCreate : function()
33940     {
33941         var label = {
33942             tag : 'label',
33943             cls : 'roo-radio-set-label',
33944             cn : [
33945                 {
33946                     tag : 'span',
33947                     html : this.fieldLabel
33948                 }
33949             ]
33950         };
33951         
33952         if(this.indicatorpos == 'left'){
33953             label.cn.unshift({
33954                 tag : 'i',
33955                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33956                 tooltip : 'This field is required'
33957             });
33958         } else {
33959             label.cn.push({
33960                 tag : 'i',
33961                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33962                 tooltip : 'This field is required'
33963             });
33964         }
33965         
33966         var items = {
33967             tag : 'div',
33968             cls : 'roo-radio-set-items'
33969         };
33970         
33971         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33972         
33973         if (align === 'left' && this.fieldLabel.length) {
33974             
33975             items = {
33976                 cls : "roo-radio-set-right", 
33977                 cn: [
33978                     items
33979                 ]
33980             };
33981             
33982             if(this.labelWidth > 12){
33983                 label.style = "width: " + this.labelWidth + 'px';
33984             }
33985             
33986             if(this.labelWidth < 13 && this.labelmd == 0){
33987                 this.labelmd = this.labelWidth;
33988             }
33989             
33990             if(this.labellg > 0){
33991                 label.cls += ' col-lg-' + this.labellg;
33992                 items.cls += ' col-lg-' + (12 - this.labellg);
33993             }
33994             
33995             if(this.labelmd > 0){
33996                 label.cls += ' col-md-' + this.labelmd;
33997                 items.cls += ' col-md-' + (12 - this.labelmd);
33998             }
33999             
34000             if(this.labelsm > 0){
34001                 label.cls += ' col-sm-' + this.labelsm;
34002                 items.cls += ' col-sm-' + (12 - this.labelsm);
34003             }
34004             
34005             if(this.labelxs > 0){
34006                 label.cls += ' col-xs-' + this.labelxs;
34007                 items.cls += ' col-xs-' + (12 - this.labelxs);
34008             }
34009         }
34010         
34011         var cfg = {
34012             tag : 'div',
34013             cls : 'roo-radio-set',
34014             cn : [
34015                 {
34016                     tag : 'input',
34017                     cls : 'roo-radio-set-input',
34018                     type : 'hidden',
34019                     name : this.name,
34020                     value : this.value ? this.value :  ''
34021                 },
34022                 label,
34023                 items
34024             ]
34025         };
34026         
34027         if(this.weight.length){
34028             cfg.cls += ' roo-radio-' + this.weight;
34029         }
34030         
34031         if(this.inline) {
34032             cfg.cls += ' roo-radio-set-inline';
34033         }
34034         
34035         var settings=this;
34036         ['xs','sm','md','lg'].map(function(size){
34037             if (settings[size]) {
34038                 cfg.cls += ' col-' + size + '-' + settings[size];
34039             }
34040         });
34041         
34042         return cfg;
34043         
34044     },
34045
34046     initEvents : function()
34047     {
34048         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34049         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34050         
34051         if(!this.fieldLabel.length){
34052             this.labelEl.hide();
34053         }
34054         
34055         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34056         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34057         
34058         this.indicator = this.indicatorEl();
34059         
34060         if(this.indicator){
34061             this.indicator.addClass('invisible');
34062         }
34063         
34064         this.originalValue = this.getValue();
34065         
34066     },
34067     
34068     inputEl: function ()
34069     {
34070         return this.el.select('.roo-radio-set-input', true).first();
34071     },
34072     
34073     getChildContainer : function()
34074     {
34075         return this.itemsEl;
34076     },
34077     
34078     register : function(item)
34079     {
34080         this.radioes.push(item);
34081         
34082     },
34083     
34084     validate : function()
34085     {   
34086         if(this.getVisibilityEl().hasClass('hidden')){
34087             return true;
34088         }
34089         
34090         var valid = false;
34091         
34092         Roo.each(this.radioes, function(i){
34093             if(!i.checked){
34094                 return;
34095             }
34096             
34097             valid = true;
34098             return false;
34099         });
34100         
34101         if(this.allowBlank) {
34102             return true;
34103         }
34104         
34105         if(this.disabled || valid){
34106             this.markValid();
34107             return true;
34108         }
34109         
34110         this.markInvalid();
34111         return false;
34112         
34113     },
34114     
34115     markValid : function()
34116     {
34117         if(this.labelEl.isVisible(true)){
34118             this.indicatorEl().removeClass('visible');
34119             this.indicatorEl().addClass('invisible');
34120         }
34121         
34122         this.el.removeClass([this.invalidClass, this.validClass]);
34123         this.el.addClass(this.validClass);
34124         
34125         this.fireEvent('valid', this);
34126     },
34127     
34128     markInvalid : function(msg)
34129     {
34130         if(this.allowBlank || this.disabled){
34131             return;
34132         }
34133         
34134         if(this.labelEl.isVisible(true)){
34135             this.indicatorEl().removeClass('invisible');
34136             this.indicatorEl().addClass('visible');
34137         }
34138         
34139         this.el.removeClass([this.invalidClass, this.validClass]);
34140         this.el.addClass(this.invalidClass);
34141         
34142         this.fireEvent('invalid', this, msg);
34143         
34144     },
34145     
34146     setValue : function(v, suppressEvent)
34147     {   
34148         if(this.value === v){
34149             return;
34150         }
34151         
34152         this.value = v;
34153         
34154         if(this.rendered){
34155             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34156         }
34157         
34158         Roo.each(this.radioes, function(i){
34159             i.checked = false;
34160             i.el.removeClass('checked');
34161         });
34162         
34163         Roo.each(this.radioes, function(i){
34164             
34165             if(i.value === v || i.value.toString() === v.toString()){
34166                 i.checked = true;
34167                 i.el.addClass('checked');
34168                 
34169                 if(suppressEvent !== true){
34170                     this.fireEvent('check', this, i);
34171                 }
34172                 
34173                 return false;
34174             }
34175             
34176         }, this);
34177         
34178         this.validate();
34179     },
34180     
34181     clearInvalid : function(){
34182         
34183         if(!this.el || this.preventMark){
34184             return;
34185         }
34186         
34187         this.el.removeClass([this.invalidClass]);
34188         
34189         this.fireEvent('valid', this);
34190     }
34191     
34192 });
34193
34194 Roo.apply(Roo.bootstrap.RadioSet, {
34195     
34196     groups: {},
34197     
34198     register : function(set)
34199     {
34200         this.groups[set.name] = set;
34201     },
34202     
34203     get: function(name) 
34204     {
34205         if (typeof(this.groups[name]) == 'undefined') {
34206             return false;
34207         }
34208         
34209         return this.groups[name] ;
34210     }
34211     
34212 });
34213 /*
34214  * Based on:
34215  * Ext JS Library 1.1.1
34216  * Copyright(c) 2006-2007, Ext JS, LLC.
34217  *
34218  * Originally Released Under LGPL - original licence link has changed is not relivant.
34219  *
34220  * Fork - LGPL
34221  * <script type="text/javascript">
34222  */
34223
34224
34225 /**
34226  * @class Roo.bootstrap.SplitBar
34227  * @extends Roo.util.Observable
34228  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34229  * <br><br>
34230  * Usage:
34231  * <pre><code>
34232 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34233                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34234 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34235 split.minSize = 100;
34236 split.maxSize = 600;
34237 split.animate = true;
34238 split.on('moved', splitterMoved);
34239 </code></pre>
34240  * @constructor
34241  * Create a new SplitBar
34242  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34243  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34244  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34245  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34246                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34247                         position of the SplitBar).
34248  */
34249 Roo.bootstrap.SplitBar = function(cfg){
34250     
34251     /** @private */
34252     
34253     //{
34254     //  dragElement : elm
34255     //  resizingElement: el,
34256         // optional..
34257     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34258     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34259         // existingProxy ???
34260     //}
34261     
34262     this.el = Roo.get(cfg.dragElement, true);
34263     this.el.dom.unselectable = "on";
34264     /** @private */
34265     this.resizingEl = Roo.get(cfg.resizingElement, true);
34266
34267     /**
34268      * @private
34269      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34270      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34271      * @type Number
34272      */
34273     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34274     
34275     /**
34276      * The minimum size of the resizing element. (Defaults to 0)
34277      * @type Number
34278      */
34279     this.minSize = 0;
34280     
34281     /**
34282      * The maximum size of the resizing element. (Defaults to 2000)
34283      * @type Number
34284      */
34285     this.maxSize = 2000;
34286     
34287     /**
34288      * Whether to animate the transition to the new size
34289      * @type Boolean
34290      */
34291     this.animate = false;
34292     
34293     /**
34294      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34295      * @type Boolean
34296      */
34297     this.useShim = false;
34298     
34299     /** @private */
34300     this.shim = null;
34301     
34302     if(!cfg.existingProxy){
34303         /** @private */
34304         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34305     }else{
34306         this.proxy = Roo.get(cfg.existingProxy).dom;
34307     }
34308     /** @private */
34309     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34310     
34311     /** @private */
34312     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34313     
34314     /** @private */
34315     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34316     
34317     /** @private */
34318     this.dragSpecs = {};
34319     
34320     /**
34321      * @private The adapter to use to positon and resize elements
34322      */
34323     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34324     this.adapter.init(this);
34325     
34326     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34327         /** @private */
34328         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34329         this.el.addClass("roo-splitbar-h");
34330     }else{
34331         /** @private */
34332         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34333         this.el.addClass("roo-splitbar-v");
34334     }
34335     
34336     this.addEvents({
34337         /**
34338          * @event resize
34339          * Fires when the splitter is moved (alias for {@link #event-moved})
34340          * @param {Roo.bootstrap.SplitBar} this
34341          * @param {Number} newSize the new width or height
34342          */
34343         "resize" : true,
34344         /**
34345          * @event moved
34346          * Fires when the splitter is moved
34347          * @param {Roo.bootstrap.SplitBar} this
34348          * @param {Number} newSize the new width or height
34349          */
34350         "moved" : true,
34351         /**
34352          * @event beforeresize
34353          * Fires before the splitter is dragged
34354          * @param {Roo.bootstrap.SplitBar} this
34355          */
34356         "beforeresize" : true,
34357
34358         "beforeapply" : true
34359     });
34360
34361     Roo.util.Observable.call(this);
34362 };
34363
34364 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34365     onStartProxyDrag : function(x, y){
34366         this.fireEvent("beforeresize", this);
34367         if(!this.overlay){
34368             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34369             o.unselectable();
34370             o.enableDisplayMode("block");
34371             // all splitbars share the same overlay
34372             Roo.bootstrap.SplitBar.prototype.overlay = o;
34373         }
34374         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34375         this.overlay.show();
34376         Roo.get(this.proxy).setDisplayed("block");
34377         var size = this.adapter.getElementSize(this);
34378         this.activeMinSize = this.getMinimumSize();;
34379         this.activeMaxSize = this.getMaximumSize();;
34380         var c1 = size - this.activeMinSize;
34381         var c2 = Math.max(this.activeMaxSize - size, 0);
34382         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34383             this.dd.resetConstraints();
34384             this.dd.setXConstraint(
34385                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34386                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34387             );
34388             this.dd.setYConstraint(0, 0);
34389         }else{
34390             this.dd.resetConstraints();
34391             this.dd.setXConstraint(0, 0);
34392             this.dd.setYConstraint(
34393                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34394                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34395             );
34396          }
34397         this.dragSpecs.startSize = size;
34398         this.dragSpecs.startPoint = [x, y];
34399         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34400     },
34401     
34402     /** 
34403      * @private Called after the drag operation by the DDProxy
34404      */
34405     onEndProxyDrag : function(e){
34406         Roo.get(this.proxy).setDisplayed(false);
34407         var endPoint = Roo.lib.Event.getXY(e);
34408         if(this.overlay){
34409             this.overlay.hide();
34410         }
34411         var newSize;
34412         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34413             newSize = this.dragSpecs.startSize + 
34414                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34415                     endPoint[0] - this.dragSpecs.startPoint[0] :
34416                     this.dragSpecs.startPoint[0] - endPoint[0]
34417                 );
34418         }else{
34419             newSize = this.dragSpecs.startSize + 
34420                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34421                     endPoint[1] - this.dragSpecs.startPoint[1] :
34422                     this.dragSpecs.startPoint[1] - endPoint[1]
34423                 );
34424         }
34425         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34426         if(newSize != this.dragSpecs.startSize){
34427             if(this.fireEvent('beforeapply', this, newSize) !== false){
34428                 this.adapter.setElementSize(this, newSize);
34429                 this.fireEvent("moved", this, newSize);
34430                 this.fireEvent("resize", this, newSize);
34431             }
34432         }
34433     },
34434     
34435     /**
34436      * Get the adapter this SplitBar uses
34437      * @return The adapter object
34438      */
34439     getAdapter : function(){
34440         return this.adapter;
34441     },
34442     
34443     /**
34444      * Set the adapter this SplitBar uses
34445      * @param {Object} adapter A SplitBar adapter object
34446      */
34447     setAdapter : function(adapter){
34448         this.adapter = adapter;
34449         this.adapter.init(this);
34450     },
34451     
34452     /**
34453      * Gets the minimum size for the resizing element
34454      * @return {Number} The minimum size
34455      */
34456     getMinimumSize : function(){
34457         return this.minSize;
34458     },
34459     
34460     /**
34461      * Sets the minimum size for the resizing element
34462      * @param {Number} minSize The minimum size
34463      */
34464     setMinimumSize : function(minSize){
34465         this.minSize = minSize;
34466     },
34467     
34468     /**
34469      * Gets the maximum size for the resizing element
34470      * @return {Number} The maximum size
34471      */
34472     getMaximumSize : function(){
34473         return this.maxSize;
34474     },
34475     
34476     /**
34477      * Sets the maximum size for the resizing element
34478      * @param {Number} maxSize The maximum size
34479      */
34480     setMaximumSize : function(maxSize){
34481         this.maxSize = maxSize;
34482     },
34483     
34484     /**
34485      * Sets the initialize size for the resizing element
34486      * @param {Number} size The initial size
34487      */
34488     setCurrentSize : function(size){
34489         var oldAnimate = this.animate;
34490         this.animate = false;
34491         this.adapter.setElementSize(this, size);
34492         this.animate = oldAnimate;
34493     },
34494     
34495     /**
34496      * Destroy this splitbar. 
34497      * @param {Boolean} removeEl True to remove the element
34498      */
34499     destroy : function(removeEl){
34500         if(this.shim){
34501             this.shim.remove();
34502         }
34503         this.dd.unreg();
34504         this.proxy.parentNode.removeChild(this.proxy);
34505         if(removeEl){
34506             this.el.remove();
34507         }
34508     }
34509 });
34510
34511 /**
34512  * @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.
34513  */
34514 Roo.bootstrap.SplitBar.createProxy = function(dir){
34515     var proxy = new Roo.Element(document.createElement("div"));
34516     proxy.unselectable();
34517     var cls = 'roo-splitbar-proxy';
34518     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34519     document.body.appendChild(proxy.dom);
34520     return proxy.dom;
34521 };
34522
34523 /** 
34524  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34525  * Default Adapter. It assumes the splitter and resizing element are not positioned
34526  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34527  */
34528 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34529 };
34530
34531 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34532     // do nothing for now
34533     init : function(s){
34534     
34535     },
34536     /**
34537      * Called before drag operations to get the current size of the resizing element. 
34538      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34539      */
34540      getElementSize : function(s){
34541         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34542             return s.resizingEl.getWidth();
34543         }else{
34544             return s.resizingEl.getHeight();
34545         }
34546     },
34547     
34548     /**
34549      * Called after drag operations to set the size of the resizing element.
34550      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34551      * @param {Number} newSize The new size to set
34552      * @param {Function} onComplete A function to be invoked when resizing is complete
34553      */
34554     setElementSize : function(s, newSize, onComplete){
34555         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34556             if(!s.animate){
34557                 s.resizingEl.setWidth(newSize);
34558                 if(onComplete){
34559                     onComplete(s, newSize);
34560                 }
34561             }else{
34562                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34563             }
34564         }else{
34565             
34566             if(!s.animate){
34567                 s.resizingEl.setHeight(newSize);
34568                 if(onComplete){
34569                     onComplete(s, newSize);
34570                 }
34571             }else{
34572                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34573             }
34574         }
34575     }
34576 };
34577
34578 /** 
34579  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34580  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34581  * Adapter that  moves the splitter element to align with the resized sizing element. 
34582  * Used with an absolute positioned SplitBar.
34583  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34584  * document.body, make sure you assign an id to the body element.
34585  */
34586 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34587     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34588     this.container = Roo.get(container);
34589 };
34590
34591 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34592     init : function(s){
34593         this.basic.init(s);
34594     },
34595     
34596     getElementSize : function(s){
34597         return this.basic.getElementSize(s);
34598     },
34599     
34600     setElementSize : function(s, newSize, onComplete){
34601         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34602     },
34603     
34604     moveSplitter : function(s){
34605         var yes = Roo.bootstrap.SplitBar;
34606         switch(s.placement){
34607             case yes.LEFT:
34608                 s.el.setX(s.resizingEl.getRight());
34609                 break;
34610             case yes.RIGHT:
34611                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34612                 break;
34613             case yes.TOP:
34614                 s.el.setY(s.resizingEl.getBottom());
34615                 break;
34616             case yes.BOTTOM:
34617                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34618                 break;
34619         }
34620     }
34621 };
34622
34623 /**
34624  * Orientation constant - Create a vertical SplitBar
34625  * @static
34626  * @type Number
34627  */
34628 Roo.bootstrap.SplitBar.VERTICAL = 1;
34629
34630 /**
34631  * Orientation constant - Create a horizontal SplitBar
34632  * @static
34633  * @type Number
34634  */
34635 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34636
34637 /**
34638  * Placement constant - The resizing element is to the left of the splitter element
34639  * @static
34640  * @type Number
34641  */
34642 Roo.bootstrap.SplitBar.LEFT = 1;
34643
34644 /**
34645  * Placement constant - The resizing element is to the right of the splitter element
34646  * @static
34647  * @type Number
34648  */
34649 Roo.bootstrap.SplitBar.RIGHT = 2;
34650
34651 /**
34652  * Placement constant - The resizing element is positioned above the splitter element
34653  * @static
34654  * @type Number
34655  */
34656 Roo.bootstrap.SplitBar.TOP = 3;
34657
34658 /**
34659  * Placement constant - The resizing element is positioned under splitter element
34660  * @static
34661  * @type Number
34662  */
34663 Roo.bootstrap.SplitBar.BOTTOM = 4;
34664 Roo.namespace("Roo.bootstrap.layout");/*
34665  * Based on:
34666  * Ext JS Library 1.1.1
34667  * Copyright(c) 2006-2007, Ext JS, LLC.
34668  *
34669  * Originally Released Under LGPL - original licence link has changed is not relivant.
34670  *
34671  * Fork - LGPL
34672  * <script type="text/javascript">
34673  */
34674
34675 /**
34676  * @class Roo.bootstrap.layout.Manager
34677  * @extends Roo.bootstrap.Component
34678  * Base class for layout managers.
34679  */
34680 Roo.bootstrap.layout.Manager = function(config)
34681 {
34682     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34683
34684
34685
34686
34687
34688     /** false to disable window resize monitoring @type Boolean */
34689     this.monitorWindowResize = true;
34690     this.regions = {};
34691     this.addEvents({
34692         /**
34693          * @event layout
34694          * Fires when a layout is performed.
34695          * @param {Roo.LayoutManager} this
34696          */
34697         "layout" : true,
34698         /**
34699          * @event regionresized
34700          * Fires when the user resizes a region.
34701          * @param {Roo.LayoutRegion} region The resized region
34702          * @param {Number} newSize The new size (width for east/west, height for north/south)
34703          */
34704         "regionresized" : true,
34705         /**
34706          * @event regioncollapsed
34707          * Fires when a region is collapsed.
34708          * @param {Roo.LayoutRegion} region The collapsed region
34709          */
34710         "regioncollapsed" : true,
34711         /**
34712          * @event regionexpanded
34713          * Fires when a region is expanded.
34714          * @param {Roo.LayoutRegion} region The expanded region
34715          */
34716         "regionexpanded" : true
34717     });
34718     this.updating = false;
34719
34720     if (config.el) {
34721         this.el = Roo.get(config.el);
34722         this.initEvents();
34723     }
34724
34725 };
34726
34727 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34728
34729
34730     regions : null,
34731
34732     monitorWindowResize : true,
34733
34734
34735     updating : false,
34736
34737
34738     onRender : function(ct, position)
34739     {
34740         if(!this.el){
34741             this.el = Roo.get(ct);
34742             this.initEvents();
34743         }
34744         //this.fireEvent('render',this);
34745     },
34746
34747
34748     initEvents: function()
34749     {
34750
34751
34752         // ie scrollbar fix
34753         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34754             document.body.scroll = "no";
34755         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34756             this.el.position('relative');
34757         }
34758         this.id = this.el.id;
34759         this.el.addClass("roo-layout-container");
34760         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34761         if(this.el.dom != document.body ) {
34762             this.el.on('resize', this.layout,this);
34763             this.el.on('show', this.layout,this);
34764         }
34765
34766     },
34767
34768     /**
34769      * Returns true if this layout is currently being updated
34770      * @return {Boolean}
34771      */
34772     isUpdating : function(){
34773         return this.updating;
34774     },
34775
34776     /**
34777      * Suspend the LayoutManager from doing auto-layouts while
34778      * making multiple add or remove calls
34779      */
34780     beginUpdate : function(){
34781         this.updating = true;
34782     },
34783
34784     /**
34785      * Restore auto-layouts and optionally disable the manager from performing a layout
34786      * @param {Boolean} noLayout true to disable a layout update
34787      */
34788     endUpdate : function(noLayout){
34789         this.updating = false;
34790         if(!noLayout){
34791             this.layout();
34792         }
34793     },
34794
34795     layout: function(){
34796         // abstract...
34797     },
34798
34799     onRegionResized : function(region, newSize){
34800         this.fireEvent("regionresized", region, newSize);
34801         this.layout();
34802     },
34803
34804     onRegionCollapsed : function(region){
34805         this.fireEvent("regioncollapsed", region);
34806     },
34807
34808     onRegionExpanded : function(region){
34809         this.fireEvent("regionexpanded", region);
34810     },
34811
34812     /**
34813      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34814      * performs box-model adjustments.
34815      * @return {Object} The size as an object {width: (the width), height: (the height)}
34816      */
34817     getViewSize : function()
34818     {
34819         var size;
34820         if(this.el.dom != document.body){
34821             size = this.el.getSize();
34822         }else{
34823             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34824         }
34825         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34826         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34827         return size;
34828     },
34829
34830     /**
34831      * Returns the Element this layout is bound to.
34832      * @return {Roo.Element}
34833      */
34834     getEl : function(){
34835         return this.el;
34836     },
34837
34838     /**
34839      * Returns the specified region.
34840      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34841      * @return {Roo.LayoutRegion}
34842      */
34843     getRegion : function(target){
34844         return this.regions[target.toLowerCase()];
34845     },
34846
34847     onWindowResize : function(){
34848         if(this.monitorWindowResize){
34849             this.layout();
34850         }
34851     }
34852 });
34853 /*
34854  * Based on:
34855  * Ext JS Library 1.1.1
34856  * Copyright(c) 2006-2007, Ext JS, LLC.
34857  *
34858  * Originally Released Under LGPL - original licence link has changed is not relivant.
34859  *
34860  * Fork - LGPL
34861  * <script type="text/javascript">
34862  */
34863 /**
34864  * @class Roo.bootstrap.layout.Border
34865  * @extends Roo.bootstrap.layout.Manager
34866  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34867  * please see: examples/bootstrap/nested.html<br><br>
34868  
34869 <b>The container the layout is rendered into can be either the body element or any other element.
34870 If it is not the body element, the container needs to either be an absolute positioned element,
34871 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34872 the container size if it is not the body element.</b>
34873
34874 * @constructor
34875 * Create a new Border
34876 * @param {Object} config Configuration options
34877  */
34878 Roo.bootstrap.layout.Border = function(config){
34879     config = config || {};
34880     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34881     
34882     
34883     
34884     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34885         if(config[region]){
34886             config[region].region = region;
34887             this.addRegion(config[region]);
34888         }
34889     },this);
34890     
34891 };
34892
34893 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34894
34895 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34896     /**
34897      * Creates and adds a new region if it doesn't already exist.
34898      * @param {String} target The target region key (north, south, east, west or center).
34899      * @param {Object} config The regions config object
34900      * @return {BorderLayoutRegion} The new region
34901      */
34902     addRegion : function(config)
34903     {
34904         if(!this.regions[config.region]){
34905             var r = this.factory(config);
34906             this.bindRegion(r);
34907         }
34908         return this.regions[config.region];
34909     },
34910
34911     // private (kinda)
34912     bindRegion : function(r){
34913         this.regions[r.config.region] = r;
34914         
34915         r.on("visibilitychange",    this.layout, this);
34916         r.on("paneladded",          this.layout, this);
34917         r.on("panelremoved",        this.layout, this);
34918         r.on("invalidated",         this.layout, this);
34919         r.on("resized",             this.onRegionResized, this);
34920         r.on("collapsed",           this.onRegionCollapsed, this);
34921         r.on("expanded",            this.onRegionExpanded, this);
34922     },
34923
34924     /**
34925      * Performs a layout update.
34926      */
34927     layout : function()
34928     {
34929         if(this.updating) {
34930             return;
34931         }
34932         
34933         // render all the rebions if they have not been done alreayd?
34934         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34935             if(this.regions[region] && !this.regions[region].bodyEl){
34936                 this.regions[region].onRender(this.el)
34937             }
34938         },this);
34939         
34940         var size = this.getViewSize();
34941         var w = size.width;
34942         var h = size.height;
34943         var centerW = w;
34944         var centerH = h;
34945         var centerY = 0;
34946         var centerX = 0;
34947         //var x = 0, y = 0;
34948
34949         var rs = this.regions;
34950         var north = rs["north"];
34951         var south = rs["south"]; 
34952         var west = rs["west"];
34953         var east = rs["east"];
34954         var center = rs["center"];
34955         //if(this.hideOnLayout){ // not supported anymore
34956             //c.el.setStyle("display", "none");
34957         //}
34958         if(north && north.isVisible()){
34959             var b = north.getBox();
34960             var m = north.getMargins();
34961             b.width = w - (m.left+m.right);
34962             b.x = m.left;
34963             b.y = m.top;
34964             centerY = b.height + b.y + m.bottom;
34965             centerH -= centerY;
34966             north.updateBox(this.safeBox(b));
34967         }
34968         if(south && south.isVisible()){
34969             var b = south.getBox();
34970             var m = south.getMargins();
34971             b.width = w - (m.left+m.right);
34972             b.x = m.left;
34973             var totalHeight = (b.height + m.top + m.bottom);
34974             b.y = h - totalHeight + m.top;
34975             centerH -= totalHeight;
34976             south.updateBox(this.safeBox(b));
34977         }
34978         if(west && west.isVisible()){
34979             var b = west.getBox();
34980             var m = west.getMargins();
34981             b.height = centerH - (m.top+m.bottom);
34982             b.x = m.left;
34983             b.y = centerY + m.top;
34984             var totalWidth = (b.width + m.left + m.right);
34985             centerX += totalWidth;
34986             centerW -= totalWidth;
34987             west.updateBox(this.safeBox(b));
34988         }
34989         if(east && east.isVisible()){
34990             var b = east.getBox();
34991             var m = east.getMargins();
34992             b.height = centerH - (m.top+m.bottom);
34993             var totalWidth = (b.width + m.left + m.right);
34994             b.x = w - totalWidth + m.left;
34995             b.y = centerY + m.top;
34996             centerW -= totalWidth;
34997             east.updateBox(this.safeBox(b));
34998         }
34999         if(center){
35000             var m = center.getMargins();
35001             var centerBox = {
35002                 x: centerX + m.left,
35003                 y: centerY + m.top,
35004                 width: centerW - (m.left+m.right),
35005                 height: centerH - (m.top+m.bottom)
35006             };
35007             //if(this.hideOnLayout){
35008                 //center.el.setStyle("display", "block");
35009             //}
35010             center.updateBox(this.safeBox(centerBox));
35011         }
35012         this.el.repaint();
35013         this.fireEvent("layout", this);
35014     },
35015
35016     // private
35017     safeBox : function(box){
35018         box.width = Math.max(0, box.width);
35019         box.height = Math.max(0, box.height);
35020         return box;
35021     },
35022
35023     /**
35024      * Adds a ContentPanel (or subclass) to this layout.
35025      * @param {String} target The target region key (north, south, east, west or center).
35026      * @param {Roo.ContentPanel} panel The panel to add
35027      * @return {Roo.ContentPanel} The added panel
35028      */
35029     add : function(target, panel){
35030          
35031         target = target.toLowerCase();
35032         return this.regions[target].add(panel);
35033     },
35034
35035     /**
35036      * Remove a ContentPanel (or subclass) to this layout.
35037      * @param {String} target The target region key (north, south, east, west or center).
35038      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35039      * @return {Roo.ContentPanel} The removed panel
35040      */
35041     remove : function(target, panel){
35042         target = target.toLowerCase();
35043         return this.regions[target].remove(panel);
35044     },
35045
35046     /**
35047      * Searches all regions for a panel with the specified id
35048      * @param {String} panelId
35049      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35050      */
35051     findPanel : function(panelId){
35052         var rs = this.regions;
35053         for(var target in rs){
35054             if(typeof rs[target] != "function"){
35055                 var p = rs[target].getPanel(panelId);
35056                 if(p){
35057                     return p;
35058                 }
35059             }
35060         }
35061         return null;
35062     },
35063
35064     /**
35065      * Searches all regions for a panel with the specified id and activates (shows) it.
35066      * @param {String/ContentPanel} panelId The panels id or the panel itself
35067      * @return {Roo.ContentPanel} The shown panel or null
35068      */
35069     showPanel : function(panelId) {
35070       var rs = this.regions;
35071       for(var target in rs){
35072          var r = rs[target];
35073          if(typeof r != "function"){
35074             if(r.hasPanel(panelId)){
35075                return r.showPanel(panelId);
35076             }
35077          }
35078       }
35079       return null;
35080    },
35081
35082    /**
35083      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35084      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35085      */
35086    /*
35087     restoreState : function(provider){
35088         if(!provider){
35089             provider = Roo.state.Manager;
35090         }
35091         var sm = new Roo.LayoutStateManager();
35092         sm.init(this, provider);
35093     },
35094 */
35095  
35096  
35097     /**
35098      * Adds a xtype elements to the layout.
35099      * <pre><code>
35100
35101 layout.addxtype({
35102        xtype : 'ContentPanel',
35103        region: 'west',
35104        items: [ .... ]
35105    }
35106 );
35107
35108 layout.addxtype({
35109         xtype : 'NestedLayoutPanel',
35110         region: 'west',
35111         layout: {
35112            center: { },
35113            west: { }   
35114         },
35115         items : [ ... list of content panels or nested layout panels.. ]
35116    }
35117 );
35118 </code></pre>
35119      * @param {Object} cfg Xtype definition of item to add.
35120      */
35121     addxtype : function(cfg)
35122     {
35123         // basically accepts a pannel...
35124         // can accept a layout region..!?!?
35125         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35126         
35127         
35128         // theory?  children can only be panels??
35129         
35130         //if (!cfg.xtype.match(/Panel$/)) {
35131         //    return false;
35132         //}
35133         var ret = false;
35134         
35135         if (typeof(cfg.region) == 'undefined') {
35136             Roo.log("Failed to add Panel, region was not set");
35137             Roo.log(cfg);
35138             return false;
35139         }
35140         var region = cfg.region;
35141         delete cfg.region;
35142         
35143           
35144         var xitems = [];
35145         if (cfg.items) {
35146             xitems = cfg.items;
35147             delete cfg.items;
35148         }
35149         var nb = false;
35150         
35151         switch(cfg.xtype) 
35152         {
35153             case 'Content':  // ContentPanel (el, cfg)
35154             case 'Scroll':  // ContentPanel (el, cfg)
35155             case 'View': 
35156                 cfg.autoCreate = true;
35157                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35158                 //} else {
35159                 //    var el = this.el.createChild();
35160                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35161                 //}
35162                 
35163                 this.add(region, ret);
35164                 break;
35165             
35166             /*
35167             case 'TreePanel': // our new panel!
35168                 cfg.el = this.el.createChild();
35169                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35170                 this.add(region, ret);
35171                 break;
35172             */
35173             
35174             case 'Nest': 
35175                 // create a new Layout (which is  a Border Layout...
35176                 
35177                 var clayout = cfg.layout;
35178                 clayout.el  = this.el.createChild();
35179                 clayout.items   = clayout.items  || [];
35180                 
35181                 delete cfg.layout;
35182                 
35183                 // replace this exitems with the clayout ones..
35184                 xitems = clayout.items;
35185                  
35186                 // force background off if it's in center...
35187                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35188                     cfg.background = false;
35189                 }
35190                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35191                 
35192                 
35193                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35194                 //console.log('adding nested layout panel '  + cfg.toSource());
35195                 this.add(region, ret);
35196                 nb = {}; /// find first...
35197                 break;
35198             
35199             case 'Grid':
35200                 
35201                 // needs grid and region
35202                 
35203                 //var el = this.getRegion(region).el.createChild();
35204                 /*
35205                  *var el = this.el.createChild();
35206                 // create the grid first...
35207                 cfg.grid.container = el;
35208                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35209                 */
35210                 
35211                 if (region == 'center' && this.active ) {
35212                     cfg.background = false;
35213                 }
35214                 
35215                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35216                 
35217                 this.add(region, ret);
35218                 /*
35219                 if (cfg.background) {
35220                     // render grid on panel activation (if panel background)
35221                     ret.on('activate', function(gp) {
35222                         if (!gp.grid.rendered) {
35223                     //        gp.grid.render(el);
35224                         }
35225                     });
35226                 } else {
35227                   //  cfg.grid.render(el);
35228                 }
35229                 */
35230                 break;
35231            
35232            
35233             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35234                 // it was the old xcomponent building that caused this before.
35235                 // espeically if border is the top element in the tree.
35236                 ret = this;
35237                 break; 
35238                 
35239                     
35240                 
35241                 
35242                 
35243             default:
35244                 /*
35245                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35246                     
35247                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35248                     this.add(region, ret);
35249                 } else {
35250                 */
35251                     Roo.log(cfg);
35252                     throw "Can not add '" + cfg.xtype + "' to Border";
35253                     return null;
35254              
35255                                 
35256              
35257         }
35258         this.beginUpdate();
35259         // add children..
35260         var region = '';
35261         var abn = {};
35262         Roo.each(xitems, function(i)  {
35263             region = nb && i.region ? i.region : false;
35264             
35265             var add = ret.addxtype(i);
35266            
35267             if (region) {
35268                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35269                 if (!i.background) {
35270                     abn[region] = nb[region] ;
35271                 }
35272             }
35273             
35274         });
35275         this.endUpdate();
35276
35277         // make the last non-background panel active..
35278         //if (nb) { Roo.log(abn); }
35279         if (nb) {
35280             
35281             for(var r in abn) {
35282                 region = this.getRegion(r);
35283                 if (region) {
35284                     // tried using nb[r], but it does not work..
35285                      
35286                     region.showPanel(abn[r]);
35287                    
35288                 }
35289             }
35290         }
35291         return ret;
35292         
35293     },
35294     
35295     
35296 // private
35297     factory : function(cfg)
35298     {
35299         
35300         var validRegions = Roo.bootstrap.layout.Border.regions;
35301
35302         var target = cfg.region;
35303         cfg.mgr = this;
35304         
35305         var r = Roo.bootstrap.layout;
35306         Roo.log(target);
35307         switch(target){
35308             case "north":
35309                 return new r.North(cfg);
35310             case "south":
35311                 return new r.South(cfg);
35312             case "east":
35313                 return new r.East(cfg);
35314             case "west":
35315                 return new r.West(cfg);
35316             case "center":
35317                 return new r.Center(cfg);
35318         }
35319         throw 'Layout region "'+target+'" not supported.';
35320     }
35321     
35322     
35323 });
35324  /*
35325  * Based on:
35326  * Ext JS Library 1.1.1
35327  * Copyright(c) 2006-2007, Ext JS, LLC.
35328  *
35329  * Originally Released Under LGPL - original licence link has changed is not relivant.
35330  *
35331  * Fork - LGPL
35332  * <script type="text/javascript">
35333  */
35334  
35335 /**
35336  * @class Roo.bootstrap.layout.Basic
35337  * @extends Roo.util.Observable
35338  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35339  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35340  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35341  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35342  * @cfg {string}   region  the region that it inhabits..
35343  * @cfg {bool}   skipConfig skip config?
35344  * 
35345
35346  */
35347 Roo.bootstrap.layout.Basic = function(config){
35348     
35349     this.mgr = config.mgr;
35350     
35351     this.position = config.region;
35352     
35353     var skipConfig = config.skipConfig;
35354     
35355     this.events = {
35356         /**
35357          * @scope Roo.BasicLayoutRegion
35358          */
35359         
35360         /**
35361          * @event beforeremove
35362          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35363          * @param {Roo.LayoutRegion} this
35364          * @param {Roo.ContentPanel} panel The panel
35365          * @param {Object} e The cancel event object
35366          */
35367         "beforeremove" : true,
35368         /**
35369          * @event invalidated
35370          * Fires when the layout for this region is changed.
35371          * @param {Roo.LayoutRegion} this
35372          */
35373         "invalidated" : true,
35374         /**
35375          * @event visibilitychange
35376          * Fires when this region is shown or hidden 
35377          * @param {Roo.LayoutRegion} this
35378          * @param {Boolean} visibility true or false
35379          */
35380         "visibilitychange" : true,
35381         /**
35382          * @event paneladded
35383          * Fires when a panel is added. 
35384          * @param {Roo.LayoutRegion} this
35385          * @param {Roo.ContentPanel} panel The panel
35386          */
35387         "paneladded" : true,
35388         /**
35389          * @event panelremoved
35390          * Fires when a panel is removed. 
35391          * @param {Roo.LayoutRegion} this
35392          * @param {Roo.ContentPanel} panel The panel
35393          */
35394         "panelremoved" : true,
35395         /**
35396          * @event beforecollapse
35397          * Fires when this region before collapse.
35398          * @param {Roo.LayoutRegion} this
35399          */
35400         "beforecollapse" : true,
35401         /**
35402          * @event collapsed
35403          * Fires when this region is collapsed.
35404          * @param {Roo.LayoutRegion} this
35405          */
35406         "collapsed" : true,
35407         /**
35408          * @event expanded
35409          * Fires when this region is expanded.
35410          * @param {Roo.LayoutRegion} this
35411          */
35412         "expanded" : true,
35413         /**
35414          * @event slideshow
35415          * Fires when this region is slid into view.
35416          * @param {Roo.LayoutRegion} this
35417          */
35418         "slideshow" : true,
35419         /**
35420          * @event slidehide
35421          * Fires when this region slides out of view. 
35422          * @param {Roo.LayoutRegion} this
35423          */
35424         "slidehide" : true,
35425         /**
35426          * @event panelactivated
35427          * Fires when a panel is activated. 
35428          * @param {Roo.LayoutRegion} this
35429          * @param {Roo.ContentPanel} panel The activated panel
35430          */
35431         "panelactivated" : true,
35432         /**
35433          * @event resized
35434          * Fires when the user resizes this region. 
35435          * @param {Roo.LayoutRegion} this
35436          * @param {Number} newSize The new size (width for east/west, height for north/south)
35437          */
35438         "resized" : true
35439     };
35440     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35441     this.panels = new Roo.util.MixedCollection();
35442     this.panels.getKey = this.getPanelId.createDelegate(this);
35443     this.box = null;
35444     this.activePanel = null;
35445     // ensure listeners are added...
35446     
35447     if (config.listeners || config.events) {
35448         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35449             listeners : config.listeners || {},
35450             events : config.events || {}
35451         });
35452     }
35453     
35454     if(skipConfig !== true){
35455         this.applyConfig(config);
35456     }
35457 };
35458
35459 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35460 {
35461     getPanelId : function(p){
35462         return p.getId();
35463     },
35464     
35465     applyConfig : function(config){
35466         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35467         this.config = config;
35468         
35469     },
35470     
35471     /**
35472      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35473      * the width, for horizontal (north, south) the height.
35474      * @param {Number} newSize The new width or height
35475      */
35476     resizeTo : function(newSize){
35477         var el = this.el ? this.el :
35478                  (this.activePanel ? this.activePanel.getEl() : null);
35479         if(el){
35480             switch(this.position){
35481                 case "east":
35482                 case "west":
35483                     el.setWidth(newSize);
35484                     this.fireEvent("resized", this, newSize);
35485                 break;
35486                 case "north":
35487                 case "south":
35488                     el.setHeight(newSize);
35489                     this.fireEvent("resized", this, newSize);
35490                 break;                
35491             }
35492         }
35493     },
35494     
35495     getBox : function(){
35496         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35497     },
35498     
35499     getMargins : function(){
35500         return this.margins;
35501     },
35502     
35503     updateBox : function(box){
35504         this.box = box;
35505         var el = this.activePanel.getEl();
35506         el.dom.style.left = box.x + "px";
35507         el.dom.style.top = box.y + "px";
35508         this.activePanel.setSize(box.width, box.height);
35509     },
35510     
35511     /**
35512      * Returns the container element for this region.
35513      * @return {Roo.Element}
35514      */
35515     getEl : function(){
35516         return this.activePanel;
35517     },
35518     
35519     /**
35520      * Returns true if this region is currently visible.
35521      * @return {Boolean}
35522      */
35523     isVisible : function(){
35524         return this.activePanel ? true : false;
35525     },
35526     
35527     setActivePanel : function(panel){
35528         panel = this.getPanel(panel);
35529         if(this.activePanel && this.activePanel != panel){
35530             this.activePanel.setActiveState(false);
35531             this.activePanel.getEl().setLeftTop(-10000,-10000);
35532         }
35533         this.activePanel = panel;
35534         panel.setActiveState(true);
35535         if(this.box){
35536             panel.setSize(this.box.width, this.box.height);
35537         }
35538         this.fireEvent("panelactivated", this, panel);
35539         this.fireEvent("invalidated");
35540     },
35541     
35542     /**
35543      * Show the specified panel.
35544      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35545      * @return {Roo.ContentPanel} The shown panel or null
35546      */
35547     showPanel : function(panel){
35548         panel = this.getPanel(panel);
35549         if(panel){
35550             this.setActivePanel(panel);
35551         }
35552         return panel;
35553     },
35554     
35555     /**
35556      * Get the active panel for this region.
35557      * @return {Roo.ContentPanel} The active panel or null
35558      */
35559     getActivePanel : function(){
35560         return this.activePanel;
35561     },
35562     
35563     /**
35564      * Add the passed ContentPanel(s)
35565      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35566      * @return {Roo.ContentPanel} The panel added (if only one was added)
35567      */
35568     add : function(panel){
35569         if(arguments.length > 1){
35570             for(var i = 0, len = arguments.length; i < len; i++) {
35571                 this.add(arguments[i]);
35572             }
35573             return null;
35574         }
35575         if(this.hasPanel(panel)){
35576             this.showPanel(panel);
35577             return panel;
35578         }
35579         var el = panel.getEl();
35580         if(el.dom.parentNode != this.mgr.el.dom){
35581             this.mgr.el.dom.appendChild(el.dom);
35582         }
35583         if(panel.setRegion){
35584             panel.setRegion(this);
35585         }
35586         this.panels.add(panel);
35587         el.setStyle("position", "absolute");
35588         if(!panel.background){
35589             this.setActivePanel(panel);
35590             if(this.config.initialSize && this.panels.getCount()==1){
35591                 this.resizeTo(this.config.initialSize);
35592             }
35593         }
35594         this.fireEvent("paneladded", this, panel);
35595         return panel;
35596     },
35597     
35598     /**
35599      * Returns true if the panel is in this region.
35600      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35601      * @return {Boolean}
35602      */
35603     hasPanel : function(panel){
35604         if(typeof panel == "object"){ // must be panel obj
35605             panel = panel.getId();
35606         }
35607         return this.getPanel(panel) ? true : false;
35608     },
35609     
35610     /**
35611      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35612      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35613      * @param {Boolean} preservePanel Overrides the config preservePanel option
35614      * @return {Roo.ContentPanel} The panel that was removed
35615      */
35616     remove : function(panel, preservePanel){
35617         panel = this.getPanel(panel);
35618         if(!panel){
35619             return null;
35620         }
35621         var e = {};
35622         this.fireEvent("beforeremove", this, panel, e);
35623         if(e.cancel === true){
35624             return null;
35625         }
35626         var panelId = panel.getId();
35627         this.panels.removeKey(panelId);
35628         return panel;
35629     },
35630     
35631     /**
35632      * Returns the panel specified or null if it's not in this region.
35633      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35634      * @return {Roo.ContentPanel}
35635      */
35636     getPanel : function(id){
35637         if(typeof id == "object"){ // must be panel obj
35638             return id;
35639         }
35640         return this.panels.get(id);
35641     },
35642     
35643     /**
35644      * Returns this regions position (north/south/east/west/center).
35645      * @return {String} 
35646      */
35647     getPosition: function(){
35648         return this.position;    
35649     }
35650 });/*
35651  * Based on:
35652  * Ext JS Library 1.1.1
35653  * Copyright(c) 2006-2007, Ext JS, LLC.
35654  *
35655  * Originally Released Under LGPL - original licence link has changed is not relivant.
35656  *
35657  * Fork - LGPL
35658  * <script type="text/javascript">
35659  */
35660  
35661 /**
35662  * @class Roo.bootstrap.layout.Region
35663  * @extends Roo.bootstrap.layout.Basic
35664  * This class represents a region in a layout manager.
35665  
35666  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35667  * @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})
35668  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35669  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35670  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35671  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35672  * @cfg {String}    title           The title for the region (overrides panel titles)
35673  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35674  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35675  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35676  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35677  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35678  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35679  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35680  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35681  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35682  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35683
35684  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35685  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35686  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35687  * @cfg {Number}    width           For East/West panels
35688  * @cfg {Number}    height          For North/South panels
35689  * @cfg {Boolean}   split           To show the splitter
35690  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35691  * 
35692  * @cfg {string}   cls             Extra CSS classes to add to region
35693  * 
35694  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35695  * @cfg {string}   region  the region that it inhabits..
35696  *
35697
35698  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35699  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35700
35701  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35702  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35703  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35704  */
35705 Roo.bootstrap.layout.Region = function(config)
35706 {
35707     this.applyConfig(config);
35708
35709     var mgr = config.mgr;
35710     var pos = config.region;
35711     config.skipConfig = true;
35712     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35713     
35714     if (mgr.el) {
35715         this.onRender(mgr.el);   
35716     }
35717      
35718     this.visible = true;
35719     this.collapsed = false;
35720     this.unrendered_panels = [];
35721 };
35722
35723 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35724
35725     position: '', // set by wrapper (eg. north/south etc..)
35726     unrendered_panels : null,  // unrendered panels.
35727     createBody : function(){
35728         /** This region's body element 
35729         * @type Roo.Element */
35730         this.bodyEl = this.el.createChild({
35731                 tag: "div",
35732                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35733         });
35734     },
35735
35736     onRender: function(ctr, pos)
35737     {
35738         var dh = Roo.DomHelper;
35739         /** This region's container element 
35740         * @type Roo.Element */
35741         this.el = dh.append(ctr.dom, {
35742                 tag: "div",
35743                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35744             }, true);
35745         /** This region's title element 
35746         * @type Roo.Element */
35747     
35748         this.titleEl = dh.append(this.el.dom,
35749             {
35750                     tag: "div",
35751                     unselectable: "on",
35752                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35753                     children:[
35754                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35755                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35756                     ]}, true);
35757         
35758         this.titleEl.enableDisplayMode();
35759         /** This region's title text element 
35760         * @type HTMLElement */
35761         this.titleTextEl = this.titleEl.dom.firstChild;
35762         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35763         /*
35764         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35765         this.closeBtn.enableDisplayMode();
35766         this.closeBtn.on("click", this.closeClicked, this);
35767         this.closeBtn.hide();
35768     */
35769         this.createBody(this.config);
35770         if(this.config.hideWhenEmpty){
35771             this.hide();
35772             this.on("paneladded", this.validateVisibility, this);
35773             this.on("panelremoved", this.validateVisibility, this);
35774         }
35775         if(this.autoScroll){
35776             this.bodyEl.setStyle("overflow", "auto");
35777         }else{
35778             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35779         }
35780         //if(c.titlebar !== false){
35781             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35782                 this.titleEl.hide();
35783             }else{
35784                 this.titleEl.show();
35785                 if(this.config.title){
35786                     this.titleTextEl.innerHTML = this.config.title;
35787                 }
35788             }
35789         //}
35790         if(this.config.collapsed){
35791             this.collapse(true);
35792         }
35793         if(this.config.hidden){
35794             this.hide();
35795         }
35796         
35797         if (this.unrendered_panels && this.unrendered_panels.length) {
35798             for (var i =0;i< this.unrendered_panels.length; i++) {
35799                 this.add(this.unrendered_panels[i]);
35800             }
35801             this.unrendered_panels = null;
35802             
35803         }
35804         
35805     },
35806     
35807     applyConfig : function(c)
35808     {
35809         /*
35810          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35811             var dh = Roo.DomHelper;
35812             if(c.titlebar !== false){
35813                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35814                 this.collapseBtn.on("click", this.collapse, this);
35815                 this.collapseBtn.enableDisplayMode();
35816                 /*
35817                 if(c.showPin === true || this.showPin){
35818                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35819                     this.stickBtn.enableDisplayMode();
35820                     this.stickBtn.on("click", this.expand, this);
35821                     this.stickBtn.hide();
35822                 }
35823                 
35824             }
35825             */
35826             /** This region's collapsed element
35827             * @type Roo.Element */
35828             /*
35829              *
35830             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35831                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35832             ]}, true);
35833             
35834             if(c.floatable !== false){
35835                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35836                this.collapsedEl.on("click", this.collapseClick, this);
35837             }
35838
35839             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35840                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35841                    id: "message", unselectable: "on", style:{"float":"left"}});
35842                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35843              }
35844             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35845             this.expandBtn.on("click", this.expand, this);
35846             
35847         }
35848         
35849         if(this.collapseBtn){
35850             this.collapseBtn.setVisible(c.collapsible == true);
35851         }
35852         
35853         this.cmargins = c.cmargins || this.cmargins ||
35854                          (this.position == "west" || this.position == "east" ?
35855                              {top: 0, left: 2, right:2, bottom: 0} :
35856                              {top: 2, left: 0, right:0, bottom: 2});
35857         */
35858         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35859         
35860         
35861         this.bottomTabs = c.tabPosition != "top";
35862         
35863         this.autoScroll = c.autoScroll || false;
35864         
35865         
35866        
35867         
35868         this.duration = c.duration || .30;
35869         this.slideDuration = c.slideDuration || .45;
35870         this.config = c;
35871        
35872     },
35873     /**
35874      * Returns true if this region is currently visible.
35875      * @return {Boolean}
35876      */
35877     isVisible : function(){
35878         return this.visible;
35879     },
35880
35881     /**
35882      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35883      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35884      */
35885     //setCollapsedTitle : function(title){
35886     //    title = title || "&#160;";
35887      //   if(this.collapsedTitleTextEl){
35888       //      this.collapsedTitleTextEl.innerHTML = title;
35889        // }
35890     //},
35891
35892     getBox : function(){
35893         var b;
35894       //  if(!this.collapsed){
35895             b = this.el.getBox(false, true);
35896        // }else{
35897           //  b = this.collapsedEl.getBox(false, true);
35898         //}
35899         return b;
35900     },
35901
35902     getMargins : function(){
35903         return this.margins;
35904         //return this.collapsed ? this.cmargins : this.margins;
35905     },
35906 /*
35907     highlight : function(){
35908         this.el.addClass("x-layout-panel-dragover");
35909     },
35910
35911     unhighlight : function(){
35912         this.el.removeClass("x-layout-panel-dragover");
35913     },
35914 */
35915     updateBox : function(box)
35916     {
35917         if (!this.bodyEl) {
35918             return; // not rendered yet..
35919         }
35920         
35921         this.box = box;
35922         if(!this.collapsed){
35923             this.el.dom.style.left = box.x + "px";
35924             this.el.dom.style.top = box.y + "px";
35925             this.updateBody(box.width, box.height);
35926         }else{
35927             this.collapsedEl.dom.style.left = box.x + "px";
35928             this.collapsedEl.dom.style.top = box.y + "px";
35929             this.collapsedEl.setSize(box.width, box.height);
35930         }
35931         if(this.tabs){
35932             this.tabs.autoSizeTabs();
35933         }
35934     },
35935
35936     updateBody : function(w, h)
35937     {
35938         if(w !== null){
35939             this.el.setWidth(w);
35940             w -= this.el.getBorderWidth("rl");
35941             if(this.config.adjustments){
35942                 w += this.config.adjustments[0];
35943             }
35944         }
35945         if(h !== null && h > 0){
35946             this.el.setHeight(h);
35947             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35948             h -= this.el.getBorderWidth("tb");
35949             if(this.config.adjustments){
35950                 h += this.config.adjustments[1];
35951             }
35952             this.bodyEl.setHeight(h);
35953             if(this.tabs){
35954                 h = this.tabs.syncHeight(h);
35955             }
35956         }
35957         if(this.panelSize){
35958             w = w !== null ? w : this.panelSize.width;
35959             h = h !== null ? h : this.panelSize.height;
35960         }
35961         if(this.activePanel){
35962             var el = this.activePanel.getEl();
35963             w = w !== null ? w : el.getWidth();
35964             h = h !== null ? h : el.getHeight();
35965             this.panelSize = {width: w, height: h};
35966             this.activePanel.setSize(w, h);
35967         }
35968         if(Roo.isIE && this.tabs){
35969             this.tabs.el.repaint();
35970         }
35971     },
35972
35973     /**
35974      * Returns the container element for this region.
35975      * @return {Roo.Element}
35976      */
35977     getEl : function(){
35978         return this.el;
35979     },
35980
35981     /**
35982      * Hides this region.
35983      */
35984     hide : function(){
35985         //if(!this.collapsed){
35986             this.el.dom.style.left = "-2000px";
35987             this.el.hide();
35988         //}else{
35989          //   this.collapsedEl.dom.style.left = "-2000px";
35990          //   this.collapsedEl.hide();
35991        // }
35992         this.visible = false;
35993         this.fireEvent("visibilitychange", this, false);
35994     },
35995
35996     /**
35997      * Shows this region if it was previously hidden.
35998      */
35999     show : function(){
36000         //if(!this.collapsed){
36001             this.el.show();
36002         //}else{
36003         //    this.collapsedEl.show();
36004        // }
36005         this.visible = true;
36006         this.fireEvent("visibilitychange", this, true);
36007     },
36008 /*
36009     closeClicked : function(){
36010         if(this.activePanel){
36011             this.remove(this.activePanel);
36012         }
36013     },
36014
36015     collapseClick : function(e){
36016         if(this.isSlid){
36017            e.stopPropagation();
36018            this.slideIn();
36019         }else{
36020            e.stopPropagation();
36021            this.slideOut();
36022         }
36023     },
36024 */
36025     /**
36026      * Collapses this region.
36027      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36028      */
36029     /*
36030     collapse : function(skipAnim, skipCheck = false){
36031         if(this.collapsed) {
36032             return;
36033         }
36034         
36035         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36036             
36037             this.collapsed = true;
36038             if(this.split){
36039                 this.split.el.hide();
36040             }
36041             if(this.config.animate && skipAnim !== true){
36042                 this.fireEvent("invalidated", this);
36043                 this.animateCollapse();
36044             }else{
36045                 this.el.setLocation(-20000,-20000);
36046                 this.el.hide();
36047                 this.collapsedEl.show();
36048                 this.fireEvent("collapsed", this);
36049                 this.fireEvent("invalidated", this);
36050             }
36051         }
36052         
36053     },
36054 */
36055     animateCollapse : function(){
36056         // overridden
36057     },
36058
36059     /**
36060      * Expands this region if it was previously collapsed.
36061      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36062      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36063      */
36064     /*
36065     expand : function(e, skipAnim){
36066         if(e) {
36067             e.stopPropagation();
36068         }
36069         if(!this.collapsed || this.el.hasActiveFx()) {
36070             return;
36071         }
36072         if(this.isSlid){
36073             this.afterSlideIn();
36074             skipAnim = true;
36075         }
36076         this.collapsed = false;
36077         if(this.config.animate && skipAnim !== true){
36078             this.animateExpand();
36079         }else{
36080             this.el.show();
36081             if(this.split){
36082                 this.split.el.show();
36083             }
36084             this.collapsedEl.setLocation(-2000,-2000);
36085             this.collapsedEl.hide();
36086             this.fireEvent("invalidated", this);
36087             this.fireEvent("expanded", this);
36088         }
36089     },
36090 */
36091     animateExpand : function(){
36092         // overridden
36093     },
36094
36095     initTabs : function()
36096     {
36097         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36098         
36099         var ts = new Roo.bootstrap.panel.Tabs({
36100                 el: this.bodyEl.dom,
36101                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36102                 disableTooltips: this.config.disableTabTips,
36103                 toolbar : this.config.toolbar
36104             });
36105         
36106         if(this.config.hideTabs){
36107             ts.stripWrap.setDisplayed(false);
36108         }
36109         this.tabs = ts;
36110         ts.resizeTabs = this.config.resizeTabs === true;
36111         ts.minTabWidth = this.config.minTabWidth || 40;
36112         ts.maxTabWidth = this.config.maxTabWidth || 250;
36113         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36114         ts.monitorResize = false;
36115         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36116         ts.bodyEl.addClass('roo-layout-tabs-body');
36117         this.panels.each(this.initPanelAsTab, this);
36118     },
36119
36120     initPanelAsTab : function(panel){
36121         var ti = this.tabs.addTab(
36122             panel.getEl().id,
36123             panel.getTitle(),
36124             null,
36125             this.config.closeOnTab && panel.isClosable(),
36126             panel.tpl
36127         );
36128         if(panel.tabTip !== undefined){
36129             ti.setTooltip(panel.tabTip);
36130         }
36131         ti.on("activate", function(){
36132               this.setActivePanel(panel);
36133         }, this);
36134         
36135         if(this.config.closeOnTab){
36136             ti.on("beforeclose", function(t, e){
36137                 e.cancel = true;
36138                 this.remove(panel);
36139             }, this);
36140         }
36141         
36142         panel.tabItem = ti;
36143         
36144         return ti;
36145     },
36146
36147     updatePanelTitle : function(panel, title)
36148     {
36149         if(this.activePanel == panel){
36150             this.updateTitle(title);
36151         }
36152         if(this.tabs){
36153             var ti = this.tabs.getTab(panel.getEl().id);
36154             ti.setText(title);
36155             if(panel.tabTip !== undefined){
36156                 ti.setTooltip(panel.tabTip);
36157             }
36158         }
36159     },
36160
36161     updateTitle : function(title){
36162         if(this.titleTextEl && !this.config.title){
36163             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36164         }
36165     },
36166
36167     setActivePanel : function(panel)
36168     {
36169         panel = this.getPanel(panel);
36170         if(this.activePanel && this.activePanel != panel){
36171             if(this.activePanel.setActiveState(false) === false){
36172                 return;
36173             }
36174         }
36175         this.activePanel = panel;
36176         panel.setActiveState(true);
36177         if(this.panelSize){
36178             panel.setSize(this.panelSize.width, this.panelSize.height);
36179         }
36180         if(this.closeBtn){
36181             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36182         }
36183         this.updateTitle(panel.getTitle());
36184         if(this.tabs){
36185             this.fireEvent("invalidated", this);
36186         }
36187         this.fireEvent("panelactivated", this, panel);
36188     },
36189
36190     /**
36191      * Shows the specified panel.
36192      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36193      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36194      */
36195     showPanel : function(panel)
36196     {
36197         panel = this.getPanel(panel);
36198         if(panel){
36199             if(this.tabs){
36200                 var tab = this.tabs.getTab(panel.getEl().id);
36201                 if(tab.isHidden()){
36202                     this.tabs.unhideTab(tab.id);
36203                 }
36204                 tab.activate();
36205             }else{
36206                 this.setActivePanel(panel);
36207             }
36208         }
36209         return panel;
36210     },
36211
36212     /**
36213      * Get the active panel for this region.
36214      * @return {Roo.ContentPanel} The active panel or null
36215      */
36216     getActivePanel : function(){
36217         return this.activePanel;
36218     },
36219
36220     validateVisibility : function(){
36221         if(this.panels.getCount() < 1){
36222             this.updateTitle("&#160;");
36223             this.closeBtn.hide();
36224             this.hide();
36225         }else{
36226             if(!this.isVisible()){
36227                 this.show();
36228             }
36229         }
36230     },
36231
36232     /**
36233      * Adds the passed ContentPanel(s) to this region.
36234      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36235      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36236      */
36237     add : function(panel)
36238     {
36239         if(arguments.length > 1){
36240             for(var i = 0, len = arguments.length; i < len; i++) {
36241                 this.add(arguments[i]);
36242             }
36243             return null;
36244         }
36245         
36246         // if we have not been rendered yet, then we can not really do much of this..
36247         if (!this.bodyEl) {
36248             this.unrendered_panels.push(panel);
36249             return panel;
36250         }
36251         
36252         
36253         
36254         
36255         if(this.hasPanel(panel)){
36256             this.showPanel(panel);
36257             return panel;
36258         }
36259         panel.setRegion(this);
36260         this.panels.add(panel);
36261        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36262             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36263             // and hide them... ???
36264             this.bodyEl.dom.appendChild(panel.getEl().dom);
36265             if(panel.background !== true){
36266                 this.setActivePanel(panel);
36267             }
36268             this.fireEvent("paneladded", this, panel);
36269             return panel;
36270         }
36271         */
36272         if(!this.tabs){
36273             this.initTabs();
36274         }else{
36275             this.initPanelAsTab(panel);
36276         }
36277         
36278         
36279         if(panel.background !== true){
36280             this.tabs.activate(panel.getEl().id);
36281         }
36282         this.fireEvent("paneladded", this, panel);
36283         return panel;
36284     },
36285
36286     /**
36287      * Hides the tab for the specified panel.
36288      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36289      */
36290     hidePanel : function(panel){
36291         if(this.tabs && (panel = this.getPanel(panel))){
36292             this.tabs.hideTab(panel.getEl().id);
36293         }
36294     },
36295
36296     /**
36297      * Unhides the tab for a previously hidden panel.
36298      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36299      */
36300     unhidePanel : function(panel){
36301         if(this.tabs && (panel = this.getPanel(panel))){
36302             this.tabs.unhideTab(panel.getEl().id);
36303         }
36304     },
36305
36306     clearPanels : function(){
36307         while(this.panels.getCount() > 0){
36308              this.remove(this.panels.first());
36309         }
36310     },
36311
36312     /**
36313      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36314      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36315      * @param {Boolean} preservePanel Overrides the config preservePanel option
36316      * @return {Roo.ContentPanel} The panel that was removed
36317      */
36318     remove : function(panel, preservePanel)
36319     {
36320         panel = this.getPanel(panel);
36321         if(!panel){
36322             return null;
36323         }
36324         var e = {};
36325         this.fireEvent("beforeremove", this, panel, e);
36326         if(e.cancel === true){
36327             return null;
36328         }
36329         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36330         var panelId = panel.getId();
36331         this.panels.removeKey(panelId);
36332         if(preservePanel){
36333             document.body.appendChild(panel.getEl().dom);
36334         }
36335         if(this.tabs){
36336             this.tabs.removeTab(panel.getEl().id);
36337         }else if (!preservePanel){
36338             this.bodyEl.dom.removeChild(panel.getEl().dom);
36339         }
36340         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36341             var p = this.panels.first();
36342             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36343             tempEl.appendChild(p.getEl().dom);
36344             this.bodyEl.update("");
36345             this.bodyEl.dom.appendChild(p.getEl().dom);
36346             tempEl = null;
36347             this.updateTitle(p.getTitle());
36348             this.tabs = null;
36349             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36350             this.setActivePanel(p);
36351         }
36352         panel.setRegion(null);
36353         if(this.activePanel == panel){
36354             this.activePanel = null;
36355         }
36356         if(this.config.autoDestroy !== false && preservePanel !== true){
36357             try{panel.destroy();}catch(e){}
36358         }
36359         this.fireEvent("panelremoved", this, panel);
36360         return panel;
36361     },
36362
36363     /**
36364      * Returns the TabPanel component used by this region
36365      * @return {Roo.TabPanel}
36366      */
36367     getTabs : function(){
36368         return this.tabs;
36369     },
36370
36371     createTool : function(parentEl, className){
36372         var btn = Roo.DomHelper.append(parentEl, {
36373             tag: "div",
36374             cls: "x-layout-tools-button",
36375             children: [ {
36376                 tag: "div",
36377                 cls: "roo-layout-tools-button-inner " + className,
36378                 html: "&#160;"
36379             }]
36380         }, true);
36381         btn.addClassOnOver("roo-layout-tools-button-over");
36382         return btn;
36383     }
36384 });/*
36385  * Based on:
36386  * Ext JS Library 1.1.1
36387  * Copyright(c) 2006-2007, Ext JS, LLC.
36388  *
36389  * Originally Released Under LGPL - original licence link has changed is not relivant.
36390  *
36391  * Fork - LGPL
36392  * <script type="text/javascript">
36393  */
36394  
36395
36396
36397 /**
36398  * @class Roo.SplitLayoutRegion
36399  * @extends Roo.LayoutRegion
36400  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36401  */
36402 Roo.bootstrap.layout.Split = function(config){
36403     this.cursor = config.cursor;
36404     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36405 };
36406
36407 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36408 {
36409     splitTip : "Drag to resize.",
36410     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36411     useSplitTips : false,
36412
36413     applyConfig : function(config){
36414         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36415     },
36416     
36417     onRender : function(ctr,pos) {
36418         
36419         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36420         if(!this.config.split){
36421             return;
36422         }
36423         if(!this.split){
36424             
36425             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36426                             tag: "div",
36427                             id: this.el.id + "-split",
36428                             cls: "roo-layout-split roo-layout-split-"+this.position,
36429                             html: "&#160;"
36430             });
36431             /** The SplitBar for this region 
36432             * @type Roo.SplitBar */
36433             // does not exist yet...
36434             Roo.log([this.position, this.orientation]);
36435             
36436             this.split = new Roo.bootstrap.SplitBar({
36437                 dragElement : splitEl,
36438                 resizingElement: this.el,
36439                 orientation : this.orientation
36440             });
36441             
36442             this.split.on("moved", this.onSplitMove, this);
36443             this.split.useShim = this.config.useShim === true;
36444             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36445             if(this.useSplitTips){
36446                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36447             }
36448             //if(config.collapsible){
36449             //    this.split.el.on("dblclick", this.collapse,  this);
36450             //}
36451         }
36452         if(typeof this.config.minSize != "undefined"){
36453             this.split.minSize = this.config.minSize;
36454         }
36455         if(typeof this.config.maxSize != "undefined"){
36456             this.split.maxSize = this.config.maxSize;
36457         }
36458         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36459             this.hideSplitter();
36460         }
36461         
36462     },
36463
36464     getHMaxSize : function(){
36465          var cmax = this.config.maxSize || 10000;
36466          var center = this.mgr.getRegion("center");
36467          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36468     },
36469
36470     getVMaxSize : function(){
36471          var cmax = this.config.maxSize || 10000;
36472          var center = this.mgr.getRegion("center");
36473          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36474     },
36475
36476     onSplitMove : function(split, newSize){
36477         this.fireEvent("resized", this, newSize);
36478     },
36479     
36480     /** 
36481      * Returns the {@link Roo.SplitBar} for this region.
36482      * @return {Roo.SplitBar}
36483      */
36484     getSplitBar : function(){
36485         return this.split;
36486     },
36487     
36488     hide : function(){
36489         this.hideSplitter();
36490         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36491     },
36492
36493     hideSplitter : function(){
36494         if(this.split){
36495             this.split.el.setLocation(-2000,-2000);
36496             this.split.el.hide();
36497         }
36498     },
36499
36500     show : function(){
36501         if(this.split){
36502             this.split.el.show();
36503         }
36504         Roo.bootstrap.layout.Split.superclass.show.call(this);
36505     },
36506     
36507     beforeSlide: function(){
36508         if(Roo.isGecko){// firefox overflow auto bug workaround
36509             this.bodyEl.clip();
36510             if(this.tabs) {
36511                 this.tabs.bodyEl.clip();
36512             }
36513             if(this.activePanel){
36514                 this.activePanel.getEl().clip();
36515                 
36516                 if(this.activePanel.beforeSlide){
36517                     this.activePanel.beforeSlide();
36518                 }
36519             }
36520         }
36521     },
36522     
36523     afterSlide : function(){
36524         if(Roo.isGecko){// firefox overflow auto bug workaround
36525             this.bodyEl.unclip();
36526             if(this.tabs) {
36527                 this.tabs.bodyEl.unclip();
36528             }
36529             if(this.activePanel){
36530                 this.activePanel.getEl().unclip();
36531                 if(this.activePanel.afterSlide){
36532                     this.activePanel.afterSlide();
36533                 }
36534             }
36535         }
36536     },
36537
36538     initAutoHide : function(){
36539         if(this.autoHide !== false){
36540             if(!this.autoHideHd){
36541                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36542                 this.autoHideHd = {
36543                     "mouseout": function(e){
36544                         if(!e.within(this.el, true)){
36545                             st.delay(500);
36546                         }
36547                     },
36548                     "mouseover" : function(e){
36549                         st.cancel();
36550                     },
36551                     scope : this
36552                 };
36553             }
36554             this.el.on(this.autoHideHd);
36555         }
36556     },
36557
36558     clearAutoHide : function(){
36559         if(this.autoHide !== false){
36560             this.el.un("mouseout", this.autoHideHd.mouseout);
36561             this.el.un("mouseover", this.autoHideHd.mouseover);
36562         }
36563     },
36564
36565     clearMonitor : function(){
36566         Roo.get(document).un("click", this.slideInIf, this);
36567     },
36568
36569     // these names are backwards but not changed for compat
36570     slideOut : function(){
36571         if(this.isSlid || this.el.hasActiveFx()){
36572             return;
36573         }
36574         this.isSlid = true;
36575         if(this.collapseBtn){
36576             this.collapseBtn.hide();
36577         }
36578         this.closeBtnState = this.closeBtn.getStyle('display');
36579         this.closeBtn.hide();
36580         if(this.stickBtn){
36581             this.stickBtn.show();
36582         }
36583         this.el.show();
36584         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36585         this.beforeSlide();
36586         this.el.setStyle("z-index", 10001);
36587         this.el.slideIn(this.getSlideAnchor(), {
36588             callback: function(){
36589                 this.afterSlide();
36590                 this.initAutoHide();
36591                 Roo.get(document).on("click", this.slideInIf, this);
36592                 this.fireEvent("slideshow", this);
36593             },
36594             scope: this,
36595             block: true
36596         });
36597     },
36598
36599     afterSlideIn : function(){
36600         this.clearAutoHide();
36601         this.isSlid = false;
36602         this.clearMonitor();
36603         this.el.setStyle("z-index", "");
36604         if(this.collapseBtn){
36605             this.collapseBtn.show();
36606         }
36607         this.closeBtn.setStyle('display', this.closeBtnState);
36608         if(this.stickBtn){
36609             this.stickBtn.hide();
36610         }
36611         this.fireEvent("slidehide", this);
36612     },
36613
36614     slideIn : function(cb){
36615         if(!this.isSlid || this.el.hasActiveFx()){
36616             Roo.callback(cb);
36617             return;
36618         }
36619         this.isSlid = false;
36620         this.beforeSlide();
36621         this.el.slideOut(this.getSlideAnchor(), {
36622             callback: function(){
36623                 this.el.setLeftTop(-10000, -10000);
36624                 this.afterSlide();
36625                 this.afterSlideIn();
36626                 Roo.callback(cb);
36627             },
36628             scope: this,
36629             block: true
36630         });
36631     },
36632     
36633     slideInIf : function(e){
36634         if(!e.within(this.el)){
36635             this.slideIn();
36636         }
36637     },
36638
36639     animateCollapse : function(){
36640         this.beforeSlide();
36641         this.el.setStyle("z-index", 20000);
36642         var anchor = this.getSlideAnchor();
36643         this.el.slideOut(anchor, {
36644             callback : function(){
36645                 this.el.setStyle("z-index", "");
36646                 this.collapsedEl.slideIn(anchor, {duration:.3});
36647                 this.afterSlide();
36648                 this.el.setLocation(-10000,-10000);
36649                 this.el.hide();
36650                 this.fireEvent("collapsed", this);
36651             },
36652             scope: this,
36653             block: true
36654         });
36655     },
36656
36657     animateExpand : function(){
36658         this.beforeSlide();
36659         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36660         this.el.setStyle("z-index", 20000);
36661         this.collapsedEl.hide({
36662             duration:.1
36663         });
36664         this.el.slideIn(this.getSlideAnchor(), {
36665             callback : function(){
36666                 this.el.setStyle("z-index", "");
36667                 this.afterSlide();
36668                 if(this.split){
36669                     this.split.el.show();
36670                 }
36671                 this.fireEvent("invalidated", this);
36672                 this.fireEvent("expanded", this);
36673             },
36674             scope: this,
36675             block: true
36676         });
36677     },
36678
36679     anchors : {
36680         "west" : "left",
36681         "east" : "right",
36682         "north" : "top",
36683         "south" : "bottom"
36684     },
36685
36686     sanchors : {
36687         "west" : "l",
36688         "east" : "r",
36689         "north" : "t",
36690         "south" : "b"
36691     },
36692
36693     canchors : {
36694         "west" : "tl-tr",
36695         "east" : "tr-tl",
36696         "north" : "tl-bl",
36697         "south" : "bl-tl"
36698     },
36699
36700     getAnchor : function(){
36701         return this.anchors[this.position];
36702     },
36703
36704     getCollapseAnchor : function(){
36705         return this.canchors[this.position];
36706     },
36707
36708     getSlideAnchor : function(){
36709         return this.sanchors[this.position];
36710     },
36711
36712     getAlignAdj : function(){
36713         var cm = this.cmargins;
36714         switch(this.position){
36715             case "west":
36716                 return [0, 0];
36717             break;
36718             case "east":
36719                 return [0, 0];
36720             break;
36721             case "north":
36722                 return [0, 0];
36723             break;
36724             case "south":
36725                 return [0, 0];
36726             break;
36727         }
36728     },
36729
36730     getExpandAdj : function(){
36731         var c = this.collapsedEl, cm = this.cmargins;
36732         switch(this.position){
36733             case "west":
36734                 return [-(cm.right+c.getWidth()+cm.left), 0];
36735             break;
36736             case "east":
36737                 return [cm.right+c.getWidth()+cm.left, 0];
36738             break;
36739             case "north":
36740                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36741             break;
36742             case "south":
36743                 return [0, cm.top+cm.bottom+c.getHeight()];
36744             break;
36745         }
36746     }
36747 });/*
36748  * Based on:
36749  * Ext JS Library 1.1.1
36750  * Copyright(c) 2006-2007, Ext JS, LLC.
36751  *
36752  * Originally Released Under LGPL - original licence link has changed is not relivant.
36753  *
36754  * Fork - LGPL
36755  * <script type="text/javascript">
36756  */
36757 /*
36758  * These classes are private internal classes
36759  */
36760 Roo.bootstrap.layout.Center = function(config){
36761     config.region = "center";
36762     Roo.bootstrap.layout.Region.call(this, config);
36763     this.visible = true;
36764     this.minWidth = config.minWidth || 20;
36765     this.minHeight = config.minHeight || 20;
36766 };
36767
36768 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36769     hide : function(){
36770         // center panel can't be hidden
36771     },
36772     
36773     show : function(){
36774         // center panel can't be hidden
36775     },
36776     
36777     getMinWidth: function(){
36778         return this.minWidth;
36779     },
36780     
36781     getMinHeight: function(){
36782         return this.minHeight;
36783     }
36784 });
36785
36786
36787
36788
36789  
36790
36791
36792
36793
36794
36795 Roo.bootstrap.layout.North = function(config)
36796 {
36797     config.region = 'north';
36798     config.cursor = 'n-resize';
36799     
36800     Roo.bootstrap.layout.Split.call(this, config);
36801     
36802     
36803     if(this.split){
36804         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36805         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36806         this.split.el.addClass("roo-layout-split-v");
36807     }
36808     var size = config.initialSize || config.height;
36809     if(typeof size != "undefined"){
36810         this.el.setHeight(size);
36811     }
36812 };
36813 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36814 {
36815     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36816     
36817     
36818     
36819     getBox : function(){
36820         if(this.collapsed){
36821             return this.collapsedEl.getBox();
36822         }
36823         var box = this.el.getBox();
36824         if(this.split){
36825             box.height += this.split.el.getHeight();
36826         }
36827         return box;
36828     },
36829     
36830     updateBox : function(box){
36831         if(this.split && !this.collapsed){
36832             box.height -= this.split.el.getHeight();
36833             this.split.el.setLeft(box.x);
36834             this.split.el.setTop(box.y+box.height);
36835             this.split.el.setWidth(box.width);
36836         }
36837         if(this.collapsed){
36838             this.updateBody(box.width, null);
36839         }
36840         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36841     }
36842 });
36843
36844
36845
36846
36847
36848 Roo.bootstrap.layout.South = function(config){
36849     config.region = 'south';
36850     config.cursor = 's-resize';
36851     Roo.bootstrap.layout.Split.call(this, config);
36852     if(this.split){
36853         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36854         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36855         this.split.el.addClass("roo-layout-split-v");
36856     }
36857     var size = config.initialSize || config.height;
36858     if(typeof size != "undefined"){
36859         this.el.setHeight(size);
36860     }
36861 };
36862
36863 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36864     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36865     getBox : function(){
36866         if(this.collapsed){
36867             return this.collapsedEl.getBox();
36868         }
36869         var box = this.el.getBox();
36870         if(this.split){
36871             var sh = this.split.el.getHeight();
36872             box.height += sh;
36873             box.y -= sh;
36874         }
36875         return box;
36876     },
36877     
36878     updateBox : function(box){
36879         if(this.split && !this.collapsed){
36880             var sh = this.split.el.getHeight();
36881             box.height -= sh;
36882             box.y += sh;
36883             this.split.el.setLeft(box.x);
36884             this.split.el.setTop(box.y-sh);
36885             this.split.el.setWidth(box.width);
36886         }
36887         if(this.collapsed){
36888             this.updateBody(box.width, null);
36889         }
36890         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36891     }
36892 });
36893
36894 Roo.bootstrap.layout.East = function(config){
36895     config.region = "east";
36896     config.cursor = "e-resize";
36897     Roo.bootstrap.layout.Split.call(this, config);
36898     if(this.split){
36899         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36900         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36901         this.split.el.addClass("roo-layout-split-h");
36902     }
36903     var size = config.initialSize || config.width;
36904     if(typeof size != "undefined"){
36905         this.el.setWidth(size);
36906     }
36907 };
36908 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36909     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36910     getBox : function(){
36911         if(this.collapsed){
36912             return this.collapsedEl.getBox();
36913         }
36914         var box = this.el.getBox();
36915         if(this.split){
36916             var sw = this.split.el.getWidth();
36917             box.width += sw;
36918             box.x -= sw;
36919         }
36920         return box;
36921     },
36922
36923     updateBox : function(box){
36924         if(this.split && !this.collapsed){
36925             var sw = this.split.el.getWidth();
36926             box.width -= sw;
36927             this.split.el.setLeft(box.x);
36928             this.split.el.setTop(box.y);
36929             this.split.el.setHeight(box.height);
36930             box.x += sw;
36931         }
36932         if(this.collapsed){
36933             this.updateBody(null, box.height);
36934         }
36935         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36936     }
36937 });
36938
36939 Roo.bootstrap.layout.West = function(config){
36940     config.region = "west";
36941     config.cursor = "w-resize";
36942     
36943     Roo.bootstrap.layout.Split.call(this, config);
36944     if(this.split){
36945         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36946         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36947         this.split.el.addClass("roo-layout-split-h");
36948     }
36949     
36950 };
36951 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36952     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36953     
36954     onRender: function(ctr, pos)
36955     {
36956         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36957         var size = this.config.initialSize || this.config.width;
36958         if(typeof size != "undefined"){
36959             this.el.setWidth(size);
36960         }
36961     },
36962     
36963     getBox : function(){
36964         if(this.collapsed){
36965             return this.collapsedEl.getBox();
36966         }
36967         var box = this.el.getBox();
36968         if(this.split){
36969             box.width += this.split.el.getWidth();
36970         }
36971         return box;
36972     },
36973     
36974     updateBox : function(box){
36975         if(this.split && !this.collapsed){
36976             var sw = this.split.el.getWidth();
36977             box.width -= sw;
36978             this.split.el.setLeft(box.x+box.width);
36979             this.split.el.setTop(box.y);
36980             this.split.el.setHeight(box.height);
36981         }
36982         if(this.collapsed){
36983             this.updateBody(null, box.height);
36984         }
36985         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36986     }
36987 });
36988 Roo.namespace("Roo.bootstrap.panel");/*
36989  * Based on:
36990  * Ext JS Library 1.1.1
36991  * Copyright(c) 2006-2007, Ext JS, LLC.
36992  *
36993  * Originally Released Under LGPL - original licence link has changed is not relivant.
36994  *
36995  * Fork - LGPL
36996  * <script type="text/javascript">
36997  */
36998 /**
36999  * @class Roo.ContentPanel
37000  * @extends Roo.util.Observable
37001  * A basic ContentPanel element.
37002  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37003  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37004  * @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
37005  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37006  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37007  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37008  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37009  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37010  * @cfg {String} title          The title for this panel
37011  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37012  * @cfg {String} url            Calls {@link #setUrl} with this value
37013  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37014  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37015  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37016  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37017  * @cfg {Boolean} badges render the badges
37018
37019  * @constructor
37020  * Create a new ContentPanel.
37021  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37022  * @param {String/Object} config A string to set only the title or a config object
37023  * @param {String} content (optional) Set the HTML content for this panel
37024  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37025  */
37026 Roo.bootstrap.panel.Content = function( config){
37027     
37028     this.tpl = config.tpl || false;
37029     
37030     var el = config.el;
37031     var content = config.content;
37032
37033     if(config.autoCreate){ // xtype is available if this is called from factory
37034         el = Roo.id();
37035     }
37036     this.el = Roo.get(el);
37037     if(!this.el && config && config.autoCreate){
37038         if(typeof config.autoCreate == "object"){
37039             if(!config.autoCreate.id){
37040                 config.autoCreate.id = config.id||el;
37041             }
37042             this.el = Roo.DomHelper.append(document.body,
37043                         config.autoCreate, true);
37044         }else{
37045             var elcfg =  {   tag: "div",
37046                             cls: "roo-layout-inactive-content",
37047                             id: config.id||el
37048                             };
37049             if (config.html) {
37050                 elcfg.html = config.html;
37051                 
37052             }
37053                         
37054             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37055         }
37056     } 
37057     this.closable = false;
37058     this.loaded = false;
37059     this.active = false;
37060    
37061       
37062     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37063         
37064         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37065         
37066         this.wrapEl = this.el; //this.el.wrap();
37067         var ti = [];
37068         if (config.toolbar.items) {
37069             ti = config.toolbar.items ;
37070             delete config.toolbar.items ;
37071         }
37072         
37073         var nitems = [];
37074         this.toolbar.render(this.wrapEl, 'before');
37075         for(var i =0;i < ti.length;i++) {
37076           //  Roo.log(['add child', items[i]]);
37077             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37078         }
37079         this.toolbar.items = nitems;
37080         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37081         delete config.toolbar;
37082         
37083     }
37084     /*
37085     // xtype created footer. - not sure if will work as we normally have to render first..
37086     if (this.footer && !this.footer.el && this.footer.xtype) {
37087         if (!this.wrapEl) {
37088             this.wrapEl = this.el.wrap();
37089         }
37090     
37091         this.footer.container = this.wrapEl.createChild();
37092          
37093         this.footer = Roo.factory(this.footer, Roo);
37094         
37095     }
37096     */
37097     
37098      if(typeof config == "string"){
37099         this.title = config;
37100     }else{
37101         Roo.apply(this, config);
37102     }
37103     
37104     if(this.resizeEl){
37105         this.resizeEl = Roo.get(this.resizeEl, true);
37106     }else{
37107         this.resizeEl = this.el;
37108     }
37109     // handle view.xtype
37110     
37111  
37112     
37113     
37114     this.addEvents({
37115         /**
37116          * @event activate
37117          * Fires when this panel is activated. 
37118          * @param {Roo.ContentPanel} this
37119          */
37120         "activate" : true,
37121         /**
37122          * @event deactivate
37123          * Fires when this panel is activated. 
37124          * @param {Roo.ContentPanel} this
37125          */
37126         "deactivate" : true,
37127
37128         /**
37129          * @event resize
37130          * Fires when this panel is resized if fitToFrame is true.
37131          * @param {Roo.ContentPanel} this
37132          * @param {Number} width The width after any component adjustments
37133          * @param {Number} height The height after any component adjustments
37134          */
37135         "resize" : true,
37136         
37137          /**
37138          * @event render
37139          * Fires when this tab is created
37140          * @param {Roo.ContentPanel} this
37141          */
37142         "render" : true
37143         
37144         
37145         
37146     });
37147     
37148
37149     
37150     
37151     if(this.autoScroll){
37152         this.resizeEl.setStyle("overflow", "auto");
37153     } else {
37154         // fix randome scrolling
37155         //this.el.on('scroll', function() {
37156         //    Roo.log('fix random scolling');
37157         //    this.scrollTo('top',0); 
37158         //});
37159     }
37160     content = content || this.content;
37161     if(content){
37162         this.setContent(content);
37163     }
37164     if(config && config.url){
37165         this.setUrl(this.url, this.params, this.loadOnce);
37166     }
37167     
37168     
37169     
37170     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37171     
37172     if (this.view && typeof(this.view.xtype) != 'undefined') {
37173         this.view.el = this.el.appendChild(document.createElement("div"));
37174         this.view = Roo.factory(this.view); 
37175         this.view.render  &&  this.view.render(false, '');  
37176     }
37177     
37178     
37179     this.fireEvent('render', this);
37180 };
37181
37182 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37183     
37184     tabTip : '',
37185     
37186     setRegion : function(region){
37187         this.region = region;
37188         this.setActiveClass(region && !this.background);
37189     },
37190     
37191     
37192     setActiveClass: function(state)
37193     {
37194         if(state){
37195            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37196            this.el.setStyle('position','relative');
37197         }else{
37198            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37199            this.el.setStyle('position', 'absolute');
37200         } 
37201     },
37202     
37203     /**
37204      * Returns the toolbar for this Panel if one was configured. 
37205      * @return {Roo.Toolbar} 
37206      */
37207     getToolbar : function(){
37208         return this.toolbar;
37209     },
37210     
37211     setActiveState : function(active)
37212     {
37213         this.active = active;
37214         this.setActiveClass(active);
37215         if(!active){
37216             if(this.fireEvent("deactivate", this) === false){
37217                 return false;
37218             }
37219             return true;
37220         }
37221         this.fireEvent("activate", this);
37222         return true;
37223     },
37224     /**
37225      * Updates this panel's element
37226      * @param {String} content The new content
37227      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37228     */
37229     setContent : function(content, loadScripts){
37230         this.el.update(content, loadScripts);
37231     },
37232
37233     ignoreResize : function(w, h){
37234         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37235             return true;
37236         }else{
37237             this.lastSize = {width: w, height: h};
37238             return false;
37239         }
37240     },
37241     /**
37242      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37243      * @return {Roo.UpdateManager} The UpdateManager
37244      */
37245     getUpdateManager : function(){
37246         return this.el.getUpdateManager();
37247     },
37248      /**
37249      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37250      * @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:
37251 <pre><code>
37252 panel.load({
37253     url: "your-url.php",
37254     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37255     callback: yourFunction,
37256     scope: yourObject, //(optional scope)
37257     discardUrl: false,
37258     nocache: false,
37259     text: "Loading...",
37260     timeout: 30,
37261     scripts: false
37262 });
37263 </code></pre>
37264      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37265      * 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.
37266      * @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}
37267      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37268      * @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.
37269      * @return {Roo.ContentPanel} this
37270      */
37271     load : function(){
37272         var um = this.el.getUpdateManager();
37273         um.update.apply(um, arguments);
37274         return this;
37275     },
37276
37277
37278     /**
37279      * 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.
37280      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37281      * @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)
37282      * @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)
37283      * @return {Roo.UpdateManager} The UpdateManager
37284      */
37285     setUrl : function(url, params, loadOnce){
37286         if(this.refreshDelegate){
37287             this.removeListener("activate", this.refreshDelegate);
37288         }
37289         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37290         this.on("activate", this.refreshDelegate);
37291         return this.el.getUpdateManager();
37292     },
37293     
37294     _handleRefresh : function(url, params, loadOnce){
37295         if(!loadOnce || !this.loaded){
37296             var updater = this.el.getUpdateManager();
37297             updater.update(url, params, this._setLoaded.createDelegate(this));
37298         }
37299     },
37300     
37301     _setLoaded : function(){
37302         this.loaded = true;
37303     }, 
37304     
37305     /**
37306      * Returns this panel's id
37307      * @return {String} 
37308      */
37309     getId : function(){
37310         return this.el.id;
37311     },
37312     
37313     /** 
37314      * Returns this panel's element - used by regiosn to add.
37315      * @return {Roo.Element} 
37316      */
37317     getEl : function(){
37318         return this.wrapEl || this.el;
37319     },
37320     
37321    
37322     
37323     adjustForComponents : function(width, height)
37324     {
37325         //Roo.log('adjustForComponents ');
37326         if(this.resizeEl != this.el){
37327             width -= this.el.getFrameWidth('lr');
37328             height -= this.el.getFrameWidth('tb');
37329         }
37330         if(this.toolbar){
37331             var te = this.toolbar.getEl();
37332             te.setWidth(width);
37333             height -= te.getHeight();
37334         }
37335         if(this.footer){
37336             var te = this.footer.getEl();
37337             te.setWidth(width);
37338             height -= te.getHeight();
37339         }
37340         
37341         
37342         if(this.adjustments){
37343             width += this.adjustments[0];
37344             height += this.adjustments[1];
37345         }
37346         return {"width": width, "height": height};
37347     },
37348     
37349     setSize : function(width, height){
37350         if(this.fitToFrame && !this.ignoreResize(width, height)){
37351             if(this.fitContainer && this.resizeEl != this.el){
37352                 this.el.setSize(width, height);
37353             }
37354             var size = this.adjustForComponents(width, height);
37355             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37356             this.fireEvent('resize', this, size.width, size.height);
37357         }
37358     },
37359     
37360     /**
37361      * Returns this panel's title
37362      * @return {String} 
37363      */
37364     getTitle : function(){
37365         
37366         if (typeof(this.title) != 'object') {
37367             return this.title;
37368         }
37369         
37370         var t = '';
37371         for (var k in this.title) {
37372             if (!this.title.hasOwnProperty(k)) {
37373                 continue;
37374             }
37375             
37376             if (k.indexOf('-') >= 0) {
37377                 var s = k.split('-');
37378                 for (var i = 0; i<s.length; i++) {
37379                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37380                 }
37381             } else {
37382                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37383             }
37384         }
37385         return t;
37386     },
37387     
37388     /**
37389      * Set this panel's title
37390      * @param {String} title
37391      */
37392     setTitle : function(title){
37393         this.title = title;
37394         if(this.region){
37395             this.region.updatePanelTitle(this, title);
37396         }
37397     },
37398     
37399     /**
37400      * Returns true is this panel was configured to be closable
37401      * @return {Boolean} 
37402      */
37403     isClosable : function(){
37404         return this.closable;
37405     },
37406     
37407     beforeSlide : function(){
37408         this.el.clip();
37409         this.resizeEl.clip();
37410     },
37411     
37412     afterSlide : function(){
37413         this.el.unclip();
37414         this.resizeEl.unclip();
37415     },
37416     
37417     /**
37418      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37419      *   Will fail silently if the {@link #setUrl} method has not been called.
37420      *   This does not activate the panel, just updates its content.
37421      */
37422     refresh : function(){
37423         if(this.refreshDelegate){
37424            this.loaded = false;
37425            this.refreshDelegate();
37426         }
37427     },
37428     
37429     /**
37430      * Destroys this panel
37431      */
37432     destroy : function(){
37433         this.el.removeAllListeners();
37434         var tempEl = document.createElement("span");
37435         tempEl.appendChild(this.el.dom);
37436         tempEl.innerHTML = "";
37437         this.el.remove();
37438         this.el = null;
37439     },
37440     
37441     /**
37442      * form - if the content panel contains a form - this is a reference to it.
37443      * @type {Roo.form.Form}
37444      */
37445     form : false,
37446     /**
37447      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37448      *    This contains a reference to it.
37449      * @type {Roo.View}
37450      */
37451     view : false,
37452     
37453       /**
37454      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37455      * <pre><code>
37456
37457 layout.addxtype({
37458        xtype : 'Form',
37459        items: [ .... ]
37460    }
37461 );
37462
37463 </code></pre>
37464      * @param {Object} cfg Xtype definition of item to add.
37465      */
37466     
37467     
37468     getChildContainer: function () {
37469         return this.getEl();
37470     }
37471     
37472     
37473     /*
37474         var  ret = new Roo.factory(cfg);
37475         return ret;
37476         
37477         
37478         // add form..
37479         if (cfg.xtype.match(/^Form$/)) {
37480             
37481             var el;
37482             //if (this.footer) {
37483             //    el = this.footer.container.insertSibling(false, 'before');
37484             //} else {
37485                 el = this.el.createChild();
37486             //}
37487
37488             this.form = new  Roo.form.Form(cfg);
37489             
37490             
37491             if ( this.form.allItems.length) {
37492                 this.form.render(el.dom);
37493             }
37494             return this.form;
37495         }
37496         // should only have one of theses..
37497         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37498             // views.. should not be just added - used named prop 'view''
37499             
37500             cfg.el = this.el.appendChild(document.createElement("div"));
37501             // factory?
37502             
37503             var ret = new Roo.factory(cfg);
37504              
37505              ret.render && ret.render(false, ''); // render blank..
37506             this.view = ret;
37507             return ret;
37508         }
37509         return false;
37510     }
37511     \*/
37512 });
37513  
37514 /**
37515  * @class Roo.bootstrap.panel.Grid
37516  * @extends Roo.bootstrap.panel.Content
37517  * @constructor
37518  * Create a new GridPanel.
37519  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37520  * @param {Object} config A the config object
37521   
37522  */
37523
37524
37525
37526 Roo.bootstrap.panel.Grid = function(config)
37527 {
37528     
37529       
37530     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37531         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37532
37533     config.el = this.wrapper;
37534     //this.el = this.wrapper;
37535     
37536       if (config.container) {
37537         // ctor'ed from a Border/panel.grid
37538         
37539         
37540         this.wrapper.setStyle("overflow", "hidden");
37541         this.wrapper.addClass('roo-grid-container');
37542
37543     }
37544     
37545     
37546     if(config.toolbar){
37547         var tool_el = this.wrapper.createChild();    
37548         this.toolbar = Roo.factory(config.toolbar);
37549         var ti = [];
37550         if (config.toolbar.items) {
37551             ti = config.toolbar.items ;
37552             delete config.toolbar.items ;
37553         }
37554         
37555         var nitems = [];
37556         this.toolbar.render(tool_el);
37557         for(var i =0;i < ti.length;i++) {
37558           //  Roo.log(['add child', items[i]]);
37559             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37560         }
37561         this.toolbar.items = nitems;
37562         
37563         delete config.toolbar;
37564     }
37565     
37566     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37567     config.grid.scrollBody = true;;
37568     config.grid.monitorWindowResize = false; // turn off autosizing
37569     config.grid.autoHeight = false;
37570     config.grid.autoWidth = false;
37571     
37572     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37573     
37574     if (config.background) {
37575         // render grid on panel activation (if panel background)
37576         this.on('activate', function(gp) {
37577             if (!gp.grid.rendered) {
37578                 gp.grid.render(this.wrapper);
37579                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37580             }
37581         });
37582             
37583     } else {
37584         this.grid.render(this.wrapper);
37585         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37586
37587     }
37588     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37589     // ??? needed ??? config.el = this.wrapper;
37590     
37591     
37592     
37593   
37594     // xtype created footer. - not sure if will work as we normally have to render first..
37595     if (this.footer && !this.footer.el && this.footer.xtype) {
37596         
37597         var ctr = this.grid.getView().getFooterPanel(true);
37598         this.footer.dataSource = this.grid.dataSource;
37599         this.footer = Roo.factory(this.footer, Roo);
37600         this.footer.render(ctr);
37601         
37602     }
37603     
37604     
37605     
37606     
37607      
37608 };
37609
37610 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37611     getId : function(){
37612         return this.grid.id;
37613     },
37614     
37615     /**
37616      * Returns the grid for this panel
37617      * @return {Roo.bootstrap.Table} 
37618      */
37619     getGrid : function(){
37620         return this.grid;    
37621     },
37622     
37623     setSize : function(width, height){
37624         if(!this.ignoreResize(width, height)){
37625             var grid = this.grid;
37626             var size = this.adjustForComponents(width, height);
37627             var gridel = grid.getGridEl();
37628             gridel.setSize(size.width, size.height);
37629             /*
37630             var thd = grid.getGridEl().select('thead',true).first();
37631             var tbd = grid.getGridEl().select('tbody', true).first();
37632             if (tbd) {
37633                 tbd.setSize(width, height - thd.getHeight());
37634             }
37635             */
37636             grid.autoSize();
37637         }
37638     },
37639      
37640     
37641     
37642     beforeSlide : function(){
37643         this.grid.getView().scroller.clip();
37644     },
37645     
37646     afterSlide : function(){
37647         this.grid.getView().scroller.unclip();
37648     },
37649     
37650     destroy : function(){
37651         this.grid.destroy();
37652         delete this.grid;
37653         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37654     }
37655 });
37656
37657 /**
37658  * @class Roo.bootstrap.panel.Nest
37659  * @extends Roo.bootstrap.panel.Content
37660  * @constructor
37661  * Create a new Panel, that can contain a layout.Border.
37662  * 
37663  * 
37664  * @param {Roo.BorderLayout} layout The layout for this panel
37665  * @param {String/Object} config A string to set only the title or a config object
37666  */
37667 Roo.bootstrap.panel.Nest = function(config)
37668 {
37669     // construct with only one argument..
37670     /* FIXME - implement nicer consturctors
37671     if (layout.layout) {
37672         config = layout;
37673         layout = config.layout;
37674         delete config.layout;
37675     }
37676     if (layout.xtype && !layout.getEl) {
37677         // then layout needs constructing..
37678         layout = Roo.factory(layout, Roo);
37679     }
37680     */
37681     
37682     config.el =  config.layout.getEl();
37683     
37684     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37685     
37686     config.layout.monitorWindowResize = false; // turn off autosizing
37687     this.layout = config.layout;
37688     this.layout.getEl().addClass("roo-layout-nested-layout");
37689     
37690     
37691     
37692     
37693 };
37694
37695 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37696
37697     setSize : function(width, height){
37698         if(!this.ignoreResize(width, height)){
37699             var size = this.adjustForComponents(width, height);
37700             var el = this.layout.getEl();
37701             if (size.height < 1) {
37702                 el.setWidth(size.width);   
37703             } else {
37704                 el.setSize(size.width, size.height);
37705             }
37706             var touch = el.dom.offsetWidth;
37707             this.layout.layout();
37708             // ie requires a double layout on the first pass
37709             if(Roo.isIE && !this.initialized){
37710                 this.initialized = true;
37711                 this.layout.layout();
37712             }
37713         }
37714     },
37715     
37716     // activate all subpanels if not currently active..
37717     
37718     setActiveState : function(active){
37719         this.active = active;
37720         this.setActiveClass(active);
37721         
37722         if(!active){
37723             this.fireEvent("deactivate", this);
37724             return;
37725         }
37726         
37727         this.fireEvent("activate", this);
37728         // not sure if this should happen before or after..
37729         if (!this.layout) {
37730             return; // should not happen..
37731         }
37732         var reg = false;
37733         for (var r in this.layout.regions) {
37734             reg = this.layout.getRegion(r);
37735             if (reg.getActivePanel()) {
37736                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37737                 reg.setActivePanel(reg.getActivePanel());
37738                 continue;
37739             }
37740             if (!reg.panels.length) {
37741                 continue;
37742             }
37743             reg.showPanel(reg.getPanel(0));
37744         }
37745         
37746         
37747         
37748         
37749     },
37750     
37751     /**
37752      * Returns the nested BorderLayout for this panel
37753      * @return {Roo.BorderLayout} 
37754      */
37755     getLayout : function(){
37756         return this.layout;
37757     },
37758     
37759      /**
37760      * Adds a xtype elements to the layout of the nested panel
37761      * <pre><code>
37762
37763 panel.addxtype({
37764        xtype : 'ContentPanel',
37765        region: 'west',
37766        items: [ .... ]
37767    }
37768 );
37769
37770 panel.addxtype({
37771         xtype : 'NestedLayoutPanel',
37772         region: 'west',
37773         layout: {
37774            center: { },
37775            west: { }   
37776         },
37777         items : [ ... list of content panels or nested layout panels.. ]
37778    }
37779 );
37780 </code></pre>
37781      * @param {Object} cfg Xtype definition of item to add.
37782      */
37783     addxtype : function(cfg) {
37784         return this.layout.addxtype(cfg);
37785     
37786     }
37787 });        /*
37788  * Based on:
37789  * Ext JS Library 1.1.1
37790  * Copyright(c) 2006-2007, Ext JS, LLC.
37791  *
37792  * Originally Released Under LGPL - original licence link has changed is not relivant.
37793  *
37794  * Fork - LGPL
37795  * <script type="text/javascript">
37796  */
37797 /**
37798  * @class Roo.TabPanel
37799  * @extends Roo.util.Observable
37800  * A lightweight tab container.
37801  * <br><br>
37802  * Usage:
37803  * <pre><code>
37804 // basic tabs 1, built from existing content
37805 var tabs = new Roo.TabPanel("tabs1");
37806 tabs.addTab("script", "View Script");
37807 tabs.addTab("markup", "View Markup");
37808 tabs.activate("script");
37809
37810 // more advanced tabs, built from javascript
37811 var jtabs = new Roo.TabPanel("jtabs");
37812 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37813
37814 // set up the UpdateManager
37815 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37816 var updater = tab2.getUpdateManager();
37817 updater.setDefaultUrl("ajax1.htm");
37818 tab2.on('activate', updater.refresh, updater, true);
37819
37820 // Use setUrl for Ajax loading
37821 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37822 tab3.setUrl("ajax2.htm", null, true);
37823
37824 // Disabled tab
37825 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37826 tab4.disable();
37827
37828 jtabs.activate("jtabs-1");
37829  * </code></pre>
37830  * @constructor
37831  * Create a new TabPanel.
37832  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37833  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37834  */
37835 Roo.bootstrap.panel.Tabs = function(config){
37836     /**
37837     * The container element for this TabPanel.
37838     * @type Roo.Element
37839     */
37840     this.el = Roo.get(config.el);
37841     delete config.el;
37842     if(config){
37843         if(typeof config == "boolean"){
37844             this.tabPosition = config ? "bottom" : "top";
37845         }else{
37846             Roo.apply(this, config);
37847         }
37848     }
37849     
37850     if(this.tabPosition == "bottom"){
37851         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37852         this.el.addClass("roo-tabs-bottom");
37853     }
37854     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37855     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37856     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37857     if(Roo.isIE){
37858         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37859     }
37860     if(this.tabPosition != "bottom"){
37861         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37862          * @type Roo.Element
37863          */
37864         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37865         this.el.addClass("roo-tabs-top");
37866     }
37867     this.items = [];
37868
37869     this.bodyEl.setStyle("position", "relative");
37870
37871     this.active = null;
37872     this.activateDelegate = this.activate.createDelegate(this);
37873
37874     this.addEvents({
37875         /**
37876          * @event tabchange
37877          * Fires when the active tab changes
37878          * @param {Roo.TabPanel} this
37879          * @param {Roo.TabPanelItem} activePanel The new active tab
37880          */
37881         "tabchange": true,
37882         /**
37883          * @event beforetabchange
37884          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37885          * @param {Roo.TabPanel} this
37886          * @param {Object} e Set cancel to true on this object to cancel the tab change
37887          * @param {Roo.TabPanelItem} tab The tab being changed to
37888          */
37889         "beforetabchange" : true
37890     });
37891
37892     Roo.EventManager.onWindowResize(this.onResize, this);
37893     this.cpad = this.el.getPadding("lr");
37894     this.hiddenCount = 0;
37895
37896
37897     // toolbar on the tabbar support...
37898     if (this.toolbar) {
37899         alert("no toolbar support yet");
37900         this.toolbar  = false;
37901         /*
37902         var tcfg = this.toolbar;
37903         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37904         this.toolbar = new Roo.Toolbar(tcfg);
37905         if (Roo.isSafari) {
37906             var tbl = tcfg.container.child('table', true);
37907             tbl.setAttribute('width', '100%');
37908         }
37909         */
37910         
37911     }
37912    
37913
37914
37915     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37916 };
37917
37918 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37919     /*
37920      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37921      */
37922     tabPosition : "top",
37923     /*
37924      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37925      */
37926     currentTabWidth : 0,
37927     /*
37928      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37929      */
37930     minTabWidth : 40,
37931     /*
37932      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37933      */
37934     maxTabWidth : 250,
37935     /*
37936      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37937      */
37938     preferredTabWidth : 175,
37939     /*
37940      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37941      */
37942     resizeTabs : false,
37943     /*
37944      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37945      */
37946     monitorResize : true,
37947     /*
37948      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37949      */
37950     toolbar : false,
37951
37952     /**
37953      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37954      * @param {String} id The id of the div to use <b>or create</b>
37955      * @param {String} text The text for the tab
37956      * @param {String} content (optional) Content to put in the TabPanelItem body
37957      * @param {Boolean} closable (optional) True to create a close icon on the tab
37958      * @return {Roo.TabPanelItem} The created TabPanelItem
37959      */
37960     addTab : function(id, text, content, closable, tpl)
37961     {
37962         var item = new Roo.bootstrap.panel.TabItem({
37963             panel: this,
37964             id : id,
37965             text : text,
37966             closable : closable,
37967             tpl : tpl
37968         });
37969         this.addTabItem(item);
37970         if(content){
37971             item.setContent(content);
37972         }
37973         return item;
37974     },
37975
37976     /**
37977      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37978      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37979      * @return {Roo.TabPanelItem}
37980      */
37981     getTab : function(id){
37982         return this.items[id];
37983     },
37984
37985     /**
37986      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37987      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37988      */
37989     hideTab : function(id){
37990         var t = this.items[id];
37991         if(!t.isHidden()){
37992            t.setHidden(true);
37993            this.hiddenCount++;
37994            this.autoSizeTabs();
37995         }
37996     },
37997
37998     /**
37999      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38000      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38001      */
38002     unhideTab : function(id){
38003         var t = this.items[id];
38004         if(t.isHidden()){
38005            t.setHidden(false);
38006            this.hiddenCount--;
38007            this.autoSizeTabs();
38008         }
38009     },
38010
38011     /**
38012      * Adds an existing {@link Roo.TabPanelItem}.
38013      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38014      */
38015     addTabItem : function(item){
38016         this.items[item.id] = item;
38017         this.items.push(item);
38018       //  if(this.resizeTabs){
38019     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38020   //         this.autoSizeTabs();
38021 //        }else{
38022 //            item.autoSize();
38023        // }
38024     },
38025
38026     /**
38027      * Removes a {@link Roo.TabPanelItem}.
38028      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38029      */
38030     removeTab : function(id){
38031         var items = this.items;
38032         var tab = items[id];
38033         if(!tab) { return; }
38034         var index = items.indexOf(tab);
38035         if(this.active == tab && items.length > 1){
38036             var newTab = this.getNextAvailable(index);
38037             if(newTab) {
38038                 newTab.activate();
38039             }
38040         }
38041         this.stripEl.dom.removeChild(tab.pnode.dom);
38042         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38043             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38044         }
38045         items.splice(index, 1);
38046         delete this.items[tab.id];
38047         tab.fireEvent("close", tab);
38048         tab.purgeListeners();
38049         this.autoSizeTabs();
38050     },
38051
38052     getNextAvailable : function(start){
38053         var items = this.items;
38054         var index = start;
38055         // look for a next tab that will slide over to
38056         // replace the one being removed
38057         while(index < items.length){
38058             var item = items[++index];
38059             if(item && !item.isHidden()){
38060                 return item;
38061             }
38062         }
38063         // if one isn't found select the previous tab (on the left)
38064         index = start;
38065         while(index >= 0){
38066             var item = items[--index];
38067             if(item && !item.isHidden()){
38068                 return item;
38069             }
38070         }
38071         return null;
38072     },
38073
38074     /**
38075      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38076      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38077      */
38078     disableTab : function(id){
38079         var tab = this.items[id];
38080         if(tab && this.active != tab){
38081             tab.disable();
38082         }
38083     },
38084
38085     /**
38086      * Enables a {@link Roo.TabPanelItem} that is disabled.
38087      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38088      */
38089     enableTab : function(id){
38090         var tab = this.items[id];
38091         tab.enable();
38092     },
38093
38094     /**
38095      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38096      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38097      * @return {Roo.TabPanelItem} The TabPanelItem.
38098      */
38099     activate : function(id){
38100         var tab = this.items[id];
38101         if(!tab){
38102             return null;
38103         }
38104         if(tab == this.active || tab.disabled){
38105             return tab;
38106         }
38107         var e = {};
38108         this.fireEvent("beforetabchange", this, e, tab);
38109         if(e.cancel !== true && !tab.disabled){
38110             if(this.active){
38111                 this.active.hide();
38112             }
38113             this.active = this.items[id];
38114             this.active.show();
38115             this.fireEvent("tabchange", this, this.active);
38116         }
38117         return tab;
38118     },
38119
38120     /**
38121      * Gets the active {@link Roo.TabPanelItem}.
38122      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38123      */
38124     getActiveTab : function(){
38125         return this.active;
38126     },
38127
38128     /**
38129      * Updates the tab body element to fit the height of the container element
38130      * for overflow scrolling
38131      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38132      */
38133     syncHeight : function(targetHeight){
38134         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38135         var bm = this.bodyEl.getMargins();
38136         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38137         this.bodyEl.setHeight(newHeight);
38138         return newHeight;
38139     },
38140
38141     onResize : function(){
38142         if(this.monitorResize){
38143             this.autoSizeTabs();
38144         }
38145     },
38146
38147     /**
38148      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38149      */
38150     beginUpdate : function(){
38151         this.updating = true;
38152     },
38153
38154     /**
38155      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38156      */
38157     endUpdate : function(){
38158         this.updating = false;
38159         this.autoSizeTabs();
38160     },
38161
38162     /**
38163      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38164      */
38165     autoSizeTabs : function(){
38166         var count = this.items.length;
38167         var vcount = count - this.hiddenCount;
38168         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38169             return;
38170         }
38171         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38172         var availWidth = Math.floor(w / vcount);
38173         var b = this.stripBody;
38174         if(b.getWidth() > w){
38175             var tabs = this.items;
38176             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38177             if(availWidth < this.minTabWidth){
38178                 /*if(!this.sleft){    // incomplete scrolling code
38179                     this.createScrollButtons();
38180                 }
38181                 this.showScroll();
38182                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38183             }
38184         }else{
38185             if(this.currentTabWidth < this.preferredTabWidth){
38186                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38187             }
38188         }
38189     },
38190
38191     /**
38192      * Returns the number of tabs in this TabPanel.
38193      * @return {Number}
38194      */
38195      getCount : function(){
38196          return this.items.length;
38197      },
38198
38199     /**
38200      * Resizes all the tabs to the passed width
38201      * @param {Number} The new width
38202      */
38203     setTabWidth : function(width){
38204         this.currentTabWidth = width;
38205         for(var i = 0, len = this.items.length; i < len; i++) {
38206                 if(!this.items[i].isHidden()) {
38207                 this.items[i].setWidth(width);
38208             }
38209         }
38210     },
38211
38212     /**
38213      * Destroys this TabPanel
38214      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38215      */
38216     destroy : function(removeEl){
38217         Roo.EventManager.removeResizeListener(this.onResize, this);
38218         for(var i = 0, len = this.items.length; i < len; i++){
38219             this.items[i].purgeListeners();
38220         }
38221         if(removeEl === true){
38222             this.el.update("");
38223             this.el.remove();
38224         }
38225     },
38226     
38227     createStrip : function(container)
38228     {
38229         var strip = document.createElement("nav");
38230         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38231         container.appendChild(strip);
38232         return strip;
38233     },
38234     
38235     createStripList : function(strip)
38236     {
38237         // div wrapper for retard IE
38238         // returns the "tr" element.
38239         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38240         //'<div class="x-tabs-strip-wrap">'+
38241           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38242           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38243         return strip.firstChild; //.firstChild.firstChild.firstChild;
38244     },
38245     createBody : function(container)
38246     {
38247         var body = document.createElement("div");
38248         Roo.id(body, "tab-body");
38249         //Roo.fly(body).addClass("x-tabs-body");
38250         Roo.fly(body).addClass("tab-content");
38251         container.appendChild(body);
38252         return body;
38253     },
38254     createItemBody :function(bodyEl, id){
38255         var body = Roo.getDom(id);
38256         if(!body){
38257             body = document.createElement("div");
38258             body.id = id;
38259         }
38260         //Roo.fly(body).addClass("x-tabs-item-body");
38261         Roo.fly(body).addClass("tab-pane");
38262          bodyEl.insertBefore(body, bodyEl.firstChild);
38263         return body;
38264     },
38265     /** @private */
38266     createStripElements :  function(stripEl, text, closable, tpl)
38267     {
38268         var td = document.createElement("li"); // was td..
38269         
38270         
38271         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38272         
38273         
38274         stripEl.appendChild(td);
38275         /*if(closable){
38276             td.className = "x-tabs-closable";
38277             if(!this.closeTpl){
38278                 this.closeTpl = new Roo.Template(
38279                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38280                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38281                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38282                 );
38283             }
38284             var el = this.closeTpl.overwrite(td, {"text": text});
38285             var close = el.getElementsByTagName("div")[0];
38286             var inner = el.getElementsByTagName("em")[0];
38287             return {"el": el, "close": close, "inner": inner};
38288         } else {
38289         */
38290         // not sure what this is..
38291 //            if(!this.tabTpl){
38292                 //this.tabTpl = new Roo.Template(
38293                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38294                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38295                 //);
38296 //                this.tabTpl = new Roo.Template(
38297 //                   '<a href="#">' +
38298 //                   '<span unselectable="on"' +
38299 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38300 //                            ' >{text}</span></a>'
38301 //                );
38302 //                
38303 //            }
38304
38305
38306             var template = tpl || this.tabTpl || false;
38307             
38308             if(!template){
38309                 
38310                 template = new Roo.Template(
38311                    '<a href="#">' +
38312                    '<span unselectable="on"' +
38313                             (this.disableTooltips ? '' : ' title="{text}"') +
38314                             ' >{text}</span></a>'
38315                 );
38316             }
38317             
38318             switch (typeof(template)) {
38319                 case 'object' :
38320                     break;
38321                 case 'string' :
38322                     template = new Roo.Template(template);
38323                     break;
38324                 default :
38325                     break;
38326             }
38327             
38328             var el = template.overwrite(td, {"text": text});
38329             
38330             var inner = el.getElementsByTagName("span")[0];
38331             
38332             return {"el": el, "inner": inner};
38333             
38334     }
38335         
38336     
38337 });
38338
38339 /**
38340  * @class Roo.TabPanelItem
38341  * @extends Roo.util.Observable
38342  * Represents an individual item (tab plus body) in a TabPanel.
38343  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38344  * @param {String} id The id of this TabPanelItem
38345  * @param {String} text The text for the tab of this TabPanelItem
38346  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38347  */
38348 Roo.bootstrap.panel.TabItem = function(config){
38349     /**
38350      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38351      * @type Roo.TabPanel
38352      */
38353     this.tabPanel = config.panel;
38354     /**
38355      * The id for this TabPanelItem
38356      * @type String
38357      */
38358     this.id = config.id;
38359     /** @private */
38360     this.disabled = false;
38361     /** @private */
38362     this.text = config.text;
38363     /** @private */
38364     this.loaded = false;
38365     this.closable = config.closable;
38366
38367     /**
38368      * The body element for this TabPanelItem.
38369      * @type Roo.Element
38370      */
38371     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38372     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38373     this.bodyEl.setStyle("display", "block");
38374     this.bodyEl.setStyle("zoom", "1");
38375     //this.hideAction();
38376
38377     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38378     /** @private */
38379     this.el = Roo.get(els.el);
38380     this.inner = Roo.get(els.inner, true);
38381     this.textEl = Roo.get(this.el.dom.firstChild, true);
38382     this.pnode = Roo.get(els.el.parentNode, true);
38383 //    this.el.on("mousedown", this.onTabMouseDown, this);
38384     this.el.on("click", this.onTabClick, this);
38385     /** @private */
38386     if(config.closable){
38387         var c = Roo.get(els.close, true);
38388         c.dom.title = this.closeText;
38389         c.addClassOnOver("close-over");
38390         c.on("click", this.closeClick, this);
38391      }
38392
38393     this.addEvents({
38394          /**
38395          * @event activate
38396          * Fires when this tab becomes the active tab.
38397          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38398          * @param {Roo.TabPanelItem} this
38399          */
38400         "activate": true,
38401         /**
38402          * @event beforeclose
38403          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38404          * @param {Roo.TabPanelItem} this
38405          * @param {Object} e Set cancel to true on this object to cancel the close.
38406          */
38407         "beforeclose": true,
38408         /**
38409          * @event close
38410          * Fires when this tab is closed.
38411          * @param {Roo.TabPanelItem} this
38412          */
38413          "close": true,
38414         /**
38415          * @event deactivate
38416          * Fires when this tab is no longer the active tab.
38417          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38418          * @param {Roo.TabPanelItem} this
38419          */
38420          "deactivate" : true
38421     });
38422     this.hidden = false;
38423
38424     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38425 };
38426
38427 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38428            {
38429     purgeListeners : function(){
38430        Roo.util.Observable.prototype.purgeListeners.call(this);
38431        this.el.removeAllListeners();
38432     },
38433     /**
38434      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38435      */
38436     show : function(){
38437         this.pnode.addClass("active");
38438         this.showAction();
38439         if(Roo.isOpera){
38440             this.tabPanel.stripWrap.repaint();
38441         }
38442         this.fireEvent("activate", this.tabPanel, this);
38443     },
38444
38445     /**
38446      * Returns true if this tab is the active tab.
38447      * @return {Boolean}
38448      */
38449     isActive : function(){
38450         return this.tabPanel.getActiveTab() == this;
38451     },
38452
38453     /**
38454      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38455      */
38456     hide : function(){
38457         this.pnode.removeClass("active");
38458         this.hideAction();
38459         this.fireEvent("deactivate", this.tabPanel, this);
38460     },
38461
38462     hideAction : function(){
38463         this.bodyEl.hide();
38464         this.bodyEl.setStyle("position", "absolute");
38465         this.bodyEl.setLeft("-20000px");
38466         this.bodyEl.setTop("-20000px");
38467     },
38468
38469     showAction : function(){
38470         this.bodyEl.setStyle("position", "relative");
38471         this.bodyEl.setTop("");
38472         this.bodyEl.setLeft("");
38473         this.bodyEl.show();
38474     },
38475
38476     /**
38477      * Set the tooltip for the tab.
38478      * @param {String} tooltip The tab's tooltip
38479      */
38480     setTooltip : function(text){
38481         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38482             this.textEl.dom.qtip = text;
38483             this.textEl.dom.removeAttribute('title');
38484         }else{
38485             this.textEl.dom.title = text;
38486         }
38487     },
38488
38489     onTabClick : function(e){
38490         e.preventDefault();
38491         this.tabPanel.activate(this.id);
38492     },
38493
38494     onTabMouseDown : function(e){
38495         e.preventDefault();
38496         this.tabPanel.activate(this.id);
38497     },
38498 /*
38499     getWidth : function(){
38500         return this.inner.getWidth();
38501     },
38502
38503     setWidth : function(width){
38504         var iwidth = width - this.pnode.getPadding("lr");
38505         this.inner.setWidth(iwidth);
38506         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38507         this.pnode.setWidth(width);
38508     },
38509 */
38510     /**
38511      * Show or hide the tab
38512      * @param {Boolean} hidden True to hide or false to show.
38513      */
38514     setHidden : function(hidden){
38515         this.hidden = hidden;
38516         this.pnode.setStyle("display", hidden ? "none" : "");
38517     },
38518
38519     /**
38520      * Returns true if this tab is "hidden"
38521      * @return {Boolean}
38522      */
38523     isHidden : function(){
38524         return this.hidden;
38525     },
38526
38527     /**
38528      * Returns the text for this tab
38529      * @return {String}
38530      */
38531     getText : function(){
38532         return this.text;
38533     },
38534     /*
38535     autoSize : function(){
38536         //this.el.beginMeasure();
38537         this.textEl.setWidth(1);
38538         /*
38539          *  #2804 [new] Tabs in Roojs
38540          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38541          */
38542         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38543         //this.el.endMeasure();
38544     //},
38545
38546     /**
38547      * Sets the text for the tab (Note: this also sets the tooltip text)
38548      * @param {String} text The tab's text and tooltip
38549      */
38550     setText : function(text){
38551         this.text = text;
38552         this.textEl.update(text);
38553         this.setTooltip(text);
38554         //if(!this.tabPanel.resizeTabs){
38555         //    this.autoSize();
38556         //}
38557     },
38558     /**
38559      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38560      */
38561     activate : function(){
38562         this.tabPanel.activate(this.id);
38563     },
38564
38565     /**
38566      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38567      */
38568     disable : function(){
38569         if(this.tabPanel.active != this){
38570             this.disabled = true;
38571             this.pnode.addClass("disabled");
38572         }
38573     },
38574
38575     /**
38576      * Enables this TabPanelItem if it was previously disabled.
38577      */
38578     enable : function(){
38579         this.disabled = false;
38580         this.pnode.removeClass("disabled");
38581     },
38582
38583     /**
38584      * Sets the content for this TabPanelItem.
38585      * @param {String} content The content
38586      * @param {Boolean} loadScripts true to look for and load scripts
38587      */
38588     setContent : function(content, loadScripts){
38589         this.bodyEl.update(content, loadScripts);
38590     },
38591
38592     /**
38593      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38594      * @return {Roo.UpdateManager} The UpdateManager
38595      */
38596     getUpdateManager : function(){
38597         return this.bodyEl.getUpdateManager();
38598     },
38599
38600     /**
38601      * Set a URL to be used to load the content for this TabPanelItem.
38602      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38603      * @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)
38604      * @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)
38605      * @return {Roo.UpdateManager} The UpdateManager
38606      */
38607     setUrl : function(url, params, loadOnce){
38608         if(this.refreshDelegate){
38609             this.un('activate', this.refreshDelegate);
38610         }
38611         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38612         this.on("activate", this.refreshDelegate);
38613         return this.bodyEl.getUpdateManager();
38614     },
38615
38616     /** @private */
38617     _handleRefresh : function(url, params, loadOnce){
38618         if(!loadOnce || !this.loaded){
38619             var updater = this.bodyEl.getUpdateManager();
38620             updater.update(url, params, this._setLoaded.createDelegate(this));
38621         }
38622     },
38623
38624     /**
38625      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38626      *   Will fail silently if the setUrl method has not been called.
38627      *   This does not activate the panel, just updates its content.
38628      */
38629     refresh : function(){
38630         if(this.refreshDelegate){
38631            this.loaded = false;
38632            this.refreshDelegate();
38633         }
38634     },
38635
38636     /** @private */
38637     _setLoaded : function(){
38638         this.loaded = true;
38639     },
38640
38641     /** @private */
38642     closeClick : function(e){
38643         var o = {};
38644         e.stopEvent();
38645         this.fireEvent("beforeclose", this, o);
38646         if(o.cancel !== true){
38647             this.tabPanel.removeTab(this.id);
38648         }
38649     },
38650     /**
38651      * The text displayed in the tooltip for the close icon.
38652      * @type String
38653      */
38654     closeText : "Close this tab"
38655 });
38656 /**
38657 *    This script refer to:
38658 *    Title: International Telephone Input
38659 *    Author: Jack O'Connor
38660 *    Code version:  v12.1.12
38661 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38662 **/
38663
38664 Roo.bootstrap.PhoneInputData = function() {
38665     var d = [
38666       [
38667         "Afghanistan (‫افغانستان‬‎)",
38668         "af",
38669         "93"
38670       ],
38671       [
38672         "Albania (Shqipëri)",
38673         "al",
38674         "355"
38675       ],
38676       [
38677         "Algeria (‫الجزائر‬‎)",
38678         "dz",
38679         "213"
38680       ],
38681       [
38682         "American Samoa",
38683         "as",
38684         "1684"
38685       ],
38686       [
38687         "Andorra",
38688         "ad",
38689         "376"
38690       ],
38691       [
38692         "Angola",
38693         "ao",
38694         "244"
38695       ],
38696       [
38697         "Anguilla",
38698         "ai",
38699         "1264"
38700       ],
38701       [
38702         "Antigua and Barbuda",
38703         "ag",
38704         "1268"
38705       ],
38706       [
38707         "Argentina",
38708         "ar",
38709         "54"
38710       ],
38711       [
38712         "Armenia (Հայաստան)",
38713         "am",
38714         "374"
38715       ],
38716       [
38717         "Aruba",
38718         "aw",
38719         "297"
38720       ],
38721       [
38722         "Australia",
38723         "au",
38724         "61",
38725         0
38726       ],
38727       [
38728         "Austria (Österreich)",
38729         "at",
38730         "43"
38731       ],
38732       [
38733         "Azerbaijan (Azərbaycan)",
38734         "az",
38735         "994"
38736       ],
38737       [
38738         "Bahamas",
38739         "bs",
38740         "1242"
38741       ],
38742       [
38743         "Bahrain (‫البحرين‬‎)",
38744         "bh",
38745         "973"
38746       ],
38747       [
38748         "Bangladesh (বাংলাদেশ)",
38749         "bd",
38750         "880"
38751       ],
38752       [
38753         "Barbados",
38754         "bb",
38755         "1246"
38756       ],
38757       [
38758         "Belarus (Беларусь)",
38759         "by",
38760         "375"
38761       ],
38762       [
38763         "Belgium (België)",
38764         "be",
38765         "32"
38766       ],
38767       [
38768         "Belize",
38769         "bz",
38770         "501"
38771       ],
38772       [
38773         "Benin (Bénin)",
38774         "bj",
38775         "229"
38776       ],
38777       [
38778         "Bermuda",
38779         "bm",
38780         "1441"
38781       ],
38782       [
38783         "Bhutan (འབྲུག)",
38784         "bt",
38785         "975"
38786       ],
38787       [
38788         "Bolivia",
38789         "bo",
38790         "591"
38791       ],
38792       [
38793         "Bosnia and Herzegovina (Босна и Херцеговина)",
38794         "ba",
38795         "387"
38796       ],
38797       [
38798         "Botswana",
38799         "bw",
38800         "267"
38801       ],
38802       [
38803         "Brazil (Brasil)",
38804         "br",
38805         "55"
38806       ],
38807       [
38808         "British Indian Ocean Territory",
38809         "io",
38810         "246"
38811       ],
38812       [
38813         "British Virgin Islands",
38814         "vg",
38815         "1284"
38816       ],
38817       [
38818         "Brunei",
38819         "bn",
38820         "673"
38821       ],
38822       [
38823         "Bulgaria (България)",
38824         "bg",
38825         "359"
38826       ],
38827       [
38828         "Burkina Faso",
38829         "bf",
38830         "226"
38831       ],
38832       [
38833         "Burundi (Uburundi)",
38834         "bi",
38835         "257"
38836       ],
38837       [
38838         "Cambodia (កម្ពុជា)",
38839         "kh",
38840         "855"
38841       ],
38842       [
38843         "Cameroon (Cameroun)",
38844         "cm",
38845         "237"
38846       ],
38847       [
38848         "Canada",
38849         "ca",
38850         "1",
38851         1,
38852         ["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"]
38853       ],
38854       [
38855         "Cape Verde (Kabu Verdi)",
38856         "cv",
38857         "238"
38858       ],
38859       [
38860         "Caribbean Netherlands",
38861         "bq",
38862         "599",
38863         1
38864       ],
38865       [
38866         "Cayman Islands",
38867         "ky",
38868         "1345"
38869       ],
38870       [
38871         "Central African Republic (République centrafricaine)",
38872         "cf",
38873         "236"
38874       ],
38875       [
38876         "Chad (Tchad)",
38877         "td",
38878         "235"
38879       ],
38880       [
38881         "Chile",
38882         "cl",
38883         "56"
38884       ],
38885       [
38886         "China (中国)",
38887         "cn",
38888         "86"
38889       ],
38890       [
38891         "Christmas Island",
38892         "cx",
38893         "61",
38894         2
38895       ],
38896       [
38897         "Cocos (Keeling) Islands",
38898         "cc",
38899         "61",
38900         1
38901       ],
38902       [
38903         "Colombia",
38904         "co",
38905         "57"
38906       ],
38907       [
38908         "Comoros (‫جزر القمر‬‎)",
38909         "km",
38910         "269"
38911       ],
38912       [
38913         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38914         "cd",
38915         "243"
38916       ],
38917       [
38918         "Congo (Republic) (Congo-Brazzaville)",
38919         "cg",
38920         "242"
38921       ],
38922       [
38923         "Cook Islands",
38924         "ck",
38925         "682"
38926       ],
38927       [
38928         "Costa Rica",
38929         "cr",
38930         "506"
38931       ],
38932       [
38933         "Côte d’Ivoire",
38934         "ci",
38935         "225"
38936       ],
38937       [
38938         "Croatia (Hrvatska)",
38939         "hr",
38940         "385"
38941       ],
38942       [
38943         "Cuba",
38944         "cu",
38945         "53"
38946       ],
38947       [
38948         "Curaçao",
38949         "cw",
38950         "599",
38951         0
38952       ],
38953       [
38954         "Cyprus (Κύπρος)",
38955         "cy",
38956         "357"
38957       ],
38958       [
38959         "Czech Republic (Česká republika)",
38960         "cz",
38961         "420"
38962       ],
38963       [
38964         "Denmark (Danmark)",
38965         "dk",
38966         "45"
38967       ],
38968       [
38969         "Djibouti",
38970         "dj",
38971         "253"
38972       ],
38973       [
38974         "Dominica",
38975         "dm",
38976         "1767"
38977       ],
38978       [
38979         "Dominican Republic (República Dominicana)",
38980         "do",
38981         "1",
38982         2,
38983         ["809", "829", "849"]
38984       ],
38985       [
38986         "Ecuador",
38987         "ec",
38988         "593"
38989       ],
38990       [
38991         "Egypt (‫مصر‬‎)",
38992         "eg",
38993         "20"
38994       ],
38995       [
38996         "El Salvador",
38997         "sv",
38998         "503"
38999       ],
39000       [
39001         "Equatorial Guinea (Guinea Ecuatorial)",
39002         "gq",
39003         "240"
39004       ],
39005       [
39006         "Eritrea",
39007         "er",
39008         "291"
39009       ],
39010       [
39011         "Estonia (Eesti)",
39012         "ee",
39013         "372"
39014       ],
39015       [
39016         "Ethiopia",
39017         "et",
39018         "251"
39019       ],
39020       [
39021         "Falkland Islands (Islas Malvinas)",
39022         "fk",
39023         "500"
39024       ],
39025       [
39026         "Faroe Islands (Føroyar)",
39027         "fo",
39028         "298"
39029       ],
39030       [
39031         "Fiji",
39032         "fj",
39033         "679"
39034       ],
39035       [
39036         "Finland (Suomi)",
39037         "fi",
39038         "358",
39039         0
39040       ],
39041       [
39042         "France",
39043         "fr",
39044         "33"
39045       ],
39046       [
39047         "French Guiana (Guyane française)",
39048         "gf",
39049         "594"
39050       ],
39051       [
39052         "French Polynesia (Polynésie française)",
39053         "pf",
39054         "689"
39055       ],
39056       [
39057         "Gabon",
39058         "ga",
39059         "241"
39060       ],
39061       [
39062         "Gambia",
39063         "gm",
39064         "220"
39065       ],
39066       [
39067         "Georgia (საქართველო)",
39068         "ge",
39069         "995"
39070       ],
39071       [
39072         "Germany (Deutschland)",
39073         "de",
39074         "49"
39075       ],
39076       [
39077         "Ghana (Gaana)",
39078         "gh",
39079         "233"
39080       ],
39081       [
39082         "Gibraltar",
39083         "gi",
39084         "350"
39085       ],
39086       [
39087         "Greece (Ελλάδα)",
39088         "gr",
39089         "30"
39090       ],
39091       [
39092         "Greenland (Kalaallit Nunaat)",
39093         "gl",
39094         "299"
39095       ],
39096       [
39097         "Grenada",
39098         "gd",
39099         "1473"
39100       ],
39101       [
39102         "Guadeloupe",
39103         "gp",
39104         "590",
39105         0
39106       ],
39107       [
39108         "Guam",
39109         "gu",
39110         "1671"
39111       ],
39112       [
39113         "Guatemala",
39114         "gt",
39115         "502"
39116       ],
39117       [
39118         "Guernsey",
39119         "gg",
39120         "44",
39121         1
39122       ],
39123       [
39124         "Guinea (Guinée)",
39125         "gn",
39126         "224"
39127       ],
39128       [
39129         "Guinea-Bissau (Guiné Bissau)",
39130         "gw",
39131         "245"
39132       ],
39133       [
39134         "Guyana",
39135         "gy",
39136         "592"
39137       ],
39138       [
39139         "Haiti",
39140         "ht",
39141         "509"
39142       ],
39143       [
39144         "Honduras",
39145         "hn",
39146         "504"
39147       ],
39148       [
39149         "Hong Kong (香港)",
39150         "hk",
39151         "852"
39152       ],
39153       [
39154         "Hungary (Magyarország)",
39155         "hu",
39156         "36"
39157       ],
39158       [
39159         "Iceland (Ísland)",
39160         "is",
39161         "354"
39162       ],
39163       [
39164         "India (भारत)",
39165         "in",
39166         "91"
39167       ],
39168       [
39169         "Indonesia",
39170         "id",
39171         "62"
39172       ],
39173       [
39174         "Iran (‫ایران‬‎)",
39175         "ir",
39176         "98"
39177       ],
39178       [
39179         "Iraq (‫العراق‬‎)",
39180         "iq",
39181         "964"
39182       ],
39183       [
39184         "Ireland",
39185         "ie",
39186         "353"
39187       ],
39188       [
39189         "Isle of Man",
39190         "im",
39191         "44",
39192         2
39193       ],
39194       [
39195         "Israel (‫ישראל‬‎)",
39196         "il",
39197         "972"
39198       ],
39199       [
39200         "Italy (Italia)",
39201         "it",
39202         "39",
39203         0
39204       ],
39205       [
39206         "Jamaica",
39207         "jm",
39208         "1876"
39209       ],
39210       [
39211         "Japan (日本)",
39212         "jp",
39213         "81"
39214       ],
39215       [
39216         "Jersey",
39217         "je",
39218         "44",
39219         3
39220       ],
39221       [
39222         "Jordan (‫الأردن‬‎)",
39223         "jo",
39224         "962"
39225       ],
39226       [
39227         "Kazakhstan (Казахстан)",
39228         "kz",
39229         "7",
39230         1
39231       ],
39232       [
39233         "Kenya",
39234         "ke",
39235         "254"
39236       ],
39237       [
39238         "Kiribati",
39239         "ki",
39240         "686"
39241       ],
39242       [
39243         "Kosovo",
39244         "xk",
39245         "383"
39246       ],
39247       [
39248         "Kuwait (‫الكويت‬‎)",
39249         "kw",
39250         "965"
39251       ],
39252       [
39253         "Kyrgyzstan (Кыргызстан)",
39254         "kg",
39255         "996"
39256       ],
39257       [
39258         "Laos (ລາວ)",
39259         "la",
39260         "856"
39261       ],
39262       [
39263         "Latvia (Latvija)",
39264         "lv",
39265         "371"
39266       ],
39267       [
39268         "Lebanon (‫لبنان‬‎)",
39269         "lb",
39270         "961"
39271       ],
39272       [
39273         "Lesotho",
39274         "ls",
39275         "266"
39276       ],
39277       [
39278         "Liberia",
39279         "lr",
39280         "231"
39281       ],
39282       [
39283         "Libya (‫ليبيا‬‎)",
39284         "ly",
39285         "218"
39286       ],
39287       [
39288         "Liechtenstein",
39289         "li",
39290         "423"
39291       ],
39292       [
39293         "Lithuania (Lietuva)",
39294         "lt",
39295         "370"
39296       ],
39297       [
39298         "Luxembourg",
39299         "lu",
39300         "352"
39301       ],
39302       [
39303         "Macau (澳門)",
39304         "mo",
39305         "853"
39306       ],
39307       [
39308         "Macedonia (FYROM) (Македонија)",
39309         "mk",
39310         "389"
39311       ],
39312       [
39313         "Madagascar (Madagasikara)",
39314         "mg",
39315         "261"
39316       ],
39317       [
39318         "Malawi",
39319         "mw",
39320         "265"
39321       ],
39322       [
39323         "Malaysia",
39324         "my",
39325         "60"
39326       ],
39327       [
39328         "Maldives",
39329         "mv",
39330         "960"
39331       ],
39332       [
39333         "Mali",
39334         "ml",
39335         "223"
39336       ],
39337       [
39338         "Malta",
39339         "mt",
39340         "356"
39341       ],
39342       [
39343         "Marshall Islands",
39344         "mh",
39345         "692"
39346       ],
39347       [
39348         "Martinique",
39349         "mq",
39350         "596"
39351       ],
39352       [
39353         "Mauritania (‫موريتانيا‬‎)",
39354         "mr",
39355         "222"
39356       ],
39357       [
39358         "Mauritius (Moris)",
39359         "mu",
39360         "230"
39361       ],
39362       [
39363         "Mayotte",
39364         "yt",
39365         "262",
39366         1
39367       ],
39368       [
39369         "Mexico (México)",
39370         "mx",
39371         "52"
39372       ],
39373       [
39374         "Micronesia",
39375         "fm",
39376         "691"
39377       ],
39378       [
39379         "Moldova (Republica Moldova)",
39380         "md",
39381         "373"
39382       ],
39383       [
39384         "Monaco",
39385         "mc",
39386         "377"
39387       ],
39388       [
39389         "Mongolia (Монгол)",
39390         "mn",
39391         "976"
39392       ],
39393       [
39394         "Montenegro (Crna Gora)",
39395         "me",
39396         "382"
39397       ],
39398       [
39399         "Montserrat",
39400         "ms",
39401         "1664"
39402       ],
39403       [
39404         "Morocco (‫المغرب‬‎)",
39405         "ma",
39406         "212",
39407         0
39408       ],
39409       [
39410         "Mozambique (Moçambique)",
39411         "mz",
39412         "258"
39413       ],
39414       [
39415         "Myanmar (Burma) (မြန်မာ)",
39416         "mm",
39417         "95"
39418       ],
39419       [
39420         "Namibia (Namibië)",
39421         "na",
39422         "264"
39423       ],
39424       [
39425         "Nauru",
39426         "nr",
39427         "674"
39428       ],
39429       [
39430         "Nepal (नेपाल)",
39431         "np",
39432         "977"
39433       ],
39434       [
39435         "Netherlands (Nederland)",
39436         "nl",
39437         "31"
39438       ],
39439       [
39440         "New Caledonia (Nouvelle-Calédonie)",
39441         "nc",
39442         "687"
39443       ],
39444       [
39445         "New Zealand",
39446         "nz",
39447         "64"
39448       ],
39449       [
39450         "Nicaragua",
39451         "ni",
39452         "505"
39453       ],
39454       [
39455         "Niger (Nijar)",
39456         "ne",
39457         "227"
39458       ],
39459       [
39460         "Nigeria",
39461         "ng",
39462         "234"
39463       ],
39464       [
39465         "Niue",
39466         "nu",
39467         "683"
39468       ],
39469       [
39470         "Norfolk Island",
39471         "nf",
39472         "672"
39473       ],
39474       [
39475         "North Korea (조선 민주주의 인민 공화국)",
39476         "kp",
39477         "850"
39478       ],
39479       [
39480         "Northern Mariana Islands",
39481         "mp",
39482         "1670"
39483       ],
39484       [
39485         "Norway (Norge)",
39486         "no",
39487         "47",
39488         0
39489       ],
39490       [
39491         "Oman (‫عُمان‬‎)",
39492         "om",
39493         "968"
39494       ],
39495       [
39496         "Pakistan (‫پاکستان‬‎)",
39497         "pk",
39498         "92"
39499       ],
39500       [
39501         "Palau",
39502         "pw",
39503         "680"
39504       ],
39505       [
39506         "Palestine (‫فلسطين‬‎)",
39507         "ps",
39508         "970"
39509       ],
39510       [
39511         "Panama (Panamá)",
39512         "pa",
39513         "507"
39514       ],
39515       [
39516         "Papua New Guinea",
39517         "pg",
39518         "675"
39519       ],
39520       [
39521         "Paraguay",
39522         "py",
39523         "595"
39524       ],
39525       [
39526         "Peru (Perú)",
39527         "pe",
39528         "51"
39529       ],
39530       [
39531         "Philippines",
39532         "ph",
39533         "63"
39534       ],
39535       [
39536         "Poland (Polska)",
39537         "pl",
39538         "48"
39539       ],
39540       [
39541         "Portugal",
39542         "pt",
39543         "351"
39544       ],
39545       [
39546         "Puerto Rico",
39547         "pr",
39548         "1",
39549         3,
39550         ["787", "939"]
39551       ],
39552       [
39553         "Qatar (‫قطر‬‎)",
39554         "qa",
39555         "974"
39556       ],
39557       [
39558         "Réunion (La Réunion)",
39559         "re",
39560         "262",
39561         0
39562       ],
39563       [
39564         "Romania (România)",
39565         "ro",
39566         "40"
39567       ],
39568       [
39569         "Russia (Россия)",
39570         "ru",
39571         "7",
39572         0
39573       ],
39574       [
39575         "Rwanda",
39576         "rw",
39577         "250"
39578       ],
39579       [
39580         "Saint Barthélemy",
39581         "bl",
39582         "590",
39583         1
39584       ],
39585       [
39586         "Saint Helena",
39587         "sh",
39588         "290"
39589       ],
39590       [
39591         "Saint Kitts and Nevis",
39592         "kn",
39593         "1869"
39594       ],
39595       [
39596         "Saint Lucia",
39597         "lc",
39598         "1758"
39599       ],
39600       [
39601         "Saint Martin (Saint-Martin (partie française))",
39602         "mf",
39603         "590",
39604         2
39605       ],
39606       [
39607         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39608         "pm",
39609         "508"
39610       ],
39611       [
39612         "Saint Vincent and the Grenadines",
39613         "vc",
39614         "1784"
39615       ],
39616       [
39617         "Samoa",
39618         "ws",
39619         "685"
39620       ],
39621       [
39622         "San Marino",
39623         "sm",
39624         "378"
39625       ],
39626       [
39627         "São Tomé and Príncipe (São Tomé e Príncipe)",
39628         "st",
39629         "239"
39630       ],
39631       [
39632         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39633         "sa",
39634         "966"
39635       ],
39636       [
39637         "Senegal (Sénégal)",
39638         "sn",
39639         "221"
39640       ],
39641       [
39642         "Serbia (Србија)",
39643         "rs",
39644         "381"
39645       ],
39646       [
39647         "Seychelles",
39648         "sc",
39649         "248"
39650       ],
39651       [
39652         "Sierra Leone",
39653         "sl",
39654         "232"
39655       ],
39656       [
39657         "Singapore",
39658         "sg",
39659         "65"
39660       ],
39661       [
39662         "Sint Maarten",
39663         "sx",
39664         "1721"
39665       ],
39666       [
39667         "Slovakia (Slovensko)",
39668         "sk",
39669         "421"
39670       ],
39671       [
39672         "Slovenia (Slovenija)",
39673         "si",
39674         "386"
39675       ],
39676       [
39677         "Solomon Islands",
39678         "sb",
39679         "677"
39680       ],
39681       [
39682         "Somalia (Soomaaliya)",
39683         "so",
39684         "252"
39685       ],
39686       [
39687         "South Africa",
39688         "za",
39689         "27"
39690       ],
39691       [
39692         "South Korea (대한민국)",
39693         "kr",
39694         "82"
39695       ],
39696       [
39697         "South Sudan (‫جنوب السودان‬‎)",
39698         "ss",
39699         "211"
39700       ],
39701       [
39702         "Spain (España)",
39703         "es",
39704         "34"
39705       ],
39706       [
39707         "Sri Lanka (ශ්‍රී ලංකාව)",
39708         "lk",
39709         "94"
39710       ],
39711       [
39712         "Sudan (‫السودان‬‎)",
39713         "sd",
39714         "249"
39715       ],
39716       [
39717         "Suriname",
39718         "sr",
39719         "597"
39720       ],
39721       [
39722         "Svalbard and Jan Mayen",
39723         "sj",
39724         "47",
39725         1
39726       ],
39727       [
39728         "Swaziland",
39729         "sz",
39730         "268"
39731       ],
39732       [
39733         "Sweden (Sverige)",
39734         "se",
39735         "46"
39736       ],
39737       [
39738         "Switzerland (Schweiz)",
39739         "ch",
39740         "41"
39741       ],
39742       [
39743         "Syria (‫سوريا‬‎)",
39744         "sy",
39745         "963"
39746       ],
39747       [
39748         "Taiwan (台灣)",
39749         "tw",
39750         "886"
39751       ],
39752       [
39753         "Tajikistan",
39754         "tj",
39755         "992"
39756       ],
39757       [
39758         "Tanzania",
39759         "tz",
39760         "255"
39761       ],
39762       [
39763         "Thailand (ไทย)",
39764         "th",
39765         "66"
39766       ],
39767       [
39768         "Timor-Leste",
39769         "tl",
39770         "670"
39771       ],
39772       [
39773         "Togo",
39774         "tg",
39775         "228"
39776       ],
39777       [
39778         "Tokelau",
39779         "tk",
39780         "690"
39781       ],
39782       [
39783         "Tonga",
39784         "to",
39785         "676"
39786       ],
39787       [
39788         "Trinidad and Tobago",
39789         "tt",
39790         "1868"
39791       ],
39792       [
39793         "Tunisia (‫تونس‬‎)",
39794         "tn",
39795         "216"
39796       ],
39797       [
39798         "Turkey (Türkiye)",
39799         "tr",
39800         "90"
39801       ],
39802       [
39803         "Turkmenistan",
39804         "tm",
39805         "993"
39806       ],
39807       [
39808         "Turks and Caicos Islands",
39809         "tc",
39810         "1649"
39811       ],
39812       [
39813         "Tuvalu",
39814         "tv",
39815         "688"
39816       ],
39817       [
39818         "U.S. Virgin Islands",
39819         "vi",
39820         "1340"
39821       ],
39822       [
39823         "Uganda",
39824         "ug",
39825         "256"
39826       ],
39827       [
39828         "Ukraine (Україна)",
39829         "ua",
39830         "380"
39831       ],
39832       [
39833         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39834         "ae",
39835         "971"
39836       ],
39837       [
39838         "United Kingdom",
39839         "gb",
39840         "44",
39841         0
39842       ],
39843       [
39844         "United States",
39845         "us",
39846         "1",
39847         0
39848       ],
39849       [
39850         "Uruguay",
39851         "uy",
39852         "598"
39853       ],
39854       [
39855         "Uzbekistan (Oʻzbekiston)",
39856         "uz",
39857         "998"
39858       ],
39859       [
39860         "Vanuatu",
39861         "vu",
39862         "678"
39863       ],
39864       [
39865         "Vatican City (Città del Vaticano)",
39866         "va",
39867         "39",
39868         1
39869       ],
39870       [
39871         "Venezuela",
39872         "ve",
39873         "58"
39874       ],
39875       [
39876         "Vietnam (Việt Nam)",
39877         "vn",
39878         "84"
39879       ],
39880       [
39881         "Wallis and Futuna (Wallis-et-Futuna)",
39882         "wf",
39883         "681"
39884       ],
39885       [
39886         "Western Sahara (‫الصحراء الغربية‬‎)",
39887         "eh",
39888         "212",
39889         1
39890       ],
39891       [
39892         "Yemen (‫اليمن‬‎)",
39893         "ye",
39894         "967"
39895       ],
39896       [
39897         "Zambia",
39898         "zm",
39899         "260"
39900       ],
39901       [
39902         "Zimbabwe",
39903         "zw",
39904         "263"
39905       ],
39906       [
39907         "Åland Islands",
39908         "ax",
39909         "358",
39910         1
39911       ]
39912   ];
39913   
39914   return d;
39915 }/**
39916 *    This script refer to:
39917 *    Title: International Telephone Input
39918 *    Author: Jack O'Connor
39919 *    Code version:  v12.1.12
39920 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39921 **/
39922
39923 /**
39924  * @class Roo.bootstrap.PhoneInput
39925  * @extends Roo.bootstrap.TriggerField
39926  * An input with International dial-code selection
39927  
39928  * @cfg {String} defaultDialCode default '+852'
39929  * @cfg {Array} preferedCountries default []
39930   
39931  * @constructor
39932  * Create a new PhoneInput.
39933  * @param {Object} config Configuration options
39934  */
39935
39936 Roo.bootstrap.PhoneInput = function(config) {
39937     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39938 };
39939
39940 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39941         
39942         listWidth: undefined,
39943         
39944         selectedClass: 'active',
39945         
39946         invalidClass : "has-warning",
39947         
39948         validClass: 'has-success',
39949         
39950         allowed: '0123456789',
39951         
39952         max_length: 15,
39953         
39954         /**
39955          * @cfg {String} defaultDialCode The default dial code when initializing the input
39956          */
39957         defaultDialCode: '+852',
39958         
39959         /**
39960          * @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
39961          */
39962         preferedCountries: false,
39963         
39964         getAutoCreate : function()
39965         {
39966             var data = Roo.bootstrap.PhoneInputData();
39967             var align = this.labelAlign || this.parentLabelAlign();
39968             var id = Roo.id();
39969             
39970             this.allCountries = [];
39971             this.dialCodeMapping = [];
39972             
39973             for (var i = 0; i < data.length; i++) {
39974               var c = data[i];
39975               this.allCountries[i] = {
39976                 name: c[0],
39977                 iso2: c[1],
39978                 dialCode: c[2],
39979                 priority: c[3] || 0,
39980                 areaCodes: c[4] || null
39981               };
39982               this.dialCodeMapping[c[2]] = {
39983                   name: c[0],
39984                   iso2: c[1],
39985                   priority: c[3] || 0,
39986                   areaCodes: c[4] || null
39987               };
39988             }
39989             
39990             var cfg = {
39991                 cls: 'form-group',
39992                 cn: []
39993             };
39994             
39995             var input =  {
39996                 tag: 'input',
39997                 id : id,
39998                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39999                 maxlength: this.max_length,
40000                 cls : 'form-control tel-input',
40001                 autocomplete: 'new-password'
40002             };
40003             
40004             var hiddenInput = {
40005                 tag: 'input',
40006                 type: 'hidden',
40007                 cls: 'hidden-tel-input'
40008             };
40009             
40010             if (this.name) {
40011                 hiddenInput.name = this.name;
40012             }
40013             
40014             if (this.disabled) {
40015                 input.disabled = true;
40016             }
40017             
40018             var flag_container = {
40019                 tag: 'div',
40020                 cls: 'flag-box',
40021                 cn: [
40022                     {
40023                         tag: 'div',
40024                         cls: 'flag'
40025                     },
40026                     {
40027                         tag: 'div',
40028                         cls: 'caret'
40029                     }
40030                 ]
40031             };
40032             
40033             var box = {
40034                 tag: 'div',
40035                 cls: this.hasFeedback ? 'has-feedback' : '',
40036                 cn: [
40037                     hiddenInput,
40038                     input,
40039                     {
40040                         tag: 'input',
40041                         cls: 'dial-code-holder',
40042                         disabled: true
40043                     }
40044                 ]
40045             };
40046             
40047             var container = {
40048                 cls: 'roo-select2-container input-group',
40049                 cn: [
40050                     flag_container,
40051                     box
40052                 ]
40053             };
40054             
40055             if (this.fieldLabel.length) {
40056                 var indicator = {
40057                     tag: 'i',
40058                     tooltip: 'This field is required'
40059                 };
40060                 
40061                 var label = {
40062                     tag: 'label',
40063                     'for':  id,
40064                     cls: 'control-label',
40065                     cn: []
40066                 };
40067                 
40068                 var label_text = {
40069                     tag: 'span',
40070                     html: this.fieldLabel
40071                 };
40072                 
40073                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40074                 label.cn = [
40075                     indicator,
40076                     label_text
40077                 ];
40078                 
40079                 if(this.indicatorpos == 'right') {
40080                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40081                     label.cn = [
40082                         label_text,
40083                         indicator
40084                     ];
40085                 }
40086                 
40087                 if(align == 'left') {
40088                     container = {
40089                         tag: 'div',
40090                         cn: [
40091                             container
40092                         ]
40093                     };
40094                     
40095                     if(this.labelWidth > 12){
40096                         label.style = "width: " + this.labelWidth + 'px';
40097                     }
40098                     if(this.labelWidth < 13 && this.labelmd == 0){
40099                         this.labelmd = this.labelWidth;
40100                     }
40101                     if(this.labellg > 0){
40102                         label.cls += ' col-lg-' + this.labellg;
40103                         input.cls += ' col-lg-' + (12 - this.labellg);
40104                     }
40105                     if(this.labelmd > 0){
40106                         label.cls += ' col-md-' + this.labelmd;
40107                         container.cls += ' col-md-' + (12 - this.labelmd);
40108                     }
40109                     if(this.labelsm > 0){
40110                         label.cls += ' col-sm-' + this.labelsm;
40111                         container.cls += ' col-sm-' + (12 - this.labelsm);
40112                     }
40113                     if(this.labelxs > 0){
40114                         label.cls += ' col-xs-' + this.labelxs;
40115                         container.cls += ' col-xs-' + (12 - this.labelxs);
40116                     }
40117                 }
40118             }
40119             
40120             cfg.cn = [
40121                 label,
40122                 container
40123             ];
40124             
40125             var settings = this;
40126             
40127             ['xs','sm','md','lg'].map(function(size){
40128                 if (settings[size]) {
40129                     cfg.cls += ' col-' + size + '-' + settings[size];
40130                 }
40131             });
40132             
40133             this.store = new Roo.data.Store({
40134                 proxy : new Roo.data.MemoryProxy({}),
40135                 reader : new Roo.data.JsonReader({
40136                     fields : [
40137                         {
40138                             'name' : 'name',
40139                             'type' : 'string'
40140                         },
40141                         {
40142                             'name' : 'iso2',
40143                             'type' : 'string'
40144                         },
40145                         {
40146                             'name' : 'dialCode',
40147                             'type' : 'string'
40148                         },
40149                         {
40150                             'name' : 'priority',
40151                             'type' : 'string'
40152                         },
40153                         {
40154                             'name' : 'areaCodes',
40155                             'type' : 'string'
40156                         }
40157                     ]
40158                 })
40159             });
40160             
40161             if(!this.preferedCountries) {
40162                 this.preferedCountries = [
40163                     'hk',
40164                     'gb',
40165                     'us'
40166                 ];
40167             }
40168             
40169             var p = this.preferedCountries.reverse();
40170             
40171             if(p) {
40172                 for (var i = 0; i < p.length; i++) {
40173                     for (var j = 0; j < this.allCountries.length; j++) {
40174                         if(this.allCountries[j].iso2 == p[i]) {
40175                             var t = this.allCountries[j];
40176                             this.allCountries.splice(j,1);
40177                             this.allCountries.unshift(t);
40178                         }
40179                     } 
40180                 }
40181             }
40182             
40183             this.store.proxy.data = {
40184                 success: true,
40185                 data: this.allCountries
40186             };
40187             
40188             return cfg;
40189         },
40190         
40191         initEvents : function()
40192         {
40193             this.createList();
40194             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40195             
40196             this.indicator = this.indicatorEl();
40197             this.flag = this.flagEl();
40198             this.dialCodeHolder = this.dialCodeHolderEl();
40199             
40200             this.trigger = this.el.select('div.flag-box',true).first();
40201             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40202             
40203             var _this = this;
40204             
40205             (function(){
40206                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40207                 _this.list.setWidth(lw);
40208             }).defer(100);
40209             
40210             this.list.on('mouseover', this.onViewOver, this);
40211             this.list.on('mousemove', this.onViewMove, this);
40212             this.inputEl().on("keyup", this.onKeyUp, this);
40213             this.inputEl().on("keypress", this.onKeyPress, this);
40214             
40215             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40216
40217             this.view = new Roo.View(this.list, this.tpl, {
40218                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40219             });
40220             
40221             this.view.on('click', this.onViewClick, this);
40222             this.setValue(this.defaultDialCode);
40223         },
40224         
40225         onTriggerClick : function(e)
40226         {
40227             Roo.log('trigger click');
40228             if(this.disabled){
40229                 return;
40230             }
40231             
40232             if(this.isExpanded()){
40233                 this.collapse();
40234                 this.hasFocus = false;
40235             }else {
40236                 this.store.load({});
40237                 this.hasFocus = true;
40238                 this.expand();
40239             }
40240         },
40241         
40242         isExpanded : function()
40243         {
40244             return this.list.isVisible();
40245         },
40246         
40247         collapse : function()
40248         {
40249             if(!this.isExpanded()){
40250                 return;
40251             }
40252             this.list.hide();
40253             Roo.get(document).un('mousedown', this.collapseIf, this);
40254             Roo.get(document).un('mousewheel', this.collapseIf, this);
40255             this.fireEvent('collapse', this);
40256             this.validate();
40257         },
40258         
40259         expand : function()
40260         {
40261             Roo.log('expand');
40262
40263             if(this.isExpanded() || !this.hasFocus){
40264                 return;
40265             }
40266             
40267             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40268             this.list.setWidth(lw);
40269             
40270             this.list.show();
40271             this.restrictHeight();
40272             
40273             Roo.get(document).on('mousedown', this.collapseIf, this);
40274             Roo.get(document).on('mousewheel', this.collapseIf, this);
40275             
40276             this.fireEvent('expand', this);
40277         },
40278         
40279         restrictHeight : function()
40280         {
40281             this.list.alignTo(this.inputEl(), this.listAlign);
40282             this.list.alignTo(this.inputEl(), this.listAlign);
40283         },
40284         
40285         onViewOver : function(e, t)
40286         {
40287             if(this.inKeyMode){
40288                 return;
40289             }
40290             var item = this.view.findItemFromChild(t);
40291             
40292             if(item){
40293                 var index = this.view.indexOf(item);
40294                 this.select(index, false);
40295             }
40296         },
40297
40298         // private
40299         onViewClick : function(view, doFocus, el, e)
40300         {
40301             var index = this.view.getSelectedIndexes()[0];
40302             
40303             var r = this.store.getAt(index);
40304             
40305             if(r){
40306                 this.onSelect(r, index);
40307             }
40308             if(doFocus !== false && !this.blockFocus){
40309                 this.inputEl().focus();
40310             }
40311         },
40312         
40313         onViewMove : function(e, t)
40314         {
40315             this.inKeyMode = false;
40316         },
40317         
40318         select : function(index, scrollIntoView)
40319         {
40320             this.selectedIndex = index;
40321             this.view.select(index);
40322             if(scrollIntoView !== false){
40323                 var el = this.view.getNode(index);
40324                 if(el){
40325                     this.list.scrollChildIntoView(el, false);
40326                 }
40327             }
40328         },
40329         
40330         createList : function()
40331         {
40332             this.list = Roo.get(document.body).createChild({
40333                 tag: 'ul',
40334                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40335                 style: 'display:none'
40336             });
40337             
40338             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40339         },
40340         
40341         collapseIf : function(e)
40342         {
40343             var in_combo  = e.within(this.el);
40344             var in_list =  e.within(this.list);
40345             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40346             
40347             if (in_combo || in_list || is_list) {
40348                 return;
40349             }
40350             this.collapse();
40351         },
40352         
40353         onSelect : function(record, index)
40354         {
40355             if(this.fireEvent('beforeselect', this, record, index) !== false){
40356                 
40357                 this.setFlagClass(record.data.iso2);
40358                 this.setDialCode(record.data.dialCode);
40359                 this.hasFocus = false;
40360                 this.collapse();
40361                 this.fireEvent('select', this, record, index);
40362             }
40363         },
40364         
40365         flagEl : function()
40366         {
40367             var flag = this.el.select('div.flag',true).first();
40368             if(!flag){
40369                 return false;
40370             }
40371             return flag;
40372         },
40373         
40374         dialCodeHolderEl : function()
40375         {
40376             var d = this.el.select('input.dial-code-holder',true).first();
40377             if(!d){
40378                 return false;
40379             }
40380             return d;
40381         },
40382         
40383         setDialCode : function(v)
40384         {
40385             this.dialCodeHolder.dom.value = '+'+v;
40386         },
40387         
40388         setFlagClass : function(n)
40389         {
40390             this.flag.dom.className = 'flag '+n;
40391         },
40392         
40393         getValue : function()
40394         {
40395             var v = this.inputEl().getValue();
40396             if(this.dialCodeHolder) {
40397                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40398             }
40399             return v;
40400         },
40401         
40402         setValue : function(v)
40403         {
40404             var d = this.getDialCode(v);
40405             
40406             //invalid dial code
40407             if(v.length == 0 || !d || d.length == 0) {
40408                 if(this.rendered){
40409                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40410                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40411                 }
40412                 return;
40413             }
40414             
40415             //valid dial code
40416             this.setFlagClass(this.dialCodeMapping[d].iso2);
40417             this.setDialCode(d);
40418             this.inputEl().dom.value = v.replace('+'+d,'');
40419             this.hiddenEl().dom.value = this.getValue();
40420             
40421             this.validate();
40422         },
40423         
40424         getDialCode : function(v)
40425         {
40426             v = v ||  '';
40427             
40428             if (v.length == 0) {
40429                 return this.dialCodeHolder.dom.value;
40430             }
40431             
40432             var dialCode = "";
40433             if (v.charAt(0) != "+") {
40434                 return false;
40435             }
40436             var numericChars = "";
40437             for (var i = 1; i < v.length; i++) {
40438               var c = v.charAt(i);
40439               if (!isNaN(c)) {
40440                 numericChars += c;
40441                 if (this.dialCodeMapping[numericChars]) {
40442                   dialCode = v.substr(1, i);
40443                 }
40444                 if (numericChars.length == 4) {
40445                   break;
40446                 }
40447               }
40448             }
40449             return dialCode;
40450         },
40451         
40452         reset : function()
40453         {
40454             this.setValue(this.defaultDialCode);
40455             this.validate();
40456         },
40457         
40458         hiddenEl : function()
40459         {
40460             return this.el.select('input.hidden-tel-input',true).first();
40461         },
40462         
40463         // after setting val
40464         onKeyUp : function(e){
40465             this.setValue(this.getValue());
40466         },
40467         
40468         onKeyPress : function(e){
40469             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40470                 e.stopEvent();
40471             }
40472         }
40473         
40474 });
40475 /**
40476  * @class Roo.bootstrap.MoneyField
40477  * @extends Roo.bootstrap.ComboBox
40478  * Bootstrap MoneyField class
40479  * 
40480  * @constructor
40481  * Create a new MoneyField.
40482  * @param {Object} config Configuration options
40483  */
40484
40485 Roo.bootstrap.MoneyField = function(config) {
40486     
40487     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40488     
40489 };
40490
40491 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40492     
40493     /**
40494      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40495      */
40496     allowDecimals : true,
40497     /**
40498      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40499      */
40500     decimalSeparator : ".",
40501     /**
40502      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40503      */
40504     decimalPrecision : 0,
40505     /**
40506      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40507      */
40508     allowNegative : true,
40509     /**
40510      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40511      */
40512     allowZero: true,
40513     /**
40514      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40515      */
40516     minValue : Number.NEGATIVE_INFINITY,
40517     /**
40518      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40519      */
40520     maxValue : Number.MAX_VALUE,
40521     /**
40522      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40523      */
40524     minText : "The minimum value for this field is {0}",
40525     /**
40526      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40527      */
40528     maxText : "The maximum value for this field is {0}",
40529     /**
40530      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40531      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40532      */
40533     nanText : "{0} is not a valid number",
40534     /**
40535      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40536      */
40537     castInt : true,
40538     /**
40539      * @cfg {String} defaults currency of the MoneyField
40540      * value should be in lkey
40541      */
40542     defaultCurrency : false,
40543     /**
40544      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40545      */
40546     thousandsDelimiter : false,
40547     /**
40548      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40549      */
40550     max_length: false,
40551     
40552     inputlg : 9,
40553     inputmd : 9,
40554     inputsm : 9,
40555     inputxs : 6,
40556     
40557     store : false,
40558     
40559     getAutoCreate : function()
40560     {
40561         var align = this.labelAlign || this.parentLabelAlign();
40562         
40563         var id = Roo.id();
40564
40565         var cfg = {
40566             cls: 'form-group',
40567             cn: []
40568         };
40569
40570         var input =  {
40571             tag: 'input',
40572             id : id,
40573             cls : 'form-control roo-money-amount-input',
40574             autocomplete: 'new-password'
40575         };
40576         
40577         var hiddenInput = {
40578             tag: 'input',
40579             type: 'hidden',
40580             id: Roo.id(),
40581             cls: 'hidden-number-input'
40582         };
40583         
40584         if(this.max_length) {
40585             input.maxlength = this.max_length; 
40586         }
40587         
40588         if (this.name) {
40589             hiddenInput.name = this.name;
40590         }
40591
40592         if (this.disabled) {
40593             input.disabled = true;
40594         }
40595
40596         var clg = 12 - this.inputlg;
40597         var cmd = 12 - this.inputmd;
40598         var csm = 12 - this.inputsm;
40599         var cxs = 12 - this.inputxs;
40600         
40601         var container = {
40602             tag : 'div',
40603             cls : 'row roo-money-field',
40604             cn : [
40605                 {
40606                     tag : 'div',
40607                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40608                     cn : [
40609                         {
40610                             tag : 'div',
40611                             cls: 'roo-select2-container input-group',
40612                             cn: [
40613                                 {
40614                                     tag : 'input',
40615                                     cls : 'form-control roo-money-currency-input',
40616                                     autocomplete: 'new-password',
40617                                     readOnly : 1,
40618                                     name : this.currencyName
40619                                 },
40620                                 {
40621                                     tag :'span',
40622                                     cls : 'input-group-addon',
40623                                     cn : [
40624                                         {
40625                                             tag: 'span',
40626                                             cls: 'caret'
40627                                         }
40628                                     ]
40629                                 }
40630                             ]
40631                         }
40632                     ]
40633                 },
40634                 {
40635                     tag : 'div',
40636                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40637                     cn : [
40638                         {
40639                             tag: 'div',
40640                             cls: this.hasFeedback ? 'has-feedback' : '',
40641                             cn: [
40642                                 input
40643                             ]
40644                         }
40645                     ]
40646                 }
40647             ]
40648             
40649         };
40650         
40651         if (this.fieldLabel.length) {
40652             var indicator = {
40653                 tag: 'i',
40654                 tooltip: 'This field is required'
40655             };
40656
40657             var label = {
40658                 tag: 'label',
40659                 'for':  id,
40660                 cls: 'control-label',
40661                 cn: []
40662             };
40663
40664             var label_text = {
40665                 tag: 'span',
40666                 html: this.fieldLabel
40667             };
40668
40669             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40670             label.cn = [
40671                 indicator,
40672                 label_text
40673             ];
40674
40675             if(this.indicatorpos == 'right') {
40676                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40677                 label.cn = [
40678                     label_text,
40679                     indicator
40680                 ];
40681             }
40682
40683             if(align == 'left') {
40684                 container = {
40685                     tag: 'div',
40686                     cn: [
40687                         container
40688                     ]
40689                 };
40690
40691                 if(this.labelWidth > 12){
40692                     label.style = "width: " + this.labelWidth + 'px';
40693                 }
40694                 if(this.labelWidth < 13 && this.labelmd == 0){
40695                     this.labelmd = this.labelWidth;
40696                 }
40697                 if(this.labellg > 0){
40698                     label.cls += ' col-lg-' + this.labellg;
40699                     input.cls += ' col-lg-' + (12 - this.labellg);
40700                 }
40701                 if(this.labelmd > 0){
40702                     label.cls += ' col-md-' + this.labelmd;
40703                     container.cls += ' col-md-' + (12 - this.labelmd);
40704                 }
40705                 if(this.labelsm > 0){
40706                     label.cls += ' col-sm-' + this.labelsm;
40707                     container.cls += ' col-sm-' + (12 - this.labelsm);
40708                 }
40709                 if(this.labelxs > 0){
40710                     label.cls += ' col-xs-' + this.labelxs;
40711                     container.cls += ' col-xs-' + (12 - this.labelxs);
40712                 }
40713             }
40714         }
40715
40716         cfg.cn = [
40717             label,
40718             container,
40719             hiddenInput
40720         ];
40721         
40722         var settings = this;
40723
40724         ['xs','sm','md','lg'].map(function(size){
40725             if (settings[size]) {
40726                 cfg.cls += ' col-' + size + '-' + settings[size];
40727             }
40728         });
40729         
40730         return cfg;
40731     },
40732     
40733     initEvents : function()
40734     {
40735         this.indicator = this.indicatorEl();
40736         
40737         this.initCurrencyEvent();
40738         
40739         this.initNumberEvent();
40740     },
40741     
40742     initCurrencyEvent : function()
40743     {
40744         if (!this.store) {
40745             throw "can not find store for combo";
40746         }
40747         
40748         this.store = Roo.factory(this.store, Roo.data);
40749         this.store.parent = this;
40750         
40751         this.createList();
40752         
40753         this.triggerEl = this.el.select('.input-group-addon', true).first();
40754         
40755         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40756         
40757         var _this = this;
40758         
40759         (function(){
40760             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40761             _this.list.setWidth(lw);
40762         }).defer(100);
40763         
40764         this.list.on('mouseover', this.onViewOver, this);
40765         this.list.on('mousemove', this.onViewMove, this);
40766         this.list.on('scroll', this.onViewScroll, this);
40767         
40768         if(!this.tpl){
40769             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40770         }
40771         
40772         this.view = new Roo.View(this.list, this.tpl, {
40773             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40774         });
40775         
40776         this.view.on('click', this.onViewClick, this);
40777         
40778         this.store.on('beforeload', this.onBeforeLoad, this);
40779         this.store.on('load', this.onLoad, this);
40780         this.store.on('loadexception', this.onLoadException, this);
40781         
40782         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40783             "up" : function(e){
40784                 this.inKeyMode = true;
40785                 this.selectPrev();
40786             },
40787
40788             "down" : function(e){
40789                 if(!this.isExpanded()){
40790                     this.onTriggerClick();
40791                 }else{
40792                     this.inKeyMode = true;
40793                     this.selectNext();
40794                 }
40795             },
40796
40797             "enter" : function(e){
40798                 this.collapse();
40799                 
40800                 if(this.fireEvent("specialkey", this, e)){
40801                     this.onViewClick(false);
40802                 }
40803                 
40804                 return true;
40805             },
40806
40807             "esc" : function(e){
40808                 this.collapse();
40809             },
40810
40811             "tab" : function(e){
40812                 this.collapse();
40813                 
40814                 if(this.fireEvent("specialkey", this, e)){
40815                     this.onViewClick(false);
40816                 }
40817                 
40818                 return true;
40819             },
40820
40821             scope : this,
40822
40823             doRelay : function(foo, bar, hname){
40824                 if(hname == 'down' || this.scope.isExpanded()){
40825                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40826                 }
40827                 return true;
40828             },
40829
40830             forceKeyDown: true
40831         });
40832         
40833         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40834         
40835     },
40836     
40837     initNumberEvent : function(e)
40838     {
40839         this.inputEl().on("keydown" , this.fireKey,  this);
40840         this.inputEl().on("focus", this.onFocus,  this);
40841         this.inputEl().on("blur", this.onBlur,  this);
40842         
40843         this.inputEl().relayEvent('keyup', this);
40844         
40845         if(this.indicator){
40846             this.indicator.addClass('invisible');
40847         }
40848  
40849         this.originalValue = this.getValue();
40850         
40851         if(this.validationEvent == 'keyup'){
40852             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40853             this.inputEl().on('keyup', this.filterValidation, this);
40854         }
40855         else if(this.validationEvent !== false){
40856             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40857         }
40858         
40859         if(this.selectOnFocus){
40860             this.on("focus", this.preFocus, this);
40861             
40862         }
40863         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40864             this.inputEl().on("keypress", this.filterKeys, this);
40865         } else {
40866             this.inputEl().relayEvent('keypress', this);
40867         }
40868         
40869         var allowed = "0123456789";
40870         
40871         if(this.allowDecimals){
40872             allowed += this.decimalSeparator;
40873         }
40874         
40875         if(this.allowNegative){
40876             allowed += "-";
40877         }
40878         
40879         if(this.thousandsDelimiter) {
40880             allowed += ",";
40881         }
40882         
40883         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40884         
40885         var keyPress = function(e){
40886             
40887             var k = e.getKey();
40888             
40889             var c = e.getCharCode();
40890             
40891             if(
40892                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40893                     allowed.indexOf(String.fromCharCode(c)) === -1
40894             ){
40895                 e.stopEvent();
40896                 return;
40897             }
40898             
40899             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40900                 return;
40901             }
40902             
40903             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40904                 e.stopEvent();
40905             }
40906         };
40907         
40908         this.inputEl().on("keypress", keyPress, this);
40909         
40910     },
40911     
40912     onTriggerClick : function(e)
40913     {   
40914         if(this.disabled){
40915             return;
40916         }
40917         
40918         this.page = 0;
40919         this.loadNext = false;
40920         
40921         if(this.isExpanded()){
40922             this.collapse();
40923             return;
40924         }
40925         
40926         this.hasFocus = true;
40927         
40928         if(this.triggerAction == 'all') {
40929             this.doQuery(this.allQuery, true);
40930             return;
40931         }
40932         
40933         this.doQuery(this.getRawValue());
40934     },
40935     
40936     getCurrency : function()
40937     {   
40938         var v = this.currencyEl().getValue();
40939         
40940         return v;
40941     },
40942     
40943     restrictHeight : function()
40944     {
40945         this.list.alignTo(this.currencyEl(), this.listAlign);
40946         this.list.alignTo(this.currencyEl(), this.listAlign);
40947     },
40948     
40949     onViewClick : function(view, doFocus, el, e)
40950     {
40951         var index = this.view.getSelectedIndexes()[0];
40952         
40953         var r = this.store.getAt(index);
40954         
40955         if(r){
40956             this.onSelect(r, index);
40957         }
40958     },
40959     
40960     onSelect : function(record, index){
40961         
40962         if(this.fireEvent('beforeselect', this, record, index) !== false){
40963         
40964             this.setFromCurrencyData(index > -1 ? record.data : false);
40965             
40966             this.collapse();
40967             
40968             this.fireEvent('select', this, record, index);
40969         }
40970     },
40971     
40972     setFromCurrencyData : function(o)
40973     {
40974         var currency = '';
40975         
40976         this.lastCurrency = o;
40977         
40978         if (this.currencyField) {
40979             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40980         } else {
40981             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40982         }
40983         
40984         this.lastSelectionText = currency;
40985         
40986         //setting default currency
40987         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40988             this.setCurrency(this.defaultCurrency);
40989             return;
40990         }
40991         
40992         this.setCurrency(currency);
40993     },
40994     
40995     setFromData : function(o)
40996     {
40997         var c = {};
40998         
40999         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41000         
41001         this.setFromCurrencyData(c);
41002         
41003         var value = '';
41004         
41005         if (this.name) {
41006             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41007         } else {
41008             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41009         }
41010         
41011         this.setValue(value);
41012         
41013     },
41014     
41015     setCurrency : function(v)
41016     {   
41017         this.currencyValue = v;
41018         
41019         if(this.rendered){
41020             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41021             this.validate();
41022         }
41023     },
41024     
41025     setValue : function(v)
41026     {
41027         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41028         
41029         this.value = v;
41030         
41031         if(this.rendered){
41032             
41033             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41034             
41035             this.inputEl().dom.value = (v == '') ? '' :
41036                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41037             
41038             if(!this.allowZero && v === '0') {
41039                 this.hiddenEl().dom.value = '';
41040                 this.inputEl().dom.value = '';
41041             }
41042             
41043             this.validate();
41044         }
41045     },
41046     
41047     getRawValue : function()
41048     {
41049         var v = this.inputEl().getValue();
41050         
41051         return v;
41052     },
41053     
41054     getValue : function()
41055     {
41056         return this.fixPrecision(this.parseValue(this.getRawValue()));
41057     },
41058     
41059     parseValue : function(value)
41060     {
41061         if(this.thousandsDelimiter) {
41062             value += "";
41063             r = new RegExp(",", "g");
41064             value = value.replace(r, "");
41065         }
41066         
41067         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41068         return isNaN(value) ? '' : value;
41069         
41070     },
41071     
41072     fixPrecision : function(value)
41073     {
41074         if(this.thousandsDelimiter) {
41075             value += "";
41076             r = new RegExp(",", "g");
41077             value = value.replace(r, "");
41078         }
41079         
41080         var nan = isNaN(value);
41081         
41082         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41083             return nan ? '' : value;
41084         }
41085         return parseFloat(value).toFixed(this.decimalPrecision);
41086     },
41087     
41088     decimalPrecisionFcn : function(v)
41089     {
41090         return Math.floor(v);
41091     },
41092     
41093     validateValue : function(value)
41094     {
41095         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41096             return false;
41097         }
41098         
41099         var num = this.parseValue(value);
41100         
41101         if(isNaN(num)){
41102             this.markInvalid(String.format(this.nanText, value));
41103             return false;
41104         }
41105         
41106         if(num < this.minValue){
41107             this.markInvalid(String.format(this.minText, this.minValue));
41108             return false;
41109         }
41110         
41111         if(num > this.maxValue){
41112             this.markInvalid(String.format(this.maxText, this.maxValue));
41113             return false;
41114         }
41115         
41116         return true;
41117     },
41118     
41119     validate : function()
41120     {
41121         if(this.disabled || this.allowBlank){
41122             this.markValid();
41123             return true;
41124         }
41125         
41126         var currency = this.getCurrency();
41127         
41128         if(this.validateValue(this.getRawValue()) && currency.length){
41129             this.markValid();
41130             return true;
41131         }
41132         
41133         this.markInvalid();
41134         return false;
41135     },
41136     
41137     getName: function()
41138     {
41139         return this.name;
41140     },
41141     
41142     beforeBlur : function()
41143     {
41144         if(!this.castInt){
41145             return;
41146         }
41147         
41148         var v = this.parseValue(this.getRawValue());
41149         
41150         if(v || v == 0){
41151             this.setValue(v);
41152         }
41153     },
41154     
41155     onBlur : function()
41156     {
41157         this.beforeBlur();
41158         
41159         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41160             //this.el.removeClass(this.focusClass);
41161         }
41162         
41163         this.hasFocus = false;
41164         
41165         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41166             this.validate();
41167         }
41168         
41169         var v = this.getValue();
41170         
41171         if(String(v) !== String(this.startValue)){
41172             this.fireEvent('change', this, v, this.startValue);
41173         }
41174         
41175         this.fireEvent("blur", this);
41176     },
41177     
41178     inputEl : function()
41179     {
41180         return this.el.select('.roo-money-amount-input', true).first();
41181     },
41182     
41183     currencyEl : function()
41184     {
41185         return this.el.select('.roo-money-currency-input', true).first();
41186     },
41187     
41188     hiddenEl : function()
41189     {
41190         return this.el.select('input.hidden-number-input',true).first();
41191     }
41192     
41193 });