Uncommited changes synced
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | 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',
17649                             html : this.title
17650                         },
17651                         {
17652                             cls : 'popover-content',
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(['fade','top','bottom', 'left', 'right','in']);
17778         if (!this.title.length) {
17779             this.el.select('.popover-title',true).hide();
17780         }
17781         
17782         var placement = typeof this.placement == 'function' ?
17783             this.placement.call(this, this.el, on_el) :
17784             this.placement;
17785             
17786         var autoToken = /\s?auto?\s?/i;
17787         var autoPlace = autoToken.test(placement);
17788         if (autoPlace) {
17789             placement = placement.replace(autoToken, '') || 'top';
17790         }
17791         
17792         //this.el.detach()
17793         //this.el.setXY([0,0]);
17794         this.el.show();
17795         this.el.dom.style.display='block';
17796         this.el.addClass(placement);
17797         
17798         //this.el.appendTo(on_el);
17799         
17800         var p = this.getPosition();
17801         var box = this.el.getBox();
17802         
17803         if (autoPlace) {
17804             // fixme..
17805         }
17806         var align = Roo.bootstrap.Popover.alignment[placement];
17807         
17808 //        Roo.log(align);
17809         this.el.alignTo(on_el, align[0],align[1]);
17810         //var arrow = this.el.select('.arrow',true).first();
17811         //arrow.set(align[2], 
17812         
17813         this.el.addClass('in');
17814         
17815         
17816         if (this.el.hasClass('fade')) {
17817             // fade it?
17818         }
17819         
17820         this.hoverState = 'in';
17821         
17822         this.fireEvent('show', this);
17823         
17824     },
17825     hide : function()
17826     {
17827         this.el.setXY([0,0]);
17828         this.el.removeClass('in');
17829         this.el.hide();
17830         this.hoverState = null;
17831         
17832         this.fireEvent('hide', this);
17833     }
17834     
17835 });
17836
17837 Roo.bootstrap.Popover.alignment = {
17838     'left' : ['r-l', [-10,0], 'right'],
17839     'right' : ['l-r', [10,0], 'left'],
17840     'bottom' : ['t-b', [0,10], 'top'],
17841     'top' : [ 'b-t', [0,-10], 'bottom']
17842 };
17843
17844  /*
17845  * - LGPL
17846  *
17847  * Progress
17848  * 
17849  */
17850
17851 /**
17852  * @class Roo.bootstrap.Progress
17853  * @extends Roo.bootstrap.Component
17854  * Bootstrap Progress class
17855  * @cfg {Boolean} striped striped of the progress bar
17856  * @cfg {Boolean} active animated of the progress bar
17857  * 
17858  * 
17859  * @constructor
17860  * Create a new Progress
17861  * @param {Object} config The config object
17862  */
17863
17864 Roo.bootstrap.Progress = function(config){
17865     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17866 };
17867
17868 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17869     
17870     striped : false,
17871     active: false,
17872     
17873     getAutoCreate : function(){
17874         var cfg = {
17875             tag: 'div',
17876             cls: 'progress'
17877         };
17878         
17879         
17880         if(this.striped){
17881             cfg.cls += ' progress-striped';
17882         }
17883       
17884         if(this.active){
17885             cfg.cls += ' active';
17886         }
17887         
17888         
17889         return cfg;
17890     }
17891    
17892 });
17893
17894  
17895
17896  /*
17897  * - LGPL
17898  *
17899  * ProgressBar
17900  * 
17901  */
17902
17903 /**
17904  * @class Roo.bootstrap.ProgressBar
17905  * @extends Roo.bootstrap.Component
17906  * Bootstrap ProgressBar class
17907  * @cfg {Number} aria_valuenow aria-value now
17908  * @cfg {Number} aria_valuemin aria-value min
17909  * @cfg {Number} aria_valuemax aria-value max
17910  * @cfg {String} label label for the progress bar
17911  * @cfg {String} panel (success | info | warning | danger )
17912  * @cfg {String} role role of the progress bar
17913  * @cfg {String} sr_only text
17914  * 
17915  * 
17916  * @constructor
17917  * Create a new ProgressBar
17918  * @param {Object} config The config object
17919  */
17920
17921 Roo.bootstrap.ProgressBar = function(config){
17922     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17923 };
17924
17925 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17926     
17927     aria_valuenow : 0,
17928     aria_valuemin : 0,
17929     aria_valuemax : 100,
17930     label : false,
17931     panel : false,
17932     role : false,
17933     sr_only: false,
17934     
17935     getAutoCreate : function()
17936     {
17937         
17938         var cfg = {
17939             tag: 'div',
17940             cls: 'progress-bar',
17941             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17942         };
17943         
17944         if(this.sr_only){
17945             cfg.cn = {
17946                 tag: 'span',
17947                 cls: 'sr-only',
17948                 html: this.sr_only
17949             }
17950         }
17951         
17952         if(this.role){
17953             cfg.role = this.role;
17954         }
17955         
17956         if(this.aria_valuenow){
17957             cfg['aria-valuenow'] = this.aria_valuenow;
17958         }
17959         
17960         if(this.aria_valuemin){
17961             cfg['aria-valuemin'] = this.aria_valuemin;
17962         }
17963         
17964         if(this.aria_valuemax){
17965             cfg['aria-valuemax'] = this.aria_valuemax;
17966         }
17967         
17968         if(this.label && !this.sr_only){
17969             cfg.html = this.label;
17970         }
17971         
17972         if(this.panel){
17973             cfg.cls += ' progress-bar-' + this.panel;
17974         }
17975         
17976         return cfg;
17977     },
17978     
17979     update : function(aria_valuenow)
17980     {
17981         this.aria_valuenow = aria_valuenow;
17982         
17983         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17984     }
17985    
17986 });
17987
17988  
17989
17990  /*
17991  * - LGPL
17992  *
17993  * column
17994  * 
17995  */
17996
17997 /**
17998  * @class Roo.bootstrap.TabGroup
17999  * @extends Roo.bootstrap.Column
18000  * Bootstrap Column class
18001  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18002  * @cfg {Boolean} carousel true to make the group behave like a carousel
18003  * @cfg {Boolean} bullets show bullets for the panels
18004  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18005  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18006  * @cfg {Boolean} showarrow (true|false) show arrow default true
18007  * 
18008  * @constructor
18009  * Create a new TabGroup
18010  * @param {Object} config The config object
18011  */
18012
18013 Roo.bootstrap.TabGroup = function(config){
18014     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18015     if (!this.navId) {
18016         this.navId = Roo.id();
18017     }
18018     this.tabs = [];
18019     Roo.bootstrap.TabGroup.register(this);
18020     
18021 };
18022
18023 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18024     
18025     carousel : false,
18026     transition : false,
18027     bullets : 0,
18028     timer : 0,
18029     autoslide : false,
18030     slideFn : false,
18031     slideOnTouch : false,
18032     showarrow : true,
18033     
18034     getAutoCreate : function()
18035     {
18036         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18037         
18038         cfg.cls += ' tab-content';
18039         
18040         if (this.carousel) {
18041             cfg.cls += ' carousel slide';
18042             
18043             cfg.cn = [{
18044                cls : 'carousel-inner',
18045                cn : []
18046             }];
18047         
18048             if(this.bullets  && !Roo.isTouch){
18049                 
18050                 var bullets = {
18051                     cls : 'carousel-bullets',
18052                     cn : []
18053                 };
18054                
18055                 if(this.bullets_cls){
18056                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18057                 }
18058                 
18059                 bullets.cn.push({
18060                     cls : 'clear'
18061                 });
18062                 
18063                 cfg.cn[0].cn.push(bullets);
18064             }
18065             
18066             if(this.showarrow){
18067                 cfg.cn[0].cn.push({
18068                     tag : 'div',
18069                     class : 'carousel-arrow',
18070                     cn : [
18071                         {
18072                             tag : 'div',
18073                             class : 'carousel-prev',
18074                             cn : [
18075                                 {
18076                                     tag : 'i',
18077                                     class : 'fa fa-chevron-left'
18078                                 }
18079                             ]
18080                         },
18081                         {
18082                             tag : 'div',
18083                             class : 'carousel-next',
18084                             cn : [
18085                                 {
18086                                     tag : 'i',
18087                                     class : 'fa fa-chevron-right'
18088                                 }
18089                             ]
18090                         }
18091                     ]
18092                 });
18093             }
18094             
18095         }
18096         
18097         return cfg;
18098     },
18099     
18100     initEvents:  function()
18101     {
18102 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18103 //            this.el.on("touchstart", this.onTouchStart, this);
18104 //        }
18105         
18106         if(this.autoslide){
18107             var _this = this;
18108             
18109             this.slideFn = window.setInterval(function() {
18110                 _this.showPanelNext();
18111             }, this.timer);
18112         }
18113         
18114         if(this.showarrow){
18115             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18116             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18117         }
18118         
18119         
18120     },
18121     
18122 //    onTouchStart : function(e, el, o)
18123 //    {
18124 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18125 //            return;
18126 //        }
18127 //        
18128 //        this.showPanelNext();
18129 //    },
18130     
18131     
18132     getChildContainer : function()
18133     {
18134         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18135     },
18136     
18137     /**
18138     * register a Navigation item
18139     * @param {Roo.bootstrap.NavItem} the navitem to add
18140     */
18141     register : function(item)
18142     {
18143         this.tabs.push( item);
18144         item.navId = this.navId; // not really needed..
18145         this.addBullet();
18146     
18147     },
18148     
18149     getActivePanel : function()
18150     {
18151         var r = false;
18152         Roo.each(this.tabs, function(t) {
18153             if (t.active) {
18154                 r = t;
18155                 return false;
18156             }
18157             return null;
18158         });
18159         return r;
18160         
18161     },
18162     getPanelByName : function(n)
18163     {
18164         var r = false;
18165         Roo.each(this.tabs, function(t) {
18166             if (t.tabId == n) {
18167                 r = t;
18168                 return false;
18169             }
18170             return null;
18171         });
18172         return r;
18173     },
18174     indexOfPanel : function(p)
18175     {
18176         var r = false;
18177         Roo.each(this.tabs, function(t,i) {
18178             if (t.tabId == p.tabId) {
18179                 r = i;
18180                 return false;
18181             }
18182             return null;
18183         });
18184         return r;
18185     },
18186     /**
18187      * show a specific panel
18188      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18189      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18190      */
18191     showPanel : function (pan)
18192     {
18193         if(this.transition || typeof(pan) == 'undefined'){
18194             Roo.log("waiting for the transitionend");
18195             return;
18196         }
18197         
18198         if (typeof(pan) == 'number') {
18199             pan = this.tabs[pan];
18200         }
18201         
18202         if (typeof(pan) == 'string') {
18203             pan = this.getPanelByName(pan);
18204         }
18205         
18206         var cur = this.getActivePanel();
18207         
18208         if(!pan || !cur){
18209             Roo.log('pan or acitve pan is undefined');
18210             return false;
18211         }
18212         
18213         if (pan.tabId == this.getActivePanel().tabId) {
18214             return true;
18215         }
18216         
18217         if (false === cur.fireEvent('beforedeactivate')) {
18218             return false;
18219         }
18220         
18221         if(this.bullets > 0 && !Roo.isTouch){
18222             this.setActiveBullet(this.indexOfPanel(pan));
18223         }
18224         
18225         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18226             
18227             this.transition = true;
18228             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18229             var lr = dir == 'next' ? 'left' : 'right';
18230             pan.el.addClass(dir); // or prev
18231             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18232             cur.el.addClass(lr); // or right
18233             pan.el.addClass(lr);
18234             
18235             var _this = this;
18236             cur.el.on('transitionend', function() {
18237                 Roo.log("trans end?");
18238                 
18239                 pan.el.removeClass([lr,dir]);
18240                 pan.setActive(true);
18241                 
18242                 cur.el.removeClass([lr]);
18243                 cur.setActive(false);
18244                 
18245                 _this.transition = false;
18246                 
18247             }, this, { single:  true } );
18248             
18249             return true;
18250         }
18251         
18252         cur.setActive(false);
18253         pan.setActive(true);
18254         
18255         return true;
18256         
18257     },
18258     showPanelNext : function()
18259     {
18260         var i = this.indexOfPanel(this.getActivePanel());
18261         
18262         if (i >= this.tabs.length - 1 && !this.autoslide) {
18263             return;
18264         }
18265         
18266         if (i >= this.tabs.length - 1 && this.autoslide) {
18267             i = -1;
18268         }
18269         
18270         this.showPanel(this.tabs[i+1]);
18271     },
18272     
18273     showPanelPrev : function()
18274     {
18275         var i = this.indexOfPanel(this.getActivePanel());
18276         
18277         if (i  < 1 && !this.autoslide) {
18278             return;
18279         }
18280         
18281         if (i < 1 && this.autoslide) {
18282             i = this.tabs.length;
18283         }
18284         
18285         this.showPanel(this.tabs[i-1]);
18286     },
18287     
18288     
18289     addBullet: function()
18290     {
18291         if(!this.bullets || Roo.isTouch){
18292             return;
18293         }
18294         var ctr = this.el.select('.carousel-bullets',true).first();
18295         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18296         var bullet = ctr.createChild({
18297             cls : 'bullet bullet-' + i
18298         },ctr.dom.lastChild);
18299         
18300         
18301         var _this = this;
18302         
18303         bullet.on('click', (function(e, el, o, ii, t){
18304
18305             e.preventDefault();
18306
18307             this.showPanel(ii);
18308
18309             if(this.autoslide && this.slideFn){
18310                 clearInterval(this.slideFn);
18311                 this.slideFn = window.setInterval(function() {
18312                     _this.showPanelNext();
18313                 }, this.timer);
18314             }
18315
18316         }).createDelegate(this, [i, bullet], true));
18317                 
18318         
18319     },
18320      
18321     setActiveBullet : function(i)
18322     {
18323         if(Roo.isTouch){
18324             return;
18325         }
18326         
18327         Roo.each(this.el.select('.bullet', true).elements, function(el){
18328             el.removeClass('selected');
18329         });
18330
18331         var bullet = this.el.select('.bullet-' + i, true).first();
18332         
18333         if(!bullet){
18334             return;
18335         }
18336         
18337         bullet.addClass('selected');
18338     }
18339     
18340     
18341   
18342 });
18343
18344  
18345
18346  
18347  
18348 Roo.apply(Roo.bootstrap.TabGroup, {
18349     
18350     groups: {},
18351      /**
18352     * register a Navigation Group
18353     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18354     */
18355     register : function(navgrp)
18356     {
18357         this.groups[navgrp.navId] = navgrp;
18358         
18359     },
18360     /**
18361     * fetch a Navigation Group based on the navigation ID
18362     * if one does not exist , it will get created.
18363     * @param {string} the navgroup to add
18364     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18365     */
18366     get: function(navId) {
18367         if (typeof(this.groups[navId]) == 'undefined') {
18368             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18369         }
18370         return this.groups[navId] ;
18371     }
18372     
18373     
18374     
18375 });
18376
18377  /*
18378  * - LGPL
18379  *
18380  * TabPanel
18381  * 
18382  */
18383
18384 /**
18385  * @class Roo.bootstrap.TabPanel
18386  * @extends Roo.bootstrap.Component
18387  * Bootstrap TabPanel class
18388  * @cfg {Boolean} active panel active
18389  * @cfg {String} html panel content
18390  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18391  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18392  * @cfg {String} href click to link..
18393  * 
18394  * 
18395  * @constructor
18396  * Create a new TabPanel
18397  * @param {Object} config The config object
18398  */
18399
18400 Roo.bootstrap.TabPanel = function(config){
18401     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18402     this.addEvents({
18403         /**
18404              * @event changed
18405              * Fires when the active status changes
18406              * @param {Roo.bootstrap.TabPanel} this
18407              * @param {Boolean} state the new state
18408             
18409          */
18410         'changed': true,
18411         /**
18412              * @event beforedeactivate
18413              * Fires before a tab is de-activated - can be used to do validation on a form.
18414              * @param {Roo.bootstrap.TabPanel} this
18415              * @return {Boolean} false if there is an error
18416             
18417          */
18418         'beforedeactivate': true
18419      });
18420     
18421     this.tabId = this.tabId || Roo.id();
18422   
18423 };
18424
18425 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18426     
18427     active: false,
18428     html: false,
18429     tabId: false,
18430     navId : false,
18431     href : '',
18432     
18433     getAutoCreate : function(){
18434         var cfg = {
18435             tag: 'div',
18436             // item is needed for carousel - not sure if it has any effect otherwise
18437             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18438             html: this.html || ''
18439         };
18440         
18441         if(this.active){
18442             cfg.cls += ' active';
18443         }
18444         
18445         if(this.tabId){
18446             cfg.tabId = this.tabId;
18447         }
18448         
18449         
18450         return cfg;
18451     },
18452     
18453     initEvents:  function()
18454     {
18455         var p = this.parent();
18456         
18457         this.navId = this.navId || p.navId;
18458         
18459         if (typeof(this.navId) != 'undefined') {
18460             // not really needed.. but just in case.. parent should be a NavGroup.
18461             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18462             
18463             tg.register(this);
18464             
18465             var i = tg.tabs.length - 1;
18466             
18467             if(this.active && tg.bullets > 0 && i < tg.bullets){
18468                 tg.setActiveBullet(i);
18469             }
18470         }
18471         
18472         this.el.on('click', this.onClick, this);
18473         
18474         if(Roo.isTouch){
18475             this.el.on("touchstart", this.onTouchStart, this);
18476             this.el.on("touchmove", this.onTouchMove, this);
18477             this.el.on("touchend", this.onTouchEnd, this);
18478         }
18479         
18480     },
18481     
18482     onRender : function(ct, position)
18483     {
18484         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18485     },
18486     
18487     setActive : function(state)
18488     {
18489         Roo.log("panel - set active " + this.tabId + "=" + state);
18490         
18491         this.active = state;
18492         if (!state) {
18493             this.el.removeClass('active');
18494             
18495         } else  if (!this.el.hasClass('active')) {
18496             this.el.addClass('active');
18497         }
18498         
18499         this.fireEvent('changed', this, state);
18500     },
18501     
18502     onClick : function(e)
18503     {
18504         e.preventDefault();
18505         
18506         if(!this.href.length){
18507             return;
18508         }
18509         
18510         window.location.href = this.href;
18511     },
18512     
18513     startX : 0,
18514     startY : 0,
18515     endX : 0,
18516     endY : 0,
18517     swiping : false,
18518     
18519     onTouchStart : function(e)
18520     {
18521         this.swiping = false;
18522         
18523         this.startX = e.browserEvent.touches[0].clientX;
18524         this.startY = e.browserEvent.touches[0].clientY;
18525     },
18526     
18527     onTouchMove : function(e)
18528     {
18529         this.swiping = true;
18530         
18531         this.endX = e.browserEvent.touches[0].clientX;
18532         this.endY = e.browserEvent.touches[0].clientY;
18533     },
18534     
18535     onTouchEnd : function(e)
18536     {
18537         if(!this.swiping){
18538             this.onClick(e);
18539             return;
18540         }
18541         
18542         var tabGroup = this.parent();
18543         
18544         if(this.endX > this.startX){ // swiping right
18545             tabGroup.showPanelPrev();
18546             return;
18547         }
18548         
18549         if(this.startX > this.endX){ // swiping left
18550             tabGroup.showPanelNext();
18551             return;
18552         }
18553     }
18554     
18555     
18556 });
18557  
18558
18559  
18560
18561  /*
18562  * - LGPL
18563  *
18564  * DateField
18565  * 
18566  */
18567
18568 /**
18569  * @class Roo.bootstrap.DateField
18570  * @extends Roo.bootstrap.Input
18571  * Bootstrap DateField class
18572  * @cfg {Number} weekStart default 0
18573  * @cfg {String} viewMode default empty, (months|years)
18574  * @cfg {String} minViewMode default empty, (months|years)
18575  * @cfg {Number} startDate default -Infinity
18576  * @cfg {Number} endDate default Infinity
18577  * @cfg {Boolean} todayHighlight default false
18578  * @cfg {Boolean} todayBtn default false
18579  * @cfg {Boolean} calendarWeeks default false
18580  * @cfg {Object} daysOfWeekDisabled default empty
18581  * @cfg {Boolean} singleMode default false (true | false)
18582  * 
18583  * @cfg {Boolean} keyboardNavigation default true
18584  * @cfg {String} language default en
18585  * 
18586  * @constructor
18587  * Create a new DateField
18588  * @param {Object} config The config object
18589  */
18590
18591 Roo.bootstrap.DateField = function(config){
18592     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18593      this.addEvents({
18594             /**
18595              * @event show
18596              * Fires when this field show.
18597              * @param {Roo.bootstrap.DateField} this
18598              * @param {Mixed} date The date value
18599              */
18600             show : true,
18601             /**
18602              * @event show
18603              * Fires when this field hide.
18604              * @param {Roo.bootstrap.DateField} this
18605              * @param {Mixed} date The date value
18606              */
18607             hide : true,
18608             /**
18609              * @event select
18610              * Fires when select a date.
18611              * @param {Roo.bootstrap.DateField} this
18612              * @param {Mixed} date The date value
18613              */
18614             select : true,
18615             /**
18616              * @event beforeselect
18617              * Fires when before select a date.
18618              * @param {Roo.bootstrap.DateField} this
18619              * @param {Mixed} date The date value
18620              */
18621             beforeselect : true
18622         });
18623 };
18624
18625 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18626     
18627     /**
18628      * @cfg {String} format
18629      * The default date format string which can be overriden for localization support.  The format must be
18630      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18631      */
18632     format : "m/d/y",
18633     /**
18634      * @cfg {String} altFormats
18635      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18636      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18637      */
18638     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18639     
18640     weekStart : 0,
18641     
18642     viewMode : '',
18643     
18644     minViewMode : '',
18645     
18646     todayHighlight : false,
18647     
18648     todayBtn: false,
18649     
18650     language: 'en',
18651     
18652     keyboardNavigation: true,
18653     
18654     calendarWeeks: false,
18655     
18656     startDate: -Infinity,
18657     
18658     endDate: Infinity,
18659     
18660     daysOfWeekDisabled: [],
18661     
18662     _events: [],
18663     
18664     singleMode : false,
18665     
18666     UTCDate: function()
18667     {
18668         return new Date(Date.UTC.apply(Date, arguments));
18669     },
18670     
18671     UTCToday: function()
18672     {
18673         var today = new Date();
18674         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18675     },
18676     
18677     getDate: function() {
18678             var d = this.getUTCDate();
18679             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18680     },
18681     
18682     getUTCDate: function() {
18683             return this.date;
18684     },
18685     
18686     setDate: function(d) {
18687             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18688     },
18689     
18690     setUTCDate: function(d) {
18691             this.date = d;
18692             this.setValue(this.formatDate(this.date));
18693     },
18694         
18695     onRender: function(ct, position)
18696     {
18697         
18698         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18699         
18700         this.language = this.language || 'en';
18701         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18702         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18703         
18704         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18705         this.format = this.format || 'm/d/y';
18706         this.isInline = false;
18707         this.isInput = true;
18708         this.component = this.el.select('.add-on', true).first() || false;
18709         this.component = (this.component && this.component.length === 0) ? false : this.component;
18710         this.hasInput = this.component && this.inputEl().length;
18711         
18712         if (typeof(this.minViewMode === 'string')) {
18713             switch (this.minViewMode) {
18714                 case 'months':
18715                     this.minViewMode = 1;
18716                     break;
18717                 case 'years':
18718                     this.minViewMode = 2;
18719                     break;
18720                 default:
18721                     this.minViewMode = 0;
18722                     break;
18723             }
18724         }
18725         
18726         if (typeof(this.viewMode === 'string')) {
18727             switch (this.viewMode) {
18728                 case 'months':
18729                     this.viewMode = 1;
18730                     break;
18731                 case 'years':
18732                     this.viewMode = 2;
18733                     break;
18734                 default:
18735                     this.viewMode = 0;
18736                     break;
18737             }
18738         }
18739                 
18740         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18741         
18742 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18743         
18744         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18745         
18746         this.picker().on('mousedown', this.onMousedown, this);
18747         this.picker().on('click', this.onClick, this);
18748         
18749         this.picker().addClass('datepicker-dropdown');
18750         
18751         this.startViewMode = this.viewMode;
18752         
18753         if(this.singleMode){
18754             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18755                 v.setVisibilityMode(Roo.Element.DISPLAY);
18756                 v.hide();
18757             });
18758             
18759             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18760                 v.setStyle('width', '189px');
18761             });
18762         }
18763         
18764         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18765             if(!this.calendarWeeks){
18766                 v.remove();
18767                 return;
18768             }
18769             
18770             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18771             v.attr('colspan', function(i, val){
18772                 return parseInt(val) + 1;
18773             });
18774         });
18775                         
18776         
18777         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18778         
18779         this.setStartDate(this.startDate);
18780         this.setEndDate(this.endDate);
18781         
18782         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18783         
18784         this.fillDow();
18785         this.fillMonths();
18786         this.update();
18787         this.showMode();
18788         
18789         if(this.isInline) {
18790             this.showPopup();
18791         }
18792     },
18793     
18794     picker : function()
18795     {
18796         return this.pickerEl;
18797 //        return this.el.select('.datepicker', true).first();
18798     },
18799     
18800     fillDow: function()
18801     {
18802         var dowCnt = this.weekStart;
18803         
18804         var dow = {
18805             tag: 'tr',
18806             cn: [
18807                 
18808             ]
18809         };
18810         
18811         if(this.calendarWeeks){
18812             dow.cn.push({
18813                 tag: 'th',
18814                 cls: 'cw',
18815                 html: '&nbsp;'
18816             })
18817         }
18818         
18819         while (dowCnt < this.weekStart + 7) {
18820             dow.cn.push({
18821                 tag: 'th',
18822                 cls: 'dow',
18823                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18824             });
18825         }
18826         
18827         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18828     },
18829     
18830     fillMonths: function()
18831     {    
18832         var i = 0;
18833         var months = this.picker().select('>.datepicker-months td', true).first();
18834         
18835         months.dom.innerHTML = '';
18836         
18837         while (i < 12) {
18838             var month = {
18839                 tag: 'span',
18840                 cls: 'month',
18841                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18842             };
18843             
18844             months.createChild(month);
18845         }
18846         
18847     },
18848     
18849     update: function()
18850     {
18851         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;
18852         
18853         if (this.date < this.startDate) {
18854             this.viewDate = new Date(this.startDate);
18855         } else if (this.date > this.endDate) {
18856             this.viewDate = new Date(this.endDate);
18857         } else {
18858             this.viewDate = new Date(this.date);
18859         }
18860         
18861         this.fill();
18862     },
18863     
18864     fill: function() 
18865     {
18866         var d = new Date(this.viewDate),
18867                 year = d.getUTCFullYear(),
18868                 month = d.getUTCMonth(),
18869                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18870                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18871                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18872                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18873                 currentDate = this.date && this.date.valueOf(),
18874                 today = this.UTCToday();
18875         
18876         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18877         
18878 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18879         
18880 //        this.picker.select('>tfoot th.today').
18881 //                                              .text(dates[this.language].today)
18882 //                                              .toggle(this.todayBtn !== false);
18883     
18884         this.updateNavArrows();
18885         this.fillMonths();
18886                                                 
18887         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18888         
18889         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18890          
18891         prevMonth.setUTCDate(day);
18892         
18893         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18894         
18895         var nextMonth = new Date(prevMonth);
18896         
18897         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18898         
18899         nextMonth = nextMonth.valueOf();
18900         
18901         var fillMonths = false;
18902         
18903         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18904         
18905         while(prevMonth.valueOf() <= nextMonth) {
18906             var clsName = '';
18907             
18908             if (prevMonth.getUTCDay() === this.weekStart) {
18909                 if(fillMonths){
18910                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18911                 }
18912                     
18913                 fillMonths = {
18914                     tag: 'tr',
18915                     cn: []
18916                 };
18917                 
18918                 if(this.calendarWeeks){
18919                     // ISO 8601: First week contains first thursday.
18920                     // ISO also states week starts on Monday, but we can be more abstract here.
18921                     var
18922                     // Start of current week: based on weekstart/current date
18923                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18924                     // Thursday of this week
18925                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18926                     // First Thursday of year, year from thursday
18927                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18928                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18929                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18930                     
18931                     fillMonths.cn.push({
18932                         tag: 'td',
18933                         cls: 'cw',
18934                         html: calWeek
18935                     });
18936                 }
18937             }
18938             
18939             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18940                 clsName += ' old';
18941             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18942                 clsName += ' new';
18943             }
18944             if (this.todayHighlight &&
18945                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18946                 prevMonth.getUTCMonth() == today.getMonth() &&
18947                 prevMonth.getUTCDate() == today.getDate()) {
18948                 clsName += ' today';
18949             }
18950             
18951             if (currentDate && prevMonth.valueOf() === currentDate) {
18952                 clsName += ' active';
18953             }
18954             
18955             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18956                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18957                     clsName += ' disabled';
18958             }
18959             
18960             fillMonths.cn.push({
18961                 tag: 'td',
18962                 cls: 'day ' + clsName,
18963                 html: prevMonth.getDate()
18964             });
18965             
18966             prevMonth.setDate(prevMonth.getDate()+1);
18967         }
18968           
18969         var currentYear = this.date && this.date.getUTCFullYear();
18970         var currentMonth = this.date && this.date.getUTCMonth();
18971         
18972         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18973         
18974         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18975             v.removeClass('active');
18976             
18977             if(currentYear === year && k === currentMonth){
18978                 v.addClass('active');
18979             }
18980             
18981             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18982                 v.addClass('disabled');
18983             }
18984             
18985         });
18986         
18987         
18988         year = parseInt(year/10, 10) * 10;
18989         
18990         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18991         
18992         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18993         
18994         year -= 1;
18995         for (var i = -1; i < 11; i++) {
18996             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18997                 tag: 'span',
18998                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18999                 html: year
19000             });
19001             
19002             year += 1;
19003         }
19004     },
19005     
19006     showMode: function(dir) 
19007     {
19008         if (dir) {
19009             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19010         }
19011         
19012         Roo.each(this.picker().select('>div',true).elements, function(v){
19013             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19014             v.hide();
19015         });
19016         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19017     },
19018     
19019     place: function()
19020     {
19021         if(this.isInline) {
19022             return;
19023         }
19024         
19025         this.picker().removeClass(['bottom', 'top']);
19026         
19027         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19028             /*
19029              * place to the top of element!
19030              *
19031              */
19032             
19033             this.picker().addClass('top');
19034             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19035             
19036             return;
19037         }
19038         
19039         this.picker().addClass('bottom');
19040         
19041         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19042     },
19043     
19044     parseDate : function(value)
19045     {
19046         if(!value || value instanceof Date){
19047             return value;
19048         }
19049         var v = Date.parseDate(value, this.format);
19050         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19051             v = Date.parseDate(value, 'Y-m-d');
19052         }
19053         if(!v && this.altFormats){
19054             if(!this.altFormatsArray){
19055                 this.altFormatsArray = this.altFormats.split("|");
19056             }
19057             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19058                 v = Date.parseDate(value, this.altFormatsArray[i]);
19059             }
19060         }
19061         return v;
19062     },
19063     
19064     formatDate : function(date, fmt)
19065     {   
19066         return (!date || !(date instanceof Date)) ?
19067         date : date.dateFormat(fmt || this.format);
19068     },
19069     
19070     onFocus : function()
19071     {
19072         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19073         this.showPopup();
19074     },
19075     
19076     onBlur : function()
19077     {
19078         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19079         
19080         var d = this.inputEl().getValue();
19081         
19082         this.setValue(d);
19083                 
19084         this.hidePopup();
19085     },
19086     
19087     showPopup : function()
19088     {
19089         this.picker().show();
19090         this.update();
19091         this.place();
19092         
19093         this.fireEvent('showpopup', this, this.date);
19094     },
19095     
19096     hidePopup : function()
19097     {
19098         if(this.isInline) {
19099             return;
19100         }
19101         this.picker().hide();
19102         this.viewMode = this.startViewMode;
19103         this.showMode();
19104         
19105         this.fireEvent('hidepopup', this, this.date);
19106         
19107     },
19108     
19109     onMousedown: function(e)
19110     {
19111         e.stopPropagation();
19112         e.preventDefault();
19113     },
19114     
19115     keyup: function(e)
19116     {
19117         Roo.bootstrap.DateField.superclass.keyup.call(this);
19118         this.update();
19119     },
19120
19121     setValue: function(v)
19122     {
19123         if(this.fireEvent('beforeselect', this, v) !== false){
19124             var d = new Date(this.parseDate(v) ).clearTime();
19125         
19126             if(isNaN(d.getTime())){
19127                 this.date = this.viewDate = '';
19128                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19129                 return;
19130             }
19131
19132             v = this.formatDate(d);
19133
19134             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19135
19136             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19137
19138             this.update();
19139
19140             this.fireEvent('select', this, this.date);
19141         }
19142     },
19143     
19144     getValue: function()
19145     {
19146         return this.formatDate(this.date);
19147     },
19148     
19149     fireKey: function(e)
19150     {
19151         if (!this.picker().isVisible()){
19152             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19153                 this.showPopup();
19154             }
19155             return;
19156         }
19157         
19158         var dateChanged = false,
19159         dir, day, month,
19160         newDate, newViewDate;
19161         
19162         switch(e.keyCode){
19163             case 27: // escape
19164                 this.hidePopup();
19165                 e.preventDefault();
19166                 break;
19167             case 37: // left
19168             case 39: // right
19169                 if (!this.keyboardNavigation) {
19170                     break;
19171                 }
19172                 dir = e.keyCode == 37 ? -1 : 1;
19173                 
19174                 if (e.ctrlKey){
19175                     newDate = this.moveYear(this.date, dir);
19176                     newViewDate = this.moveYear(this.viewDate, dir);
19177                 } else if (e.shiftKey){
19178                     newDate = this.moveMonth(this.date, dir);
19179                     newViewDate = this.moveMonth(this.viewDate, dir);
19180                 } else {
19181                     newDate = new Date(this.date);
19182                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19183                     newViewDate = new Date(this.viewDate);
19184                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19185                 }
19186                 if (this.dateWithinRange(newDate)){
19187                     this.date = newDate;
19188                     this.viewDate = newViewDate;
19189                     this.setValue(this.formatDate(this.date));
19190 //                    this.update();
19191                     e.preventDefault();
19192                     dateChanged = true;
19193                 }
19194                 break;
19195             case 38: // up
19196             case 40: // down
19197                 if (!this.keyboardNavigation) {
19198                     break;
19199                 }
19200                 dir = e.keyCode == 38 ? -1 : 1;
19201                 if (e.ctrlKey){
19202                     newDate = this.moveYear(this.date, dir);
19203                     newViewDate = this.moveYear(this.viewDate, dir);
19204                 } else if (e.shiftKey){
19205                     newDate = this.moveMonth(this.date, dir);
19206                     newViewDate = this.moveMonth(this.viewDate, dir);
19207                 } else {
19208                     newDate = new Date(this.date);
19209                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19210                     newViewDate = new Date(this.viewDate);
19211                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19212                 }
19213                 if (this.dateWithinRange(newDate)){
19214                     this.date = newDate;
19215                     this.viewDate = newViewDate;
19216                     this.setValue(this.formatDate(this.date));
19217 //                    this.update();
19218                     e.preventDefault();
19219                     dateChanged = true;
19220                 }
19221                 break;
19222             case 13: // enter
19223                 this.setValue(this.formatDate(this.date));
19224                 this.hidePopup();
19225                 e.preventDefault();
19226                 break;
19227             case 9: // tab
19228                 this.setValue(this.formatDate(this.date));
19229                 this.hidePopup();
19230                 break;
19231             case 16: // shift
19232             case 17: // ctrl
19233             case 18: // alt
19234                 break;
19235             default :
19236                 this.hidePopup();
19237                 
19238         }
19239     },
19240     
19241     
19242     onClick: function(e) 
19243     {
19244         e.stopPropagation();
19245         e.preventDefault();
19246         
19247         var target = e.getTarget();
19248         
19249         if(target.nodeName.toLowerCase() === 'i'){
19250             target = Roo.get(target).dom.parentNode;
19251         }
19252         
19253         var nodeName = target.nodeName;
19254         var className = target.className;
19255         var html = target.innerHTML;
19256         //Roo.log(nodeName);
19257         
19258         switch(nodeName.toLowerCase()) {
19259             case 'th':
19260                 switch(className) {
19261                     case 'switch':
19262                         this.showMode(1);
19263                         break;
19264                     case 'prev':
19265                     case 'next':
19266                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19267                         switch(this.viewMode){
19268                                 case 0:
19269                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19270                                         break;
19271                                 case 1:
19272                                 case 2:
19273                                         this.viewDate = this.moveYear(this.viewDate, dir);
19274                                         break;
19275                         }
19276                         this.fill();
19277                         break;
19278                     case 'today':
19279                         var date = new Date();
19280                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19281 //                        this.fill()
19282                         this.setValue(this.formatDate(this.date));
19283                         
19284                         this.hidePopup();
19285                         break;
19286                 }
19287                 break;
19288             case 'span':
19289                 if (className.indexOf('disabled') < 0) {
19290                     this.viewDate.setUTCDate(1);
19291                     if (className.indexOf('month') > -1) {
19292                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19293                     } else {
19294                         var year = parseInt(html, 10) || 0;
19295                         this.viewDate.setUTCFullYear(year);
19296                         
19297                     }
19298                     
19299                     if(this.singleMode){
19300                         this.setValue(this.formatDate(this.viewDate));
19301                         this.hidePopup();
19302                         return;
19303                     }
19304                     
19305                     this.showMode(-1);
19306                     this.fill();
19307                 }
19308                 break;
19309                 
19310             case 'td':
19311                 //Roo.log(className);
19312                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19313                     var day = parseInt(html, 10) || 1;
19314                     var year = this.viewDate.getUTCFullYear(),
19315                         month = this.viewDate.getUTCMonth();
19316
19317                     if (className.indexOf('old') > -1) {
19318                         if(month === 0 ){
19319                             month = 11;
19320                             year -= 1;
19321                         }else{
19322                             month -= 1;
19323                         }
19324                     } else if (className.indexOf('new') > -1) {
19325                         if (month == 11) {
19326                             month = 0;
19327                             year += 1;
19328                         } else {
19329                             month += 1;
19330                         }
19331                     }
19332                     //Roo.log([year,month,day]);
19333                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19334                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19335 //                    this.fill();
19336                     //Roo.log(this.formatDate(this.date));
19337                     this.setValue(this.formatDate(this.date));
19338                     this.hidePopup();
19339                 }
19340                 break;
19341         }
19342     },
19343     
19344     setStartDate: function(startDate)
19345     {
19346         this.startDate = startDate || -Infinity;
19347         if (this.startDate !== -Infinity) {
19348             this.startDate = this.parseDate(this.startDate);
19349         }
19350         this.update();
19351         this.updateNavArrows();
19352     },
19353
19354     setEndDate: function(endDate)
19355     {
19356         this.endDate = endDate || Infinity;
19357         if (this.endDate !== Infinity) {
19358             this.endDate = this.parseDate(this.endDate);
19359         }
19360         this.update();
19361         this.updateNavArrows();
19362     },
19363     
19364     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19365     {
19366         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19367         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19368             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19369         }
19370         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19371             return parseInt(d, 10);
19372         });
19373         this.update();
19374         this.updateNavArrows();
19375     },
19376     
19377     updateNavArrows: function() 
19378     {
19379         if(this.singleMode){
19380             return;
19381         }
19382         
19383         var d = new Date(this.viewDate),
19384         year = d.getUTCFullYear(),
19385         month = d.getUTCMonth();
19386         
19387         Roo.each(this.picker().select('.prev', true).elements, function(v){
19388             v.show();
19389             switch (this.viewMode) {
19390                 case 0:
19391
19392                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19393                         v.hide();
19394                     }
19395                     break;
19396                 case 1:
19397                 case 2:
19398                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19399                         v.hide();
19400                     }
19401                     break;
19402             }
19403         });
19404         
19405         Roo.each(this.picker().select('.next', true).elements, function(v){
19406             v.show();
19407             switch (this.viewMode) {
19408                 case 0:
19409
19410                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19411                         v.hide();
19412                     }
19413                     break;
19414                 case 1:
19415                 case 2:
19416                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19417                         v.hide();
19418                     }
19419                     break;
19420             }
19421         })
19422     },
19423     
19424     moveMonth: function(date, dir)
19425     {
19426         if (!dir) {
19427             return date;
19428         }
19429         var new_date = new Date(date.valueOf()),
19430         day = new_date.getUTCDate(),
19431         month = new_date.getUTCMonth(),
19432         mag = Math.abs(dir),
19433         new_month, test;
19434         dir = dir > 0 ? 1 : -1;
19435         if (mag == 1){
19436             test = dir == -1
19437             // If going back one month, make sure month is not current month
19438             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19439             ? function(){
19440                 return new_date.getUTCMonth() == month;
19441             }
19442             // If going forward one month, make sure month is as expected
19443             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19444             : function(){
19445                 return new_date.getUTCMonth() != new_month;
19446             };
19447             new_month = month + dir;
19448             new_date.setUTCMonth(new_month);
19449             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19450             if (new_month < 0 || new_month > 11) {
19451                 new_month = (new_month + 12) % 12;
19452             }
19453         } else {
19454             // For magnitudes >1, move one month at a time...
19455             for (var i=0; i<mag; i++) {
19456                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19457                 new_date = this.moveMonth(new_date, dir);
19458             }
19459             // ...then reset the day, keeping it in the new month
19460             new_month = new_date.getUTCMonth();
19461             new_date.setUTCDate(day);
19462             test = function(){
19463                 return new_month != new_date.getUTCMonth();
19464             };
19465         }
19466         // Common date-resetting loop -- if date is beyond end of month, make it
19467         // end of month
19468         while (test()){
19469             new_date.setUTCDate(--day);
19470             new_date.setUTCMonth(new_month);
19471         }
19472         return new_date;
19473     },
19474
19475     moveYear: function(date, dir)
19476     {
19477         return this.moveMonth(date, dir*12);
19478     },
19479
19480     dateWithinRange: function(date)
19481     {
19482         return date >= this.startDate && date <= this.endDate;
19483     },
19484
19485     
19486     remove: function() 
19487     {
19488         this.picker().remove();
19489     },
19490     
19491     validateValue : function(value)
19492     {
19493         if(this.getVisibilityEl().hasClass('hidden')){
19494             return true;
19495         }
19496         
19497         if(value.length < 1)  {
19498             if(this.allowBlank){
19499                 return true;
19500             }
19501             return false;
19502         }
19503         
19504         if(value.length < this.minLength){
19505             return false;
19506         }
19507         if(value.length > this.maxLength){
19508             return false;
19509         }
19510         if(this.vtype){
19511             var vt = Roo.form.VTypes;
19512             if(!vt[this.vtype](value, this)){
19513                 return false;
19514             }
19515         }
19516         if(typeof this.validator == "function"){
19517             var msg = this.validator(value);
19518             if(msg !== true){
19519                 return false;
19520             }
19521         }
19522         
19523         if(this.regex && !this.regex.test(value)){
19524             return false;
19525         }
19526         
19527         if(typeof(this.parseDate(value)) == 'undefined'){
19528             return false;
19529         }
19530         
19531         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19532             return false;
19533         }      
19534         
19535         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19536             return false;
19537         } 
19538         
19539         
19540         return true;
19541     },
19542     
19543     reset : function()
19544     {
19545         this.date = this.viewDate = '';
19546         
19547         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19548     }
19549    
19550 });
19551
19552 Roo.apply(Roo.bootstrap.DateField,  {
19553     
19554     head : {
19555         tag: 'thead',
19556         cn: [
19557         {
19558             tag: 'tr',
19559             cn: [
19560             {
19561                 tag: 'th',
19562                 cls: 'prev',
19563                 html: '<i class="fa fa-arrow-left"/>'
19564             },
19565             {
19566                 tag: 'th',
19567                 cls: 'switch',
19568                 colspan: '5'
19569             },
19570             {
19571                 tag: 'th',
19572                 cls: 'next',
19573                 html: '<i class="fa fa-arrow-right"/>'
19574             }
19575
19576             ]
19577         }
19578         ]
19579     },
19580     
19581     content : {
19582         tag: 'tbody',
19583         cn: [
19584         {
19585             tag: 'tr',
19586             cn: [
19587             {
19588                 tag: 'td',
19589                 colspan: '7'
19590             }
19591             ]
19592         }
19593         ]
19594     },
19595     
19596     footer : {
19597         tag: 'tfoot',
19598         cn: [
19599         {
19600             tag: 'tr',
19601             cn: [
19602             {
19603                 tag: 'th',
19604                 colspan: '7',
19605                 cls: 'today'
19606             }
19607                     
19608             ]
19609         }
19610         ]
19611     },
19612     
19613     dates:{
19614         en: {
19615             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19616             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19617             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19618             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19619             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19620             today: "Today"
19621         }
19622     },
19623     
19624     modes: [
19625     {
19626         clsName: 'days',
19627         navFnc: 'Month',
19628         navStep: 1
19629     },
19630     {
19631         clsName: 'months',
19632         navFnc: 'FullYear',
19633         navStep: 1
19634     },
19635     {
19636         clsName: 'years',
19637         navFnc: 'FullYear',
19638         navStep: 10
19639     }]
19640 });
19641
19642 Roo.apply(Roo.bootstrap.DateField,  {
19643   
19644     template : {
19645         tag: 'div',
19646         cls: 'datepicker dropdown-menu roo-dynamic',
19647         cn: [
19648         {
19649             tag: 'div',
19650             cls: 'datepicker-days',
19651             cn: [
19652             {
19653                 tag: 'table',
19654                 cls: 'table-condensed',
19655                 cn:[
19656                 Roo.bootstrap.DateField.head,
19657                 {
19658                     tag: 'tbody'
19659                 },
19660                 Roo.bootstrap.DateField.footer
19661                 ]
19662             }
19663             ]
19664         },
19665         {
19666             tag: 'div',
19667             cls: 'datepicker-months',
19668             cn: [
19669             {
19670                 tag: 'table',
19671                 cls: 'table-condensed',
19672                 cn:[
19673                 Roo.bootstrap.DateField.head,
19674                 Roo.bootstrap.DateField.content,
19675                 Roo.bootstrap.DateField.footer
19676                 ]
19677             }
19678             ]
19679         },
19680         {
19681             tag: 'div',
19682             cls: 'datepicker-years',
19683             cn: [
19684             {
19685                 tag: 'table',
19686                 cls: 'table-condensed',
19687                 cn:[
19688                 Roo.bootstrap.DateField.head,
19689                 Roo.bootstrap.DateField.content,
19690                 Roo.bootstrap.DateField.footer
19691                 ]
19692             }
19693             ]
19694         }
19695         ]
19696     }
19697 });
19698
19699  
19700
19701  /*
19702  * - LGPL
19703  *
19704  * TimeField
19705  * 
19706  */
19707
19708 /**
19709  * @class Roo.bootstrap.TimeField
19710  * @extends Roo.bootstrap.Input
19711  * Bootstrap DateField class
19712  * 
19713  * 
19714  * @constructor
19715  * Create a new TimeField
19716  * @param {Object} config The config object
19717  */
19718
19719 Roo.bootstrap.TimeField = function(config){
19720     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19721     this.addEvents({
19722             /**
19723              * @event show
19724              * Fires when this field show.
19725              * @param {Roo.bootstrap.DateField} thisthis
19726              * @param {Mixed} date The date value
19727              */
19728             show : true,
19729             /**
19730              * @event show
19731              * Fires when this field hide.
19732              * @param {Roo.bootstrap.DateField} this
19733              * @param {Mixed} date The date value
19734              */
19735             hide : true,
19736             /**
19737              * @event select
19738              * Fires when select a date.
19739              * @param {Roo.bootstrap.DateField} this
19740              * @param {Mixed} date The date value
19741              */
19742             select : true
19743         });
19744 };
19745
19746 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19747     
19748     /**
19749      * @cfg {String} format
19750      * The default time format string which can be overriden for localization support.  The format must be
19751      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19752      */
19753     format : "H:i",
19754        
19755     onRender: function(ct, position)
19756     {
19757         
19758         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19759                 
19760         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19761         
19762         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19763         
19764         this.pop = this.picker().select('>.datepicker-time',true).first();
19765         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19766         
19767         this.picker().on('mousedown', this.onMousedown, this);
19768         this.picker().on('click', this.onClick, this);
19769         
19770         this.picker().addClass('datepicker-dropdown');
19771     
19772         this.fillTime();
19773         this.update();
19774             
19775         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19776         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19777         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19778         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19779         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19780         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19781
19782     },
19783     
19784     fireKey: function(e){
19785         if (!this.picker().isVisible()){
19786             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19787                 this.show();
19788             }
19789             return;
19790         }
19791
19792         e.preventDefault();
19793         
19794         switch(e.keyCode){
19795             case 27: // escape
19796                 this.hide();
19797                 break;
19798             case 37: // left
19799             case 39: // right
19800                 this.onTogglePeriod();
19801                 break;
19802             case 38: // up
19803                 this.onIncrementMinutes();
19804                 break;
19805             case 40: // down
19806                 this.onDecrementMinutes();
19807                 break;
19808             case 13: // enter
19809             case 9: // tab
19810                 this.setTime();
19811                 break;
19812         }
19813     },
19814     
19815     onClick: function(e) {
19816         e.stopPropagation();
19817         e.preventDefault();
19818     },
19819     
19820     picker : function()
19821     {
19822         return this.el.select('.datepicker', true).first();
19823     },
19824     
19825     fillTime: function()
19826     {    
19827         var time = this.pop.select('tbody', true).first();
19828         
19829         time.dom.innerHTML = '';
19830         
19831         time.createChild({
19832             tag: 'tr',
19833             cn: [
19834                 {
19835                     tag: 'td',
19836                     cn: [
19837                         {
19838                             tag: 'a',
19839                             href: '#',
19840                             cls: 'btn',
19841                             cn: [
19842                                 {
19843                                     tag: 'span',
19844                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19845                                 }
19846                             ]
19847                         } 
19848                     ]
19849                 },
19850                 {
19851                     tag: 'td',
19852                     cls: 'separator'
19853                 },
19854                 {
19855                     tag: 'td',
19856                     cn: [
19857                         {
19858                             tag: 'a',
19859                             href: '#',
19860                             cls: 'btn',
19861                             cn: [
19862                                 {
19863                                     tag: 'span',
19864                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19865                                 }
19866                             ]
19867                         }
19868                     ]
19869                 },
19870                 {
19871                     tag: 'td',
19872                     cls: 'separator'
19873                 }
19874             ]
19875         });
19876         
19877         time.createChild({
19878             tag: 'tr',
19879             cn: [
19880                 {
19881                     tag: 'td',
19882                     cn: [
19883                         {
19884                             tag: 'span',
19885                             cls: 'timepicker-hour',
19886                             html: '00'
19887                         }  
19888                     ]
19889                 },
19890                 {
19891                     tag: 'td',
19892                     cls: 'separator',
19893                     html: ':'
19894                 },
19895                 {
19896                     tag: 'td',
19897                     cn: [
19898                         {
19899                             tag: 'span',
19900                             cls: 'timepicker-minute',
19901                             html: '00'
19902                         }  
19903                     ]
19904                 },
19905                 {
19906                     tag: 'td',
19907                     cls: 'separator'
19908                 },
19909                 {
19910                     tag: 'td',
19911                     cn: [
19912                         {
19913                             tag: 'button',
19914                             type: 'button',
19915                             cls: 'btn btn-primary period',
19916                             html: 'AM'
19917                             
19918                         }
19919                     ]
19920                 }
19921             ]
19922         });
19923         
19924         time.createChild({
19925             tag: 'tr',
19926             cn: [
19927                 {
19928                     tag: 'td',
19929                     cn: [
19930                         {
19931                             tag: 'a',
19932                             href: '#',
19933                             cls: 'btn',
19934                             cn: [
19935                                 {
19936                                     tag: 'span',
19937                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19938                                 }
19939                             ]
19940                         }
19941                     ]
19942                 },
19943                 {
19944                     tag: 'td',
19945                     cls: 'separator'
19946                 },
19947                 {
19948                     tag: 'td',
19949                     cn: [
19950                         {
19951                             tag: 'a',
19952                             href: '#',
19953                             cls: 'btn',
19954                             cn: [
19955                                 {
19956                                     tag: 'span',
19957                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19958                                 }
19959                             ]
19960                         }
19961                     ]
19962                 },
19963                 {
19964                     tag: 'td',
19965                     cls: 'separator'
19966                 }
19967             ]
19968         });
19969         
19970     },
19971     
19972     update: function()
19973     {
19974         
19975         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19976         
19977         this.fill();
19978     },
19979     
19980     fill: function() 
19981     {
19982         var hours = this.time.getHours();
19983         var minutes = this.time.getMinutes();
19984         var period = 'AM';
19985         
19986         if(hours > 11){
19987             period = 'PM';
19988         }
19989         
19990         if(hours == 0){
19991             hours = 12;
19992         }
19993         
19994         
19995         if(hours > 12){
19996             hours = hours - 12;
19997         }
19998         
19999         if(hours < 10){
20000             hours = '0' + hours;
20001         }
20002         
20003         if(minutes < 10){
20004             minutes = '0' + minutes;
20005         }
20006         
20007         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20008         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20009         this.pop.select('button', true).first().dom.innerHTML = period;
20010         
20011     },
20012     
20013     place: function()
20014     {   
20015         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20016         
20017         var cls = ['bottom'];
20018         
20019         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20020             cls.pop();
20021             cls.push('top');
20022         }
20023         
20024         cls.push('right');
20025         
20026         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20027             cls.pop();
20028             cls.push('left');
20029         }
20030         
20031         this.picker().addClass(cls.join('-'));
20032         
20033         var _this = this;
20034         
20035         Roo.each(cls, function(c){
20036             if(c == 'bottom'){
20037                 _this.picker().setTop(_this.inputEl().getHeight());
20038                 return;
20039             }
20040             if(c == 'top'){
20041                 _this.picker().setTop(0 - _this.picker().getHeight());
20042                 return;
20043             }
20044             
20045             if(c == 'left'){
20046                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20047                 return;
20048             }
20049             if(c == 'right'){
20050                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20051                 return;
20052             }
20053         });
20054         
20055     },
20056   
20057     onFocus : function()
20058     {
20059         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20060         this.show();
20061     },
20062     
20063     onBlur : function()
20064     {
20065         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20066         this.hide();
20067     },
20068     
20069     show : function()
20070     {
20071         this.picker().show();
20072         this.pop.show();
20073         this.update();
20074         this.place();
20075         
20076         this.fireEvent('show', this, this.date);
20077     },
20078     
20079     hide : function()
20080     {
20081         this.picker().hide();
20082         this.pop.hide();
20083         
20084         this.fireEvent('hide', this, this.date);
20085     },
20086     
20087     setTime : function()
20088     {
20089         this.hide();
20090         this.setValue(this.time.format(this.format));
20091         
20092         this.fireEvent('select', this, this.date);
20093         
20094         
20095     },
20096     
20097     onMousedown: function(e){
20098         e.stopPropagation();
20099         e.preventDefault();
20100     },
20101     
20102     onIncrementHours: function()
20103     {
20104         Roo.log('onIncrementHours');
20105         this.time = this.time.add(Date.HOUR, 1);
20106         this.update();
20107         
20108     },
20109     
20110     onDecrementHours: function()
20111     {
20112         Roo.log('onDecrementHours');
20113         this.time = this.time.add(Date.HOUR, -1);
20114         this.update();
20115     },
20116     
20117     onIncrementMinutes: function()
20118     {
20119         Roo.log('onIncrementMinutes');
20120         this.time = this.time.add(Date.MINUTE, 1);
20121         this.update();
20122     },
20123     
20124     onDecrementMinutes: function()
20125     {
20126         Roo.log('onDecrementMinutes');
20127         this.time = this.time.add(Date.MINUTE, -1);
20128         this.update();
20129     },
20130     
20131     onTogglePeriod: function()
20132     {
20133         Roo.log('onTogglePeriod');
20134         this.time = this.time.add(Date.HOUR, 12);
20135         this.update();
20136     }
20137     
20138    
20139 });
20140
20141 Roo.apply(Roo.bootstrap.TimeField,  {
20142     
20143     content : {
20144         tag: 'tbody',
20145         cn: [
20146             {
20147                 tag: 'tr',
20148                 cn: [
20149                 {
20150                     tag: 'td',
20151                     colspan: '7'
20152                 }
20153                 ]
20154             }
20155         ]
20156     },
20157     
20158     footer : {
20159         tag: 'tfoot',
20160         cn: [
20161             {
20162                 tag: 'tr',
20163                 cn: [
20164                 {
20165                     tag: 'th',
20166                     colspan: '7',
20167                     cls: '',
20168                     cn: [
20169                         {
20170                             tag: 'button',
20171                             cls: 'btn btn-info ok',
20172                             html: 'OK'
20173                         }
20174                     ]
20175                 }
20176
20177                 ]
20178             }
20179         ]
20180     }
20181 });
20182
20183 Roo.apply(Roo.bootstrap.TimeField,  {
20184   
20185     template : {
20186         tag: 'div',
20187         cls: 'datepicker dropdown-menu',
20188         cn: [
20189             {
20190                 tag: 'div',
20191                 cls: 'datepicker-time',
20192                 cn: [
20193                 {
20194                     tag: 'table',
20195                     cls: 'table-condensed',
20196                     cn:[
20197                     Roo.bootstrap.TimeField.content,
20198                     Roo.bootstrap.TimeField.footer
20199                     ]
20200                 }
20201                 ]
20202             }
20203         ]
20204     }
20205 });
20206
20207  
20208
20209  /*
20210  * - LGPL
20211  *
20212  * MonthField
20213  * 
20214  */
20215
20216 /**
20217  * @class Roo.bootstrap.MonthField
20218  * @extends Roo.bootstrap.Input
20219  * Bootstrap MonthField class
20220  * 
20221  * @cfg {String} language default en
20222  * 
20223  * @constructor
20224  * Create a new MonthField
20225  * @param {Object} config The config object
20226  */
20227
20228 Roo.bootstrap.MonthField = function(config){
20229     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20230     
20231     this.addEvents({
20232         /**
20233          * @event show
20234          * Fires when this field show.
20235          * @param {Roo.bootstrap.MonthField} this
20236          * @param {Mixed} date The date value
20237          */
20238         show : true,
20239         /**
20240          * @event show
20241          * Fires when this field hide.
20242          * @param {Roo.bootstrap.MonthField} this
20243          * @param {Mixed} date The date value
20244          */
20245         hide : true,
20246         /**
20247          * @event select
20248          * Fires when select a date.
20249          * @param {Roo.bootstrap.MonthField} this
20250          * @param {String} oldvalue The old value
20251          * @param {String} newvalue The new value
20252          */
20253         select : true
20254     });
20255 };
20256
20257 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20258     
20259     onRender: function(ct, position)
20260     {
20261         
20262         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20263         
20264         this.language = this.language || 'en';
20265         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20266         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20267         
20268         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20269         this.isInline = false;
20270         this.isInput = true;
20271         this.component = this.el.select('.add-on', true).first() || false;
20272         this.component = (this.component && this.component.length === 0) ? false : this.component;
20273         this.hasInput = this.component && this.inputEL().length;
20274         
20275         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20276         
20277         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20278         
20279         this.picker().on('mousedown', this.onMousedown, this);
20280         this.picker().on('click', this.onClick, this);
20281         
20282         this.picker().addClass('datepicker-dropdown');
20283         
20284         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20285             v.setStyle('width', '189px');
20286         });
20287         
20288         this.fillMonths();
20289         
20290         this.update();
20291         
20292         if(this.isInline) {
20293             this.show();
20294         }
20295         
20296     },
20297     
20298     setValue: function(v, suppressEvent)
20299     {   
20300         var o = this.getValue();
20301         
20302         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20303         
20304         this.update();
20305
20306         if(suppressEvent !== true){
20307             this.fireEvent('select', this, o, v);
20308         }
20309         
20310     },
20311     
20312     getValue: function()
20313     {
20314         return this.value;
20315     },
20316     
20317     onClick: function(e) 
20318     {
20319         e.stopPropagation();
20320         e.preventDefault();
20321         
20322         var target = e.getTarget();
20323         
20324         if(target.nodeName.toLowerCase() === 'i'){
20325             target = Roo.get(target).dom.parentNode;
20326         }
20327         
20328         var nodeName = target.nodeName;
20329         var className = target.className;
20330         var html = target.innerHTML;
20331         
20332         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20333             return;
20334         }
20335         
20336         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20337         
20338         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20339         
20340         this.hide();
20341                         
20342     },
20343     
20344     picker : function()
20345     {
20346         return this.pickerEl;
20347     },
20348     
20349     fillMonths: function()
20350     {    
20351         var i = 0;
20352         var months = this.picker().select('>.datepicker-months td', true).first();
20353         
20354         months.dom.innerHTML = '';
20355         
20356         while (i < 12) {
20357             var month = {
20358                 tag: 'span',
20359                 cls: 'month',
20360                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20361             };
20362             
20363             months.createChild(month);
20364         }
20365         
20366     },
20367     
20368     update: function()
20369     {
20370         var _this = this;
20371         
20372         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20373             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20374         }
20375         
20376         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20377             e.removeClass('active');
20378             
20379             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20380                 e.addClass('active');
20381             }
20382         })
20383     },
20384     
20385     place: function()
20386     {
20387         if(this.isInline) {
20388             return;
20389         }
20390         
20391         this.picker().removeClass(['bottom', 'top']);
20392         
20393         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20394             /*
20395              * place to the top of element!
20396              *
20397              */
20398             
20399             this.picker().addClass('top');
20400             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20401             
20402             return;
20403         }
20404         
20405         this.picker().addClass('bottom');
20406         
20407         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20408     },
20409     
20410     onFocus : function()
20411     {
20412         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20413         this.show();
20414     },
20415     
20416     onBlur : function()
20417     {
20418         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20419         
20420         var d = this.inputEl().getValue();
20421         
20422         this.setValue(d);
20423                 
20424         this.hide();
20425     },
20426     
20427     show : function()
20428     {
20429         this.picker().show();
20430         this.picker().select('>.datepicker-months', true).first().show();
20431         this.update();
20432         this.place();
20433         
20434         this.fireEvent('show', this, this.date);
20435     },
20436     
20437     hide : function()
20438     {
20439         if(this.isInline) {
20440             return;
20441         }
20442         this.picker().hide();
20443         this.fireEvent('hide', this, this.date);
20444         
20445     },
20446     
20447     onMousedown: function(e)
20448     {
20449         e.stopPropagation();
20450         e.preventDefault();
20451     },
20452     
20453     keyup: function(e)
20454     {
20455         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20456         this.update();
20457     },
20458
20459     fireKey: function(e)
20460     {
20461         if (!this.picker().isVisible()){
20462             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20463                 this.show();
20464             }
20465             return;
20466         }
20467         
20468         var dir;
20469         
20470         switch(e.keyCode){
20471             case 27: // escape
20472                 this.hide();
20473                 e.preventDefault();
20474                 break;
20475             case 37: // left
20476             case 39: // right
20477                 dir = e.keyCode == 37 ? -1 : 1;
20478                 
20479                 this.vIndex = this.vIndex + dir;
20480                 
20481                 if(this.vIndex < 0){
20482                     this.vIndex = 0;
20483                 }
20484                 
20485                 if(this.vIndex > 11){
20486                     this.vIndex = 11;
20487                 }
20488                 
20489                 if(isNaN(this.vIndex)){
20490                     this.vIndex = 0;
20491                 }
20492                 
20493                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20494                 
20495                 break;
20496             case 38: // up
20497             case 40: // down
20498                 
20499                 dir = e.keyCode == 38 ? -1 : 1;
20500                 
20501                 this.vIndex = this.vIndex + dir * 4;
20502                 
20503                 if(this.vIndex < 0){
20504                     this.vIndex = 0;
20505                 }
20506                 
20507                 if(this.vIndex > 11){
20508                     this.vIndex = 11;
20509                 }
20510                 
20511                 if(isNaN(this.vIndex)){
20512                     this.vIndex = 0;
20513                 }
20514                 
20515                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20516                 break;
20517                 
20518             case 13: // enter
20519                 
20520                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20521                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20522                 }
20523                 
20524                 this.hide();
20525                 e.preventDefault();
20526                 break;
20527             case 9: // tab
20528                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20529                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20530                 }
20531                 this.hide();
20532                 break;
20533             case 16: // shift
20534             case 17: // ctrl
20535             case 18: // alt
20536                 break;
20537             default :
20538                 this.hide();
20539                 
20540         }
20541     },
20542     
20543     remove: function() 
20544     {
20545         this.picker().remove();
20546     }
20547    
20548 });
20549
20550 Roo.apply(Roo.bootstrap.MonthField,  {
20551     
20552     content : {
20553         tag: 'tbody',
20554         cn: [
20555         {
20556             tag: 'tr',
20557             cn: [
20558             {
20559                 tag: 'td',
20560                 colspan: '7'
20561             }
20562             ]
20563         }
20564         ]
20565     },
20566     
20567     dates:{
20568         en: {
20569             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20570             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20571         }
20572     }
20573 });
20574
20575 Roo.apply(Roo.bootstrap.MonthField,  {
20576   
20577     template : {
20578         tag: 'div',
20579         cls: 'datepicker dropdown-menu roo-dynamic',
20580         cn: [
20581             {
20582                 tag: 'div',
20583                 cls: 'datepicker-months',
20584                 cn: [
20585                 {
20586                     tag: 'table',
20587                     cls: 'table-condensed',
20588                     cn:[
20589                         Roo.bootstrap.DateField.content
20590                     ]
20591                 }
20592                 ]
20593             }
20594         ]
20595     }
20596 });
20597
20598  
20599
20600  
20601  /*
20602  * - LGPL
20603  *
20604  * CheckBox
20605  * 
20606  */
20607
20608 /**
20609  * @class Roo.bootstrap.CheckBox
20610  * @extends Roo.bootstrap.Input
20611  * Bootstrap CheckBox class
20612  * 
20613  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20614  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20615  * @cfg {String} boxLabel The text that appears beside the checkbox
20616  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20617  * @cfg {Boolean} checked initnal the element
20618  * @cfg {Boolean} inline inline the element (default false)
20619  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20620  * @cfg {String} tooltip label tooltip
20621  * 
20622  * @constructor
20623  * Create a new CheckBox
20624  * @param {Object} config The config object
20625  */
20626
20627 Roo.bootstrap.CheckBox = function(config){
20628     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20629    
20630     this.addEvents({
20631         /**
20632         * @event check
20633         * Fires when the element is checked or unchecked.
20634         * @param {Roo.bootstrap.CheckBox} this This input
20635         * @param {Boolean} checked The new checked value
20636         */
20637        check : true,
20638        /**
20639         * @event click
20640         * Fires when the element is click.
20641         * @param {Roo.bootstrap.CheckBox} this This input
20642         */
20643        click : true
20644     });
20645     
20646 };
20647
20648 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20649   
20650     inputType: 'checkbox',
20651     inputValue: 1,
20652     valueOff: 0,
20653     boxLabel: false,
20654     checked: false,
20655     weight : false,
20656     inline: false,
20657     tooltip : '',
20658     
20659     getAutoCreate : function()
20660     {
20661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20662         
20663         var id = Roo.id();
20664         
20665         var cfg = {};
20666         
20667         cfg.cls = 'form-group ' + this.inputType; //input-group
20668         
20669         if(this.inline){
20670             cfg.cls += ' ' + this.inputType + '-inline';
20671         }
20672         
20673         var input =  {
20674             tag: 'input',
20675             id : id,
20676             type : this.inputType,
20677             value : this.inputValue,
20678             cls : 'roo-' + this.inputType, //'form-box',
20679             placeholder : this.placeholder || ''
20680             
20681         };
20682         
20683         if(this.inputType != 'radio'){
20684             var hidden =  {
20685                 tag: 'input',
20686                 type : 'hidden',
20687                 cls : 'roo-hidden-value',
20688                 value : this.checked ? this.inputValue : this.valueOff
20689             };
20690         }
20691         
20692             
20693         if (this.weight) { // Validity check?
20694             cfg.cls += " " + this.inputType + "-" + this.weight;
20695         }
20696         
20697         if (this.disabled) {
20698             input.disabled=true;
20699         }
20700         
20701         if(this.checked){
20702             input.checked = this.checked;
20703         }
20704         
20705         if (this.name) {
20706             
20707             input.name = this.name;
20708             
20709             if(this.inputType != 'radio'){
20710                 hidden.name = this.name;
20711                 input.name = '_hidden_' + this.name;
20712             }
20713         }
20714         
20715         if (this.size) {
20716             input.cls += ' input-' + this.size;
20717         }
20718         
20719         var settings=this;
20720         
20721         ['xs','sm','md','lg'].map(function(size){
20722             if (settings[size]) {
20723                 cfg.cls += ' col-' + size + '-' + settings[size];
20724             }
20725         });
20726         
20727         var inputblock = input;
20728          
20729         if (this.before || this.after) {
20730             
20731             inputblock = {
20732                 cls : 'input-group',
20733                 cn :  [] 
20734             };
20735             
20736             if (this.before) {
20737                 inputblock.cn.push({
20738                     tag :'span',
20739                     cls : 'input-group-addon',
20740                     html : this.before
20741                 });
20742             }
20743             
20744             inputblock.cn.push(input);
20745             
20746             if(this.inputType != 'radio'){
20747                 inputblock.cn.push(hidden);
20748             }
20749             
20750             if (this.after) {
20751                 inputblock.cn.push({
20752                     tag :'span',
20753                     cls : 'input-group-addon',
20754                     html : this.after
20755                 });
20756             }
20757             
20758         }
20759         
20760         if (align ==='left' && this.fieldLabel.length) {
20761 //                Roo.log("left and has label");
20762             cfg.cn = [
20763                 {
20764                     tag: 'label',
20765                     'for' :  id,
20766                     cls : 'control-label',
20767                     html : this.fieldLabel
20768                 },
20769                 {
20770                     cls : "", 
20771                     cn: [
20772                         inputblock
20773                     ]
20774                 }
20775             ];
20776             
20777             if(this.labelWidth > 12){
20778                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20779             }
20780             
20781             if(this.labelWidth < 13 && this.labelmd == 0){
20782                 this.labelmd = this.labelWidth;
20783             }
20784             
20785             if(this.labellg > 0){
20786                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20787                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20788             }
20789             
20790             if(this.labelmd > 0){
20791                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20792                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20793             }
20794             
20795             if(this.labelsm > 0){
20796                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20797                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20798             }
20799             
20800             if(this.labelxs > 0){
20801                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20802                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20803             }
20804             
20805         } else if ( this.fieldLabel.length) {
20806 //                Roo.log(" label");
20807                 cfg.cn = [
20808                    
20809                     {
20810                         tag: this.boxLabel ? 'span' : 'label',
20811                         'for': id,
20812                         cls: 'control-label box-input-label',
20813                         //cls : 'input-group-addon',
20814                         html : this.fieldLabel
20815                     },
20816                     
20817                     inputblock
20818                     
20819                 ];
20820
20821         } else {
20822             
20823 //                Roo.log(" no label && no align");
20824                 cfg.cn = [  inputblock ] ;
20825                 
20826                 
20827         }
20828         
20829         if(this.boxLabel){
20830              var boxLabelCfg = {
20831                 tag: 'label',
20832                 //'for': id, // box label is handled by onclick - so no for...
20833                 cls: 'box-label',
20834                 html: this.boxLabel
20835             };
20836             
20837             if(this.tooltip){
20838                 boxLabelCfg.tooltip = this.tooltip;
20839             }
20840              
20841             cfg.cn.push(boxLabelCfg);
20842         }
20843         
20844         if(this.inputType != 'radio'){
20845             cfg.cn.push(hidden);
20846         }
20847         
20848         return cfg;
20849         
20850     },
20851     
20852     /**
20853      * return the real input element.
20854      */
20855     inputEl: function ()
20856     {
20857         return this.el.select('input.roo-' + this.inputType,true).first();
20858     },
20859     hiddenEl: function ()
20860     {
20861         return this.el.select('input.roo-hidden-value',true).first();
20862     },
20863     
20864     labelEl: function()
20865     {
20866         return this.el.select('label.control-label',true).first();
20867     },
20868     /* depricated... */
20869     
20870     label: function()
20871     {
20872         return this.labelEl();
20873     },
20874     
20875     boxLabelEl: function()
20876     {
20877         return this.el.select('label.box-label',true).first();
20878     },
20879     
20880     initEvents : function()
20881     {
20882 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20883         
20884         this.inputEl().on('click', this.onClick,  this);
20885         
20886         if (this.boxLabel) { 
20887             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20888         }
20889         
20890         this.startValue = this.getValue();
20891         
20892         if(this.groupId){
20893             Roo.bootstrap.CheckBox.register(this);
20894         }
20895     },
20896     
20897     onClick : function(e)
20898     {   
20899         if(this.fireEvent('click', this, e) !== false){
20900             this.setChecked(!this.checked);
20901         }
20902         
20903     },
20904     
20905     setChecked : function(state,suppressEvent)
20906     {
20907         this.startValue = this.getValue();
20908
20909         if(this.inputType == 'radio'){
20910             
20911             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20912                 e.dom.checked = false;
20913             });
20914             
20915             this.inputEl().dom.checked = true;
20916             
20917             this.inputEl().dom.value = this.inputValue;
20918             
20919             if(suppressEvent !== true){
20920                 this.fireEvent('check', this, true);
20921             }
20922             
20923             this.validate();
20924             
20925             return;
20926         }
20927         
20928         this.checked = state;
20929         
20930         this.inputEl().dom.checked = state;
20931         
20932         
20933         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20934         
20935         if(suppressEvent !== true){
20936             this.fireEvent('check', this, state);
20937         }
20938         
20939         this.validate();
20940     },
20941     
20942     getValue : function()
20943     {
20944         if(this.inputType == 'radio'){
20945             return this.getGroupValue();
20946         }
20947         
20948         return this.hiddenEl().dom.value;
20949         
20950     },
20951     
20952     getGroupValue : function()
20953     {
20954         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20955             return '';
20956         }
20957         
20958         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20959     },
20960     
20961     setValue : function(v,suppressEvent)
20962     {
20963         if(this.inputType == 'radio'){
20964             this.setGroupValue(v, suppressEvent);
20965             return;
20966         }
20967         
20968         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20969         
20970         this.validate();
20971     },
20972     
20973     setGroupValue : function(v, suppressEvent)
20974     {
20975         this.startValue = this.getValue();
20976         
20977         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20978             e.dom.checked = false;
20979             
20980             if(e.dom.value == v){
20981                 e.dom.checked = true;
20982             }
20983         });
20984         
20985         if(suppressEvent !== true){
20986             this.fireEvent('check', this, true);
20987         }
20988
20989         this.validate();
20990         
20991         return;
20992     },
20993     
20994     validate : function()
20995     {
20996         if(this.getVisibilityEl().hasClass('hidden')){
20997             return true;
20998         }
20999         
21000         if(
21001                 this.disabled || 
21002                 (this.inputType == 'radio' && this.validateRadio()) ||
21003                 (this.inputType == 'checkbox' && this.validateCheckbox())
21004         ){
21005             this.markValid();
21006             return true;
21007         }
21008         
21009         this.markInvalid();
21010         return false;
21011     },
21012     
21013     validateRadio : function()
21014     {
21015         if(this.getVisibilityEl().hasClass('hidden')){
21016             return true;
21017         }
21018         
21019         if(this.allowBlank){
21020             return true;
21021         }
21022         
21023         var valid = false;
21024         
21025         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21026             if(!e.dom.checked){
21027                 return;
21028             }
21029             
21030             valid = true;
21031             
21032             return false;
21033         });
21034         
21035         return valid;
21036     },
21037     
21038     validateCheckbox : function()
21039     {
21040         if(!this.groupId){
21041             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21042             //return (this.getValue() == this.inputValue) ? true : false;
21043         }
21044         
21045         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21046         
21047         if(!group){
21048             return false;
21049         }
21050         
21051         var r = false;
21052         
21053         for(var i in group){
21054             if(group[i].el.isVisible(true)){
21055                 r = false;
21056                 break;
21057             }
21058             
21059             r = true;
21060         }
21061         
21062         for(var i in group){
21063             if(r){
21064                 break;
21065             }
21066             
21067             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21068         }
21069         
21070         return r;
21071     },
21072     
21073     /**
21074      * Mark this field as valid
21075      */
21076     markValid : function()
21077     {
21078         var _this = this;
21079         
21080         this.fireEvent('valid', this);
21081         
21082         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21083         
21084         if(this.groupId){
21085             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21086         }
21087         
21088         if(label){
21089             label.markValid();
21090         }
21091
21092         if(this.inputType == 'radio'){
21093             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21094                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21095                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21096             });
21097             
21098             return;
21099         }
21100
21101         if(!this.groupId){
21102             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21103             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21104             return;
21105         }
21106         
21107         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21108         
21109         if(!group){
21110             return;
21111         }
21112         
21113         for(var i in group){
21114             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21115             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21116         }
21117     },
21118     
21119      /**
21120      * Mark this field as invalid
21121      * @param {String} msg The validation message
21122      */
21123     markInvalid : function(msg)
21124     {
21125         if(this.allowBlank){
21126             return;
21127         }
21128         
21129         var _this = this;
21130         
21131         this.fireEvent('invalid', this, msg);
21132         
21133         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21134         
21135         if(this.groupId){
21136             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21137         }
21138         
21139         if(label){
21140             label.markInvalid();
21141         }
21142             
21143         if(this.inputType == 'radio'){
21144             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21145                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21146                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21147             });
21148             
21149             return;
21150         }
21151         
21152         if(!this.groupId){
21153             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21154             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21155             return;
21156         }
21157         
21158         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21159         
21160         if(!group){
21161             return;
21162         }
21163         
21164         for(var i in group){
21165             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21166             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21167         }
21168         
21169     },
21170     
21171     clearInvalid : function()
21172     {
21173         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21174         
21175         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21176         
21177         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21178         
21179         if (label && label.iconEl) {
21180             label.iconEl.removeClass(label.validClass);
21181             label.iconEl.removeClass(label.invalidClass);
21182         }
21183     },
21184     
21185     disable : function()
21186     {
21187         if(this.inputType != 'radio'){
21188             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21189             return;
21190         }
21191         
21192         var _this = this;
21193         
21194         if(this.rendered){
21195             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21196                 _this.getActionEl().addClass(this.disabledClass);
21197                 e.dom.disabled = true;
21198             });
21199         }
21200         
21201         this.disabled = true;
21202         this.fireEvent("disable", this);
21203         return this;
21204     },
21205
21206     enable : function()
21207     {
21208         if(this.inputType != 'radio'){
21209             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21210             return;
21211         }
21212         
21213         var _this = this;
21214         
21215         if(this.rendered){
21216             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21217                 _this.getActionEl().removeClass(this.disabledClass);
21218                 e.dom.disabled = false;
21219             });
21220         }
21221         
21222         this.disabled = false;
21223         this.fireEvent("enable", this);
21224         return this;
21225     },
21226     
21227     setBoxLabel : function(v)
21228     {
21229         this.boxLabel = v;
21230         
21231         if(this.rendered){
21232             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21233         }
21234     }
21235
21236 });
21237
21238 Roo.apply(Roo.bootstrap.CheckBox, {
21239     
21240     groups: {},
21241     
21242      /**
21243     * register a CheckBox Group
21244     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21245     */
21246     register : function(checkbox)
21247     {
21248         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21249             this.groups[checkbox.groupId] = {};
21250         }
21251         
21252         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21253             return;
21254         }
21255         
21256         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21257         
21258     },
21259     /**
21260     * fetch a CheckBox Group based on the group ID
21261     * @param {string} the group ID
21262     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21263     */
21264     get: function(groupId) {
21265         if (typeof(this.groups[groupId]) == 'undefined') {
21266             return false;
21267         }
21268         
21269         return this.groups[groupId] ;
21270     }
21271     
21272     
21273 });
21274 /*
21275  * - LGPL
21276  *
21277  * RadioItem
21278  * 
21279  */
21280
21281 /**
21282  * @class Roo.bootstrap.Radio
21283  * @extends Roo.bootstrap.Component
21284  * Bootstrap Radio class
21285  * @cfg {String} boxLabel - the label associated
21286  * @cfg {String} value - the value of radio
21287  * 
21288  * @constructor
21289  * Create a new Radio
21290  * @param {Object} config The config object
21291  */
21292 Roo.bootstrap.Radio = function(config){
21293     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21294     
21295 };
21296
21297 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21298     
21299     boxLabel : '',
21300     
21301     value : '',
21302     
21303     getAutoCreate : function()
21304     {
21305         var cfg = {
21306             tag : 'div',
21307             cls : 'form-group radio',
21308             cn : [
21309                 {
21310                     tag : 'label',
21311                     cls : 'box-label',
21312                     html : this.boxLabel
21313                 }
21314             ]
21315         };
21316         
21317         return cfg;
21318     },
21319     
21320     initEvents : function() 
21321     {
21322         this.parent().register(this);
21323         
21324         this.el.on('click', this.onClick, this);
21325         
21326     },
21327     
21328     onClick : function(e)
21329     {
21330         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21331             this.setChecked(true);
21332         }
21333     },
21334     
21335     setChecked : function(state, suppressEvent)
21336     {
21337         this.parent().setValue(this.value, suppressEvent);
21338         
21339     },
21340     
21341     setBoxLabel : function(v)
21342     {
21343         this.boxLabel = v;
21344         
21345         if(this.rendered){
21346             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21347         }
21348     }
21349     
21350 });
21351  
21352
21353  /*
21354  * - LGPL
21355  *
21356  * Input
21357  * 
21358  */
21359
21360 /**
21361  * @class Roo.bootstrap.SecurePass
21362  * @extends Roo.bootstrap.Input
21363  * Bootstrap SecurePass class
21364  *
21365  * 
21366  * @constructor
21367  * Create a new SecurePass
21368  * @param {Object} config The config object
21369  */
21370  
21371 Roo.bootstrap.SecurePass = function (config) {
21372     // these go here, so the translation tool can replace them..
21373     this.errors = {
21374         PwdEmpty: "Please type a password, and then retype it to confirm.",
21375         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21376         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21377         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21378         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21379         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21380         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21381         TooWeak: "Your password is Too Weak."
21382     },
21383     this.meterLabel = "Password strength:";
21384     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21385     this.meterClass = [
21386         "roo-password-meter-tooweak", 
21387         "roo-password-meter-weak", 
21388         "roo-password-meter-medium", 
21389         "roo-password-meter-strong", 
21390         "roo-password-meter-grey"
21391     ];
21392     
21393     this.errors = {};
21394     
21395     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21396 }
21397
21398 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21399     /**
21400      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21401      * {
21402      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21403      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21404      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21405      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21406      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21407      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21408      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21409      * })
21410      */
21411     // private
21412     
21413     meterWidth: 300,
21414     errorMsg :'',    
21415     errors: false,
21416     imageRoot: '/',
21417     /**
21418      * @cfg {String/Object} Label for the strength meter (defaults to
21419      * 'Password strength:')
21420      */
21421     // private
21422     meterLabel: '',
21423     /**
21424      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21425      * ['Weak', 'Medium', 'Strong'])
21426      */
21427     // private    
21428     pwdStrengths: false,    
21429     // private
21430     strength: 0,
21431     // private
21432     _lastPwd: null,
21433     // private
21434     kCapitalLetter: 0,
21435     kSmallLetter: 1,
21436     kDigit: 2,
21437     kPunctuation: 3,
21438     
21439     insecure: false,
21440     // private
21441     initEvents: function ()
21442     {
21443         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21444
21445         if (this.el.is('input[type=password]') && Roo.isSafari) {
21446             this.el.on('keydown', this.SafariOnKeyDown, this);
21447         }
21448
21449         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21450     },
21451     // private
21452     onRender: function (ct, position)
21453     {
21454         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21455         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21456         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21457
21458         this.trigger.createChild({
21459                    cn: [
21460                     {
21461                     //id: 'PwdMeter',
21462                     tag: 'div',
21463                     cls: 'roo-password-meter-grey col-xs-12',
21464                     style: {
21465                         //width: 0,
21466                         //width: this.meterWidth + 'px'                                                
21467                         }
21468                     },
21469                     {                            
21470                          cls: 'roo-password-meter-text'                          
21471                     }
21472                 ]            
21473         });
21474
21475          
21476         if (this.hideTrigger) {
21477             this.trigger.setDisplayed(false);
21478         }
21479         this.setSize(this.width || '', this.height || '');
21480     },
21481     // private
21482     onDestroy: function ()
21483     {
21484         if (this.trigger) {
21485             this.trigger.removeAllListeners();
21486             this.trigger.remove();
21487         }
21488         if (this.wrap) {
21489             this.wrap.remove();
21490         }
21491         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21492     },
21493     // private
21494     checkStrength: function ()
21495     {
21496         var pwd = this.inputEl().getValue();
21497         if (pwd == this._lastPwd) {
21498             return;
21499         }
21500
21501         var strength;
21502         if (this.ClientSideStrongPassword(pwd)) {
21503             strength = 3;
21504         } else if (this.ClientSideMediumPassword(pwd)) {
21505             strength = 2;
21506         } else if (this.ClientSideWeakPassword(pwd)) {
21507             strength = 1;
21508         } else {
21509             strength = 0;
21510         }
21511         
21512         Roo.log('strength1: ' + strength);
21513         
21514         //var pm = this.trigger.child('div/div/div').dom;
21515         var pm = this.trigger.child('div/div');
21516         pm.removeClass(this.meterClass);
21517         pm.addClass(this.meterClass[strength]);
21518                 
21519         
21520         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21521                 
21522         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21523         
21524         this._lastPwd = pwd;
21525     },
21526     reset: function ()
21527     {
21528         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21529         
21530         this._lastPwd = '';
21531         
21532         var pm = this.trigger.child('div/div');
21533         pm.removeClass(this.meterClass);
21534         pm.addClass('roo-password-meter-grey');        
21535         
21536         
21537         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21538         
21539         pt.innerHTML = '';
21540         this.inputEl().dom.type='password';
21541     },
21542     // private
21543     validateValue: function (value)
21544     {
21545         
21546         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21547             return false;
21548         }
21549         if (value.length == 0) {
21550             if (this.allowBlank) {
21551                 this.clearInvalid();
21552                 return true;
21553             }
21554
21555             this.markInvalid(this.errors.PwdEmpty);
21556             this.errorMsg = this.errors.PwdEmpty;
21557             return false;
21558         }
21559         
21560         if(this.insecure){
21561             return true;
21562         }
21563         
21564         if ('[\x21-\x7e]*'.match(value)) {
21565             this.markInvalid(this.errors.PwdBadChar);
21566             this.errorMsg = this.errors.PwdBadChar;
21567             return false;
21568         }
21569         if (value.length < 6) {
21570             this.markInvalid(this.errors.PwdShort);
21571             this.errorMsg = this.errors.PwdShort;
21572             return false;
21573         }
21574         if (value.length > 16) {
21575             this.markInvalid(this.errors.PwdLong);
21576             this.errorMsg = this.errors.PwdLong;
21577             return false;
21578         }
21579         var strength;
21580         if (this.ClientSideStrongPassword(value)) {
21581             strength = 3;
21582         } else if (this.ClientSideMediumPassword(value)) {
21583             strength = 2;
21584         } else if (this.ClientSideWeakPassword(value)) {
21585             strength = 1;
21586         } else {
21587             strength = 0;
21588         }
21589
21590         
21591         if (strength < 2) {
21592             //this.markInvalid(this.errors.TooWeak);
21593             this.errorMsg = this.errors.TooWeak;
21594             //return false;
21595         }
21596         
21597         
21598         console.log('strength2: ' + strength);
21599         
21600         //var pm = this.trigger.child('div/div/div').dom;
21601         
21602         var pm = this.trigger.child('div/div');
21603         pm.removeClass(this.meterClass);
21604         pm.addClass(this.meterClass[strength]);
21605                 
21606         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21607                 
21608         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21609         
21610         this.errorMsg = ''; 
21611         return true;
21612     },
21613     // private
21614     CharacterSetChecks: function (type)
21615     {
21616         this.type = type;
21617         this.fResult = false;
21618     },
21619     // private
21620     isctype: function (character, type)
21621     {
21622         switch (type) {  
21623             case this.kCapitalLetter:
21624                 if (character >= 'A' && character <= 'Z') {
21625                     return true;
21626                 }
21627                 break;
21628             
21629             case this.kSmallLetter:
21630                 if (character >= 'a' && character <= 'z') {
21631                     return true;
21632                 }
21633                 break;
21634             
21635             case this.kDigit:
21636                 if (character >= '0' && character <= '9') {
21637                     return true;
21638                 }
21639                 break;
21640             
21641             case this.kPunctuation:
21642                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21643                     return true;
21644                 }
21645                 break;
21646             
21647             default:
21648                 return false;
21649         }
21650
21651     },
21652     // private
21653     IsLongEnough: function (pwd, size)
21654     {
21655         return !(pwd == null || isNaN(size) || pwd.length < size);
21656     },
21657     // private
21658     SpansEnoughCharacterSets: function (word, nb)
21659     {
21660         if (!this.IsLongEnough(word, nb))
21661         {
21662             return false;
21663         }
21664
21665         var characterSetChecks = new Array(
21666             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21667             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21668         );
21669         
21670         for (var index = 0; index < word.length; ++index) {
21671             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21672                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21673                     characterSetChecks[nCharSet].fResult = true;
21674                     break;
21675                 }
21676             }
21677         }
21678
21679         var nCharSets = 0;
21680         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21681             if (characterSetChecks[nCharSet].fResult) {
21682                 ++nCharSets;
21683             }
21684         }
21685
21686         if (nCharSets < nb) {
21687             return false;
21688         }
21689         return true;
21690     },
21691     // private
21692     ClientSideStrongPassword: function (pwd)
21693     {
21694         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21695     },
21696     // private
21697     ClientSideMediumPassword: function (pwd)
21698     {
21699         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21700     },
21701     // private
21702     ClientSideWeakPassword: function (pwd)
21703     {
21704         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21705     }
21706           
21707 })//<script type="text/javascript">
21708
21709 /*
21710  * Based  Ext JS Library 1.1.1
21711  * Copyright(c) 2006-2007, Ext JS, LLC.
21712  * LGPL
21713  *
21714  */
21715  
21716 /**
21717  * @class Roo.HtmlEditorCore
21718  * @extends Roo.Component
21719  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21720  *
21721  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21722  */
21723
21724 Roo.HtmlEditorCore = function(config){
21725     
21726     
21727     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21728     
21729     
21730     this.addEvents({
21731         /**
21732          * @event initialize
21733          * Fires when the editor is fully initialized (including the iframe)
21734          * @param {Roo.HtmlEditorCore} this
21735          */
21736         initialize: true,
21737         /**
21738          * @event activate
21739          * Fires when the editor is first receives the focus. Any insertion must wait
21740          * until after this event.
21741          * @param {Roo.HtmlEditorCore} this
21742          */
21743         activate: true,
21744          /**
21745          * @event beforesync
21746          * Fires before the textarea is updated with content from the editor iframe. Return false
21747          * to cancel the sync.
21748          * @param {Roo.HtmlEditorCore} this
21749          * @param {String} html
21750          */
21751         beforesync: true,
21752          /**
21753          * @event beforepush
21754          * Fires before the iframe editor is updated with content from the textarea. Return false
21755          * to cancel the push.
21756          * @param {Roo.HtmlEditorCore} this
21757          * @param {String} html
21758          */
21759         beforepush: true,
21760          /**
21761          * @event sync
21762          * Fires when the textarea is updated with content from the editor iframe.
21763          * @param {Roo.HtmlEditorCore} this
21764          * @param {String} html
21765          */
21766         sync: true,
21767          /**
21768          * @event push
21769          * Fires when the iframe editor is updated with content from the textarea.
21770          * @param {Roo.HtmlEditorCore} this
21771          * @param {String} html
21772          */
21773         push: true,
21774         
21775         /**
21776          * @event editorevent
21777          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21778          * @param {Roo.HtmlEditorCore} this
21779          */
21780         editorevent: true
21781         
21782     });
21783     
21784     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21785     
21786     // defaults : white / black...
21787     this.applyBlacklists();
21788     
21789     
21790     
21791 };
21792
21793
21794 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21795
21796
21797      /**
21798      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21799      */
21800     
21801     owner : false,
21802     
21803      /**
21804      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21805      *                        Roo.resizable.
21806      */
21807     resizable : false,
21808      /**
21809      * @cfg {Number} height (in pixels)
21810      */   
21811     height: 300,
21812    /**
21813      * @cfg {Number} width (in pixels)
21814      */   
21815     width: 500,
21816     
21817     /**
21818      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21819      * 
21820      */
21821     stylesheets: false,
21822     
21823     // id of frame..
21824     frameId: false,
21825     
21826     // private properties
21827     validationEvent : false,
21828     deferHeight: true,
21829     initialized : false,
21830     activated : false,
21831     sourceEditMode : false,
21832     onFocus : Roo.emptyFn,
21833     iframePad:3,
21834     hideMode:'offsets',
21835     
21836     clearUp: true,
21837     
21838     // blacklist + whitelisted elements..
21839     black: false,
21840     white: false,
21841      
21842     bodyCls : '',
21843
21844     /**
21845      * Protected method that will not generally be called directly. It
21846      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21847      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21848      */
21849     getDocMarkup : function(){
21850         // body styles..
21851         var st = '';
21852         
21853         // inherit styels from page...?? 
21854         if (this.stylesheets === false) {
21855             
21856             Roo.get(document.head).select('style').each(function(node) {
21857                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21858             });
21859             
21860             Roo.get(document.head).select('link').each(function(node) { 
21861                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21862             });
21863             
21864         } else if (!this.stylesheets.length) {
21865                 // simple..
21866                 st = '<style type="text/css">' +
21867                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21868                    '</style>';
21869         } else { 
21870             st = '<style type="text/css">' +
21871                     this.stylesheets +
21872                 '</style>';
21873         }
21874         
21875         st +=  '<style type="text/css">' +
21876             'IMG { cursor: pointer } ' +
21877         '</style>';
21878
21879         var cls = 'roo-htmleditor-body';
21880         
21881         if(this.bodyCls.length){
21882             cls += ' ' + this.bodyCls;
21883         }
21884         
21885         return '<html><head>' + st  +
21886             //<style type="text/css">' +
21887             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21888             //'</style>' +
21889             ' </head><body class="' +  cls + '"></body></html>';
21890     },
21891
21892     // private
21893     onRender : function(ct, position)
21894     {
21895         var _t = this;
21896         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21897         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21898         
21899         
21900         this.el.dom.style.border = '0 none';
21901         this.el.dom.setAttribute('tabIndex', -1);
21902         this.el.addClass('x-hidden hide');
21903         
21904         
21905         
21906         if(Roo.isIE){ // fix IE 1px bogus margin
21907             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21908         }
21909        
21910         
21911         this.frameId = Roo.id();
21912         
21913          
21914         
21915         var iframe = this.owner.wrap.createChild({
21916             tag: 'iframe',
21917             cls: 'form-control', // bootstrap..
21918             id: this.frameId,
21919             name: this.frameId,
21920             frameBorder : 'no',
21921             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21922         }, this.el
21923         );
21924         
21925         
21926         this.iframe = iframe.dom;
21927
21928          this.assignDocWin();
21929         
21930         this.doc.designMode = 'on';
21931        
21932         this.doc.open();
21933         this.doc.write(this.getDocMarkup());
21934         this.doc.close();
21935
21936         
21937         var task = { // must defer to wait for browser to be ready
21938             run : function(){
21939                 //console.log("run task?" + this.doc.readyState);
21940                 this.assignDocWin();
21941                 if(this.doc.body || this.doc.readyState == 'complete'){
21942                     try {
21943                         this.doc.designMode="on";
21944                     } catch (e) {
21945                         return;
21946                     }
21947                     Roo.TaskMgr.stop(task);
21948                     this.initEditor.defer(10, this);
21949                 }
21950             },
21951             interval : 10,
21952             duration: 10000,
21953             scope: this
21954         };
21955         Roo.TaskMgr.start(task);
21956
21957     },
21958
21959     // private
21960     onResize : function(w, h)
21961     {
21962          Roo.log('resize: ' +w + ',' + h );
21963         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21964         if(!this.iframe){
21965             return;
21966         }
21967         if(typeof w == 'number'){
21968             
21969             this.iframe.style.width = w + 'px';
21970         }
21971         if(typeof h == 'number'){
21972             
21973             this.iframe.style.height = h + 'px';
21974             if(this.doc){
21975                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21976             }
21977         }
21978         
21979     },
21980
21981     /**
21982      * Toggles the editor between standard and source edit mode.
21983      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21984      */
21985     toggleSourceEdit : function(sourceEditMode){
21986         
21987         this.sourceEditMode = sourceEditMode === true;
21988         
21989         if(this.sourceEditMode){
21990  
21991             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21992             
21993         }else{
21994             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21995             //this.iframe.className = '';
21996             this.deferFocus();
21997         }
21998         //this.setSize(this.owner.wrap.getSize());
21999         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22000     },
22001
22002     
22003   
22004
22005     /**
22006      * Protected method that will not generally be called directly. If you need/want
22007      * custom HTML cleanup, this is the method you should override.
22008      * @param {String} html The HTML to be cleaned
22009      * return {String} The cleaned HTML
22010      */
22011     cleanHtml : function(html){
22012         html = String(html);
22013         if(html.length > 5){
22014             if(Roo.isSafari){ // strip safari nonsense
22015                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22016             }
22017         }
22018         if(html == '&nbsp;'){
22019             html = '';
22020         }
22021         return html;
22022     },
22023
22024     /**
22025      * HTML Editor -> Textarea
22026      * Protected method that will not generally be called directly. Syncs the contents
22027      * of the editor iframe with the textarea.
22028      */
22029     syncValue : function(){
22030         if(this.initialized){
22031             var bd = (this.doc.body || this.doc.documentElement);
22032             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22033             var html = bd.innerHTML;
22034             if(Roo.isSafari){
22035                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22036                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22037                 if(m && m[1]){
22038                     html = '<div style="'+m[0]+'">' + html + '</div>';
22039                 }
22040             }
22041             html = this.cleanHtml(html);
22042             // fix up the special chars.. normaly like back quotes in word...
22043             // however we do not want to do this with chinese..
22044             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22045                 var cc = b.charCodeAt();
22046                 if (
22047                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22048                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22049                     (cc >= 0xf900 && cc < 0xfb00 )
22050                 ) {
22051                         return b;
22052                 }
22053                 return "&#"+cc+";" 
22054             });
22055             if(this.owner.fireEvent('beforesync', this, html) !== false){
22056                 this.el.dom.value = html;
22057                 this.owner.fireEvent('sync', this, html);
22058             }
22059         }
22060     },
22061
22062     /**
22063      * Protected method that will not generally be called directly. Pushes the value of the textarea
22064      * into the iframe editor.
22065      */
22066     pushValue : function(){
22067         if(this.initialized){
22068             var v = this.el.dom.value.trim();
22069             
22070 //            if(v.length < 1){
22071 //                v = '&#160;';
22072 //            }
22073             
22074             if(this.owner.fireEvent('beforepush', this, v) !== false){
22075                 var d = (this.doc.body || this.doc.documentElement);
22076                 d.innerHTML = v;
22077                 this.cleanUpPaste();
22078                 this.el.dom.value = d.innerHTML;
22079                 this.owner.fireEvent('push', this, v);
22080             }
22081         }
22082     },
22083
22084     // private
22085     deferFocus : function(){
22086         this.focus.defer(10, this);
22087     },
22088
22089     // doc'ed in Field
22090     focus : function(){
22091         if(this.win && !this.sourceEditMode){
22092             this.win.focus();
22093         }else{
22094             this.el.focus();
22095         }
22096     },
22097     
22098     assignDocWin: function()
22099     {
22100         var iframe = this.iframe;
22101         
22102          if(Roo.isIE){
22103             this.doc = iframe.contentWindow.document;
22104             this.win = iframe.contentWindow;
22105         } else {
22106 //            if (!Roo.get(this.frameId)) {
22107 //                return;
22108 //            }
22109 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22110 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22111             
22112             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22113                 return;
22114             }
22115             
22116             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22117             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22118         }
22119     },
22120     
22121     // private
22122     initEditor : function(){
22123         //console.log("INIT EDITOR");
22124         this.assignDocWin();
22125         
22126         
22127         
22128         this.doc.designMode="on";
22129         this.doc.open();
22130         this.doc.write(this.getDocMarkup());
22131         this.doc.close();
22132         
22133         var dbody = (this.doc.body || this.doc.documentElement);
22134         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22135         // this copies styles from the containing element into thsi one..
22136         // not sure why we need all of this..
22137         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22138         
22139         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22140         //ss['background-attachment'] = 'fixed'; // w3c
22141         dbody.bgProperties = 'fixed'; // ie
22142         //Roo.DomHelper.applyStyles(dbody, ss);
22143         Roo.EventManager.on(this.doc, {
22144             //'mousedown': this.onEditorEvent,
22145             'mouseup': this.onEditorEvent,
22146             'dblclick': this.onEditorEvent,
22147             'click': this.onEditorEvent,
22148             'keyup': this.onEditorEvent,
22149             buffer:100,
22150             scope: this
22151         });
22152         if(Roo.isGecko){
22153             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22154         }
22155         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22156             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22157         }
22158         this.initialized = true;
22159
22160         this.owner.fireEvent('initialize', this);
22161         this.pushValue();
22162     },
22163
22164     // private
22165     onDestroy : function(){
22166         
22167         
22168         
22169         if(this.rendered){
22170             
22171             //for (var i =0; i < this.toolbars.length;i++) {
22172             //    // fixme - ask toolbars for heights?
22173             //    this.toolbars[i].onDestroy();
22174            // }
22175             
22176             //this.wrap.dom.innerHTML = '';
22177             //this.wrap.remove();
22178         }
22179     },
22180
22181     // private
22182     onFirstFocus : function(){
22183         
22184         this.assignDocWin();
22185         
22186         
22187         this.activated = true;
22188          
22189     
22190         if(Roo.isGecko){ // prevent silly gecko errors
22191             this.win.focus();
22192             var s = this.win.getSelection();
22193             if(!s.focusNode || s.focusNode.nodeType != 3){
22194                 var r = s.getRangeAt(0);
22195                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22196                 r.collapse(true);
22197                 this.deferFocus();
22198             }
22199             try{
22200                 this.execCmd('useCSS', true);
22201                 this.execCmd('styleWithCSS', false);
22202             }catch(e){}
22203         }
22204         this.owner.fireEvent('activate', this);
22205     },
22206
22207     // private
22208     adjustFont: function(btn){
22209         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22210         //if(Roo.isSafari){ // safari
22211         //    adjust *= 2;
22212        // }
22213         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22214         if(Roo.isSafari){ // safari
22215             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22216             v =  (v < 10) ? 10 : v;
22217             v =  (v > 48) ? 48 : v;
22218             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22219             
22220         }
22221         
22222         
22223         v = Math.max(1, v+adjust);
22224         
22225         this.execCmd('FontSize', v  );
22226     },
22227
22228     onEditorEvent : function(e)
22229     {
22230         this.owner.fireEvent('editorevent', this, e);
22231       //  this.updateToolbar();
22232         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22233     },
22234
22235     insertTag : function(tg)
22236     {
22237         // could be a bit smarter... -> wrap the current selected tRoo..
22238         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22239             
22240             range = this.createRange(this.getSelection());
22241             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22242             wrappingNode.appendChild(range.extractContents());
22243             range.insertNode(wrappingNode);
22244
22245             return;
22246             
22247             
22248             
22249         }
22250         this.execCmd("formatblock",   tg);
22251         
22252     },
22253     
22254     insertText : function(txt)
22255     {
22256         
22257         
22258         var range = this.createRange();
22259         range.deleteContents();
22260                //alert(Sender.getAttribute('label'));
22261                
22262         range.insertNode(this.doc.createTextNode(txt));
22263     } ,
22264     
22265      
22266
22267     /**
22268      * Executes a Midas editor command on the editor document and performs necessary focus and
22269      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22270      * @param {String} cmd The Midas command
22271      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22272      */
22273     relayCmd : function(cmd, value){
22274         this.win.focus();
22275         this.execCmd(cmd, value);
22276         this.owner.fireEvent('editorevent', this);
22277         //this.updateToolbar();
22278         this.owner.deferFocus();
22279     },
22280
22281     /**
22282      * Executes a Midas editor command directly on the editor document.
22283      * For visual commands, you should use {@link #relayCmd} instead.
22284      * <b>This should only be called after the editor is initialized.</b>
22285      * @param {String} cmd The Midas command
22286      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22287      */
22288     execCmd : function(cmd, value){
22289         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22290         this.syncValue();
22291     },
22292  
22293  
22294    
22295     /**
22296      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22297      * to insert tRoo.
22298      * @param {String} text | dom node.. 
22299      */
22300     insertAtCursor : function(text)
22301     {
22302         
22303         if(!this.activated){
22304             return;
22305         }
22306         /*
22307         if(Roo.isIE){
22308             this.win.focus();
22309             var r = this.doc.selection.createRange();
22310             if(r){
22311                 r.collapse(true);
22312                 r.pasteHTML(text);
22313                 this.syncValue();
22314                 this.deferFocus();
22315             
22316             }
22317             return;
22318         }
22319         */
22320         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22321             this.win.focus();
22322             
22323             
22324             // from jquery ui (MIT licenced)
22325             var range, node;
22326             var win = this.win;
22327             
22328             if (win.getSelection && win.getSelection().getRangeAt) {
22329                 range = win.getSelection().getRangeAt(0);
22330                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22331                 range.insertNode(node);
22332             } else if (win.document.selection && win.document.selection.createRange) {
22333                 // no firefox support
22334                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22335                 win.document.selection.createRange().pasteHTML(txt);
22336             } else {
22337                 // no firefox support
22338                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22339                 this.execCmd('InsertHTML', txt);
22340             } 
22341             
22342             this.syncValue();
22343             
22344             this.deferFocus();
22345         }
22346     },
22347  // private
22348     mozKeyPress : function(e){
22349         if(e.ctrlKey){
22350             var c = e.getCharCode(), cmd;
22351           
22352             if(c > 0){
22353                 c = String.fromCharCode(c).toLowerCase();
22354                 switch(c){
22355                     case 'b':
22356                         cmd = 'bold';
22357                         break;
22358                     case 'i':
22359                         cmd = 'italic';
22360                         break;
22361                     
22362                     case 'u':
22363                         cmd = 'underline';
22364                         break;
22365                     
22366                     case 'v':
22367                         this.cleanUpPaste.defer(100, this);
22368                         return;
22369                         
22370                 }
22371                 if(cmd){
22372                     this.win.focus();
22373                     this.execCmd(cmd);
22374                     this.deferFocus();
22375                     e.preventDefault();
22376                 }
22377                 
22378             }
22379         }
22380     },
22381
22382     // private
22383     fixKeys : function(){ // load time branching for fastest keydown performance
22384         if(Roo.isIE){
22385             return function(e){
22386                 var k = e.getKey(), r;
22387                 if(k == e.TAB){
22388                     e.stopEvent();
22389                     r = this.doc.selection.createRange();
22390                     if(r){
22391                         r.collapse(true);
22392                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22393                         this.deferFocus();
22394                     }
22395                     return;
22396                 }
22397                 
22398                 if(k == e.ENTER){
22399                     r = this.doc.selection.createRange();
22400                     if(r){
22401                         var target = r.parentElement();
22402                         if(!target || target.tagName.toLowerCase() != 'li'){
22403                             e.stopEvent();
22404                             r.pasteHTML('<br />');
22405                             r.collapse(false);
22406                             r.select();
22407                         }
22408                     }
22409                 }
22410                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22411                     this.cleanUpPaste.defer(100, this);
22412                     return;
22413                 }
22414                 
22415                 
22416             };
22417         }else if(Roo.isOpera){
22418             return function(e){
22419                 var k = e.getKey();
22420                 if(k == e.TAB){
22421                     e.stopEvent();
22422                     this.win.focus();
22423                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22424                     this.deferFocus();
22425                 }
22426                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22427                     this.cleanUpPaste.defer(100, this);
22428                     return;
22429                 }
22430                 
22431             };
22432         }else if(Roo.isSafari){
22433             return function(e){
22434                 var k = e.getKey();
22435                 
22436                 if(k == e.TAB){
22437                     e.stopEvent();
22438                     this.execCmd('InsertText','\t');
22439                     this.deferFocus();
22440                     return;
22441                 }
22442                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22443                     this.cleanUpPaste.defer(100, this);
22444                     return;
22445                 }
22446                 
22447              };
22448         }
22449     }(),
22450     
22451     getAllAncestors: function()
22452     {
22453         var p = this.getSelectedNode();
22454         var a = [];
22455         if (!p) {
22456             a.push(p); // push blank onto stack..
22457             p = this.getParentElement();
22458         }
22459         
22460         
22461         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22462             a.push(p);
22463             p = p.parentNode;
22464         }
22465         a.push(this.doc.body);
22466         return a;
22467     },
22468     lastSel : false,
22469     lastSelNode : false,
22470     
22471     
22472     getSelection : function() 
22473     {
22474         this.assignDocWin();
22475         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22476     },
22477     
22478     getSelectedNode: function() 
22479     {
22480         // this may only work on Gecko!!!
22481         
22482         // should we cache this!!!!
22483         
22484         
22485         
22486          
22487         var range = this.createRange(this.getSelection()).cloneRange();
22488         
22489         if (Roo.isIE) {
22490             var parent = range.parentElement();
22491             while (true) {
22492                 var testRange = range.duplicate();
22493                 testRange.moveToElementText(parent);
22494                 if (testRange.inRange(range)) {
22495                     break;
22496                 }
22497                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22498                     break;
22499                 }
22500                 parent = parent.parentElement;
22501             }
22502             return parent;
22503         }
22504         
22505         // is ancestor a text element.
22506         var ac =  range.commonAncestorContainer;
22507         if (ac.nodeType == 3) {
22508             ac = ac.parentNode;
22509         }
22510         
22511         var ar = ac.childNodes;
22512          
22513         var nodes = [];
22514         var other_nodes = [];
22515         var has_other_nodes = false;
22516         for (var i=0;i<ar.length;i++) {
22517             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22518                 continue;
22519             }
22520             // fullly contained node.
22521             
22522             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22523                 nodes.push(ar[i]);
22524                 continue;
22525             }
22526             
22527             // probably selected..
22528             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22529                 other_nodes.push(ar[i]);
22530                 continue;
22531             }
22532             // outer..
22533             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22534                 continue;
22535             }
22536             
22537             
22538             has_other_nodes = true;
22539         }
22540         if (!nodes.length && other_nodes.length) {
22541             nodes= other_nodes;
22542         }
22543         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22544             return false;
22545         }
22546         
22547         return nodes[0];
22548     },
22549     createRange: function(sel)
22550     {
22551         // this has strange effects when using with 
22552         // top toolbar - not sure if it's a great idea.
22553         //this.editor.contentWindow.focus();
22554         if (typeof sel != "undefined") {
22555             try {
22556                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22557             } catch(e) {
22558                 return this.doc.createRange();
22559             }
22560         } else {
22561             return this.doc.createRange();
22562         }
22563     },
22564     getParentElement: function()
22565     {
22566         
22567         this.assignDocWin();
22568         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22569         
22570         var range = this.createRange(sel);
22571          
22572         try {
22573             var p = range.commonAncestorContainer;
22574             while (p.nodeType == 3) { // text node
22575                 p = p.parentNode;
22576             }
22577             return p;
22578         } catch (e) {
22579             return null;
22580         }
22581     
22582     },
22583     /***
22584      *
22585      * Range intersection.. the hard stuff...
22586      *  '-1' = before
22587      *  '0' = hits..
22588      *  '1' = after.
22589      *         [ -- selected range --- ]
22590      *   [fail]                        [fail]
22591      *
22592      *    basically..
22593      *      if end is before start or  hits it. fail.
22594      *      if start is after end or hits it fail.
22595      *
22596      *   if either hits (but other is outside. - then it's not 
22597      *   
22598      *    
22599      **/
22600     
22601     
22602     // @see http://www.thismuchiknow.co.uk/?p=64.
22603     rangeIntersectsNode : function(range, node)
22604     {
22605         var nodeRange = node.ownerDocument.createRange();
22606         try {
22607             nodeRange.selectNode(node);
22608         } catch (e) {
22609             nodeRange.selectNodeContents(node);
22610         }
22611     
22612         var rangeStartRange = range.cloneRange();
22613         rangeStartRange.collapse(true);
22614     
22615         var rangeEndRange = range.cloneRange();
22616         rangeEndRange.collapse(false);
22617     
22618         var nodeStartRange = nodeRange.cloneRange();
22619         nodeStartRange.collapse(true);
22620     
22621         var nodeEndRange = nodeRange.cloneRange();
22622         nodeEndRange.collapse(false);
22623     
22624         return rangeStartRange.compareBoundaryPoints(
22625                  Range.START_TO_START, nodeEndRange) == -1 &&
22626                rangeEndRange.compareBoundaryPoints(
22627                  Range.START_TO_START, nodeStartRange) == 1;
22628         
22629          
22630     },
22631     rangeCompareNode : function(range, node)
22632     {
22633         var nodeRange = node.ownerDocument.createRange();
22634         try {
22635             nodeRange.selectNode(node);
22636         } catch (e) {
22637             nodeRange.selectNodeContents(node);
22638         }
22639         
22640         
22641         range.collapse(true);
22642     
22643         nodeRange.collapse(true);
22644      
22645         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22646         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22647          
22648         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22649         
22650         var nodeIsBefore   =  ss == 1;
22651         var nodeIsAfter    = ee == -1;
22652         
22653         if (nodeIsBefore && nodeIsAfter) {
22654             return 0; // outer
22655         }
22656         if (!nodeIsBefore && nodeIsAfter) {
22657             return 1; //right trailed.
22658         }
22659         
22660         if (nodeIsBefore && !nodeIsAfter) {
22661             return 2;  // left trailed.
22662         }
22663         // fully contined.
22664         return 3;
22665     },
22666
22667     // private? - in a new class?
22668     cleanUpPaste :  function()
22669     {
22670         // cleans up the whole document..
22671         Roo.log('cleanuppaste');
22672         
22673         this.cleanUpChildren(this.doc.body);
22674         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22675         if (clean != this.doc.body.innerHTML) {
22676             this.doc.body.innerHTML = clean;
22677         }
22678         
22679     },
22680     
22681     cleanWordChars : function(input) {// change the chars to hex code
22682         var he = Roo.HtmlEditorCore;
22683         
22684         var output = input;
22685         Roo.each(he.swapCodes, function(sw) { 
22686             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22687             
22688             output = output.replace(swapper, sw[1]);
22689         });
22690         
22691         return output;
22692     },
22693     
22694     
22695     cleanUpChildren : function (n)
22696     {
22697         if (!n.childNodes.length) {
22698             return;
22699         }
22700         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22701            this.cleanUpChild(n.childNodes[i]);
22702         }
22703     },
22704     
22705     
22706         
22707     
22708     cleanUpChild : function (node)
22709     {
22710         var ed = this;
22711         //console.log(node);
22712         if (node.nodeName == "#text") {
22713             // clean up silly Windows -- stuff?
22714             return; 
22715         }
22716         if (node.nodeName == "#comment") {
22717             node.parentNode.removeChild(node);
22718             // clean up silly Windows -- stuff?
22719             return; 
22720         }
22721         var lcname = node.tagName.toLowerCase();
22722         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22723         // whitelist of tags..
22724         
22725         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22726             // remove node.
22727             node.parentNode.removeChild(node);
22728             return;
22729             
22730         }
22731         
22732         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22733         
22734         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22735         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22736         
22737         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22738         //    remove_keep_children = true;
22739         //}
22740         
22741         if (remove_keep_children) {
22742             this.cleanUpChildren(node);
22743             // inserts everything just before this node...
22744             while (node.childNodes.length) {
22745                 var cn = node.childNodes[0];
22746                 node.removeChild(cn);
22747                 node.parentNode.insertBefore(cn, node);
22748             }
22749             node.parentNode.removeChild(node);
22750             return;
22751         }
22752         
22753         if (!node.attributes || !node.attributes.length) {
22754             this.cleanUpChildren(node);
22755             return;
22756         }
22757         
22758         function cleanAttr(n,v)
22759         {
22760             
22761             if (v.match(/^\./) || v.match(/^\//)) {
22762                 return;
22763             }
22764             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22765                 return;
22766             }
22767             if (v.match(/^#/)) {
22768                 return;
22769             }
22770 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22771             node.removeAttribute(n);
22772             
22773         }
22774         
22775         var cwhite = this.cwhite;
22776         var cblack = this.cblack;
22777             
22778         function cleanStyle(n,v)
22779         {
22780             if (v.match(/expression/)) { //XSS?? should we even bother..
22781                 node.removeAttribute(n);
22782                 return;
22783             }
22784             
22785             var parts = v.split(/;/);
22786             var clean = [];
22787             
22788             Roo.each(parts, function(p) {
22789                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22790                 if (!p.length) {
22791                     return true;
22792                 }
22793                 var l = p.split(':').shift().replace(/\s+/g,'');
22794                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22795                 
22796                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22797 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22798                     //node.removeAttribute(n);
22799                     return true;
22800                 }
22801                 //Roo.log()
22802                 // only allow 'c whitelisted system attributes'
22803                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22804 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22805                     //node.removeAttribute(n);
22806                     return true;
22807                 }
22808                 
22809                 
22810                  
22811                 
22812                 clean.push(p);
22813                 return true;
22814             });
22815             if (clean.length) { 
22816                 node.setAttribute(n, clean.join(';'));
22817             } else {
22818                 node.removeAttribute(n);
22819             }
22820             
22821         }
22822         
22823         
22824         for (var i = node.attributes.length-1; i > -1 ; i--) {
22825             var a = node.attributes[i];
22826             //console.log(a);
22827             
22828             if (a.name.toLowerCase().substr(0,2)=='on')  {
22829                 node.removeAttribute(a.name);
22830                 continue;
22831             }
22832             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22833                 node.removeAttribute(a.name);
22834                 continue;
22835             }
22836             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22837                 cleanAttr(a.name,a.value); // fixme..
22838                 continue;
22839             }
22840             if (a.name == 'style') {
22841                 cleanStyle(a.name,a.value);
22842                 continue;
22843             }
22844             /// clean up MS crap..
22845             // tecnically this should be a list of valid class'es..
22846             
22847             
22848             if (a.name == 'class') {
22849                 if (a.value.match(/^Mso/)) {
22850                     node.className = '';
22851                 }
22852                 
22853                 if (a.value.match(/^body$/)) {
22854                     node.className = '';
22855                 }
22856                 continue;
22857             }
22858             
22859             // style cleanup!?
22860             // class cleanup?
22861             
22862         }
22863         
22864         
22865         this.cleanUpChildren(node);
22866         
22867         
22868     },
22869     
22870     /**
22871      * Clean up MS wordisms...
22872      */
22873     cleanWord : function(node)
22874     {
22875         
22876         
22877         if (!node) {
22878             this.cleanWord(this.doc.body);
22879             return;
22880         }
22881         if (node.nodeName == "#text") {
22882             // clean up silly Windows -- stuff?
22883             return; 
22884         }
22885         if (node.nodeName == "#comment") {
22886             node.parentNode.removeChild(node);
22887             // clean up silly Windows -- stuff?
22888             return; 
22889         }
22890         
22891         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22892             node.parentNode.removeChild(node);
22893             return;
22894         }
22895         
22896         // remove - but keep children..
22897         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22898             while (node.childNodes.length) {
22899                 var cn = node.childNodes[0];
22900                 node.removeChild(cn);
22901                 node.parentNode.insertBefore(cn, node);
22902             }
22903             node.parentNode.removeChild(node);
22904             this.iterateChildren(node, this.cleanWord);
22905             return;
22906         }
22907         // clean styles
22908         if (node.className.length) {
22909             
22910             var cn = node.className.split(/\W+/);
22911             var cna = [];
22912             Roo.each(cn, function(cls) {
22913                 if (cls.match(/Mso[a-zA-Z]+/)) {
22914                     return;
22915                 }
22916                 cna.push(cls);
22917             });
22918             node.className = cna.length ? cna.join(' ') : '';
22919             if (!cna.length) {
22920                 node.removeAttribute("class");
22921             }
22922         }
22923         
22924         if (node.hasAttribute("lang")) {
22925             node.removeAttribute("lang");
22926         }
22927         
22928         if (node.hasAttribute("style")) {
22929             
22930             var styles = node.getAttribute("style").split(";");
22931             var nstyle = [];
22932             Roo.each(styles, function(s) {
22933                 if (!s.match(/:/)) {
22934                     return;
22935                 }
22936                 var kv = s.split(":");
22937                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22938                     return;
22939                 }
22940                 // what ever is left... we allow.
22941                 nstyle.push(s);
22942             });
22943             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22944             if (!nstyle.length) {
22945                 node.removeAttribute('style');
22946             }
22947         }
22948         this.iterateChildren(node, this.cleanWord);
22949         
22950         
22951         
22952     },
22953     /**
22954      * iterateChildren of a Node, calling fn each time, using this as the scole..
22955      * @param {DomNode} node node to iterate children of.
22956      * @param {Function} fn method of this class to call on each item.
22957      */
22958     iterateChildren : function(node, fn)
22959     {
22960         if (!node.childNodes.length) {
22961                 return;
22962         }
22963         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22964            fn.call(this, node.childNodes[i])
22965         }
22966     },
22967     
22968     
22969     /**
22970      * cleanTableWidths.
22971      *
22972      * Quite often pasting from word etc.. results in tables with column and widths.
22973      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22974      *
22975      */
22976     cleanTableWidths : function(node)
22977     {
22978          
22979          
22980         if (!node) {
22981             this.cleanTableWidths(this.doc.body);
22982             return;
22983         }
22984         
22985         // ignore list...
22986         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22987             return; 
22988         }
22989         Roo.log(node.tagName);
22990         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22991             this.iterateChildren(node, this.cleanTableWidths);
22992             return;
22993         }
22994         if (node.hasAttribute('width')) {
22995             node.removeAttribute('width');
22996         }
22997         
22998          
22999         if (node.hasAttribute("style")) {
23000             // pretty basic...
23001             
23002             var styles = node.getAttribute("style").split(";");
23003             var nstyle = [];
23004             Roo.each(styles, function(s) {
23005                 if (!s.match(/:/)) {
23006                     return;
23007                 }
23008                 var kv = s.split(":");
23009                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23010                     return;
23011                 }
23012                 // what ever is left... we allow.
23013                 nstyle.push(s);
23014             });
23015             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23016             if (!nstyle.length) {
23017                 node.removeAttribute('style');
23018             }
23019         }
23020         
23021         this.iterateChildren(node, this.cleanTableWidths);
23022         
23023         
23024     },
23025     
23026     
23027     
23028     
23029     domToHTML : function(currentElement, depth, nopadtext) {
23030         
23031         depth = depth || 0;
23032         nopadtext = nopadtext || false;
23033     
23034         if (!currentElement) {
23035             return this.domToHTML(this.doc.body);
23036         }
23037         
23038         //Roo.log(currentElement);
23039         var j;
23040         var allText = false;
23041         var nodeName = currentElement.nodeName;
23042         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23043         
23044         if  (nodeName == '#text') {
23045             
23046             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23047         }
23048         
23049         
23050         var ret = '';
23051         if (nodeName != 'BODY') {
23052              
23053             var i = 0;
23054             // Prints the node tagName, such as <A>, <IMG>, etc
23055             if (tagName) {
23056                 var attr = [];
23057                 for(i = 0; i < currentElement.attributes.length;i++) {
23058                     // quoting?
23059                     var aname = currentElement.attributes.item(i).name;
23060                     if (!currentElement.attributes.item(i).value.length) {
23061                         continue;
23062                     }
23063                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23064                 }
23065                 
23066                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23067             } 
23068             else {
23069                 
23070                 // eack
23071             }
23072         } else {
23073             tagName = false;
23074         }
23075         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23076             return ret;
23077         }
23078         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23079             nopadtext = true;
23080         }
23081         
23082         
23083         // Traverse the tree
23084         i = 0;
23085         var currentElementChild = currentElement.childNodes.item(i);
23086         var allText = true;
23087         var innerHTML  = '';
23088         lastnode = '';
23089         while (currentElementChild) {
23090             // Formatting code (indent the tree so it looks nice on the screen)
23091             var nopad = nopadtext;
23092             if (lastnode == 'SPAN') {
23093                 nopad  = true;
23094             }
23095             // text
23096             if  (currentElementChild.nodeName == '#text') {
23097                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23098                 toadd = nopadtext ? toadd : toadd.trim();
23099                 if (!nopad && toadd.length > 80) {
23100                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23101                 }
23102                 innerHTML  += toadd;
23103                 
23104                 i++;
23105                 currentElementChild = currentElement.childNodes.item(i);
23106                 lastNode = '';
23107                 continue;
23108             }
23109             allText = false;
23110             
23111             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23112                 
23113             // Recursively traverse the tree structure of the child node
23114             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23115             lastnode = currentElementChild.nodeName;
23116             i++;
23117             currentElementChild=currentElement.childNodes.item(i);
23118         }
23119         
23120         ret += innerHTML;
23121         
23122         if (!allText) {
23123                 // The remaining code is mostly for formatting the tree
23124             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23125         }
23126         
23127         
23128         if (tagName) {
23129             ret+= "</"+tagName+">";
23130         }
23131         return ret;
23132         
23133     },
23134         
23135     applyBlacklists : function()
23136     {
23137         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23138         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23139         
23140         this.white = [];
23141         this.black = [];
23142         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23143             if (b.indexOf(tag) > -1) {
23144                 return;
23145             }
23146             this.white.push(tag);
23147             
23148         }, this);
23149         
23150         Roo.each(w, function(tag) {
23151             if (b.indexOf(tag) > -1) {
23152                 return;
23153             }
23154             if (this.white.indexOf(tag) > -1) {
23155                 return;
23156             }
23157             this.white.push(tag);
23158             
23159         }, this);
23160         
23161         
23162         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23163             if (w.indexOf(tag) > -1) {
23164                 return;
23165             }
23166             this.black.push(tag);
23167             
23168         }, this);
23169         
23170         Roo.each(b, function(tag) {
23171             if (w.indexOf(tag) > -1) {
23172                 return;
23173             }
23174             if (this.black.indexOf(tag) > -1) {
23175                 return;
23176             }
23177             this.black.push(tag);
23178             
23179         }, this);
23180         
23181         
23182         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23183         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23184         
23185         this.cwhite = [];
23186         this.cblack = [];
23187         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23188             if (b.indexOf(tag) > -1) {
23189                 return;
23190             }
23191             this.cwhite.push(tag);
23192             
23193         }, this);
23194         
23195         Roo.each(w, function(tag) {
23196             if (b.indexOf(tag) > -1) {
23197                 return;
23198             }
23199             if (this.cwhite.indexOf(tag) > -1) {
23200                 return;
23201             }
23202             this.cwhite.push(tag);
23203             
23204         }, this);
23205         
23206         
23207         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23208             if (w.indexOf(tag) > -1) {
23209                 return;
23210             }
23211             this.cblack.push(tag);
23212             
23213         }, this);
23214         
23215         Roo.each(b, function(tag) {
23216             if (w.indexOf(tag) > -1) {
23217                 return;
23218             }
23219             if (this.cblack.indexOf(tag) > -1) {
23220                 return;
23221             }
23222             this.cblack.push(tag);
23223             
23224         }, this);
23225     },
23226     
23227     setStylesheets : function(stylesheets)
23228     {
23229         if(typeof(stylesheets) == 'string'){
23230             Roo.get(this.iframe.contentDocument.head).createChild({
23231                 tag : 'link',
23232                 rel : 'stylesheet',
23233                 type : 'text/css',
23234                 href : stylesheets
23235             });
23236             
23237             return;
23238         }
23239         var _this = this;
23240      
23241         Roo.each(stylesheets, function(s) {
23242             if(!s.length){
23243                 return;
23244             }
23245             
23246             Roo.get(_this.iframe.contentDocument.head).createChild({
23247                 tag : 'link',
23248                 rel : 'stylesheet',
23249                 type : 'text/css',
23250                 href : s
23251             });
23252         });
23253
23254         
23255     },
23256     
23257     removeStylesheets : function()
23258     {
23259         var _this = this;
23260         
23261         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23262             s.remove();
23263         });
23264     },
23265     
23266     setStyle : function(style)
23267     {
23268         Roo.get(this.iframe.contentDocument.head).createChild({
23269             tag : 'style',
23270             type : 'text/css',
23271             html : style
23272         });
23273
23274         return;
23275     }
23276     
23277     // hide stuff that is not compatible
23278     /**
23279      * @event blur
23280      * @hide
23281      */
23282     /**
23283      * @event change
23284      * @hide
23285      */
23286     /**
23287      * @event focus
23288      * @hide
23289      */
23290     /**
23291      * @event specialkey
23292      * @hide
23293      */
23294     /**
23295      * @cfg {String} fieldClass @hide
23296      */
23297     /**
23298      * @cfg {String} focusClass @hide
23299      */
23300     /**
23301      * @cfg {String} autoCreate @hide
23302      */
23303     /**
23304      * @cfg {String} inputType @hide
23305      */
23306     /**
23307      * @cfg {String} invalidClass @hide
23308      */
23309     /**
23310      * @cfg {String} invalidText @hide
23311      */
23312     /**
23313      * @cfg {String} msgFx @hide
23314      */
23315     /**
23316      * @cfg {String} validateOnBlur @hide
23317      */
23318 });
23319
23320 Roo.HtmlEditorCore.white = [
23321         'area', 'br', 'img', 'input', 'hr', 'wbr',
23322         
23323        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23324        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23325        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23326        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23327        'table',   'ul',         'xmp', 
23328        
23329        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23330       'thead',   'tr', 
23331      
23332       'dir', 'menu', 'ol', 'ul', 'dl',
23333        
23334       'embed',  'object'
23335 ];
23336
23337
23338 Roo.HtmlEditorCore.black = [
23339     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23340         'applet', // 
23341         'base',   'basefont', 'bgsound', 'blink',  'body', 
23342         'frame',  'frameset', 'head',    'html',   'ilayer', 
23343         'iframe', 'layer',  'link',     'meta',    'object',   
23344         'script', 'style' ,'title',  'xml' // clean later..
23345 ];
23346 Roo.HtmlEditorCore.clean = [
23347     'script', 'style', 'title', 'xml'
23348 ];
23349 Roo.HtmlEditorCore.remove = [
23350     'font'
23351 ];
23352 // attributes..
23353
23354 Roo.HtmlEditorCore.ablack = [
23355     'on'
23356 ];
23357     
23358 Roo.HtmlEditorCore.aclean = [ 
23359     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23360 ];
23361
23362 // protocols..
23363 Roo.HtmlEditorCore.pwhite= [
23364         'http',  'https',  'mailto'
23365 ];
23366
23367 // white listed style attributes.
23368 Roo.HtmlEditorCore.cwhite= [
23369       //  'text-align', /// default is to allow most things..
23370       
23371          
23372 //        'font-size'//??
23373 ];
23374
23375 // black listed style attributes.
23376 Roo.HtmlEditorCore.cblack= [
23377       //  'font-size' -- this can be set by the project 
23378 ];
23379
23380
23381 Roo.HtmlEditorCore.swapCodes   =[ 
23382     [    8211, "--" ], 
23383     [    8212, "--" ], 
23384     [    8216,  "'" ],  
23385     [    8217, "'" ],  
23386     [    8220, '"' ],  
23387     [    8221, '"' ],  
23388     [    8226, "*" ],  
23389     [    8230, "..." ]
23390 ]; 
23391
23392     /*
23393  * - LGPL
23394  *
23395  * HtmlEditor
23396  * 
23397  */
23398
23399 /**
23400  * @class Roo.bootstrap.HtmlEditor
23401  * @extends Roo.bootstrap.TextArea
23402  * Bootstrap HtmlEditor class
23403
23404  * @constructor
23405  * Create a new HtmlEditor
23406  * @param {Object} config The config object
23407  */
23408
23409 Roo.bootstrap.HtmlEditor = function(config){
23410     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23411     if (!this.toolbars) {
23412         this.toolbars = [];
23413     }
23414     
23415     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23416     this.addEvents({
23417             /**
23418              * @event initialize
23419              * Fires when the editor is fully initialized (including the iframe)
23420              * @param {HtmlEditor} this
23421              */
23422             initialize: true,
23423             /**
23424              * @event activate
23425              * Fires when the editor is first receives the focus. Any insertion must wait
23426              * until after this event.
23427              * @param {HtmlEditor} this
23428              */
23429             activate: true,
23430              /**
23431              * @event beforesync
23432              * Fires before the textarea is updated with content from the editor iframe. Return false
23433              * to cancel the sync.
23434              * @param {HtmlEditor} this
23435              * @param {String} html
23436              */
23437             beforesync: true,
23438              /**
23439              * @event beforepush
23440              * Fires before the iframe editor is updated with content from the textarea. Return false
23441              * to cancel the push.
23442              * @param {HtmlEditor} this
23443              * @param {String} html
23444              */
23445             beforepush: true,
23446              /**
23447              * @event sync
23448              * Fires when the textarea is updated with content from the editor iframe.
23449              * @param {HtmlEditor} this
23450              * @param {String} html
23451              */
23452             sync: true,
23453              /**
23454              * @event push
23455              * Fires when the iframe editor is updated with content from the textarea.
23456              * @param {HtmlEditor} this
23457              * @param {String} html
23458              */
23459             push: true,
23460              /**
23461              * @event editmodechange
23462              * Fires when the editor switches edit modes
23463              * @param {HtmlEditor} this
23464              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23465              */
23466             editmodechange: true,
23467             /**
23468              * @event editorevent
23469              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23470              * @param {HtmlEditor} this
23471              */
23472             editorevent: true,
23473             /**
23474              * @event firstfocus
23475              * Fires when on first focus - needed by toolbars..
23476              * @param {HtmlEditor} this
23477              */
23478             firstfocus: true,
23479             /**
23480              * @event autosave
23481              * Auto save the htmlEditor value as a file into Events
23482              * @param {HtmlEditor} this
23483              */
23484             autosave: true,
23485             /**
23486              * @event savedpreview
23487              * preview the saved version of htmlEditor
23488              * @param {HtmlEditor} this
23489              */
23490             savedpreview: true
23491         });
23492 };
23493
23494
23495 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23496     
23497     
23498       /**
23499      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23500      */
23501     toolbars : false,
23502     
23503      /**
23504     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23505     */
23506     btns : [],
23507    
23508      /**
23509      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23510      *                        Roo.resizable.
23511      */
23512     resizable : false,
23513      /**
23514      * @cfg {Number} height (in pixels)
23515      */   
23516     height: 300,
23517    /**
23518      * @cfg {Number} width (in pixels)
23519      */   
23520     width: false,
23521     
23522     /**
23523      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23524      * 
23525      */
23526     stylesheets: false,
23527     
23528     // id of frame..
23529     frameId: false,
23530     
23531     // private properties
23532     validationEvent : false,
23533     deferHeight: true,
23534     initialized : false,
23535     activated : false,
23536     
23537     onFocus : Roo.emptyFn,
23538     iframePad:3,
23539     hideMode:'offsets',
23540     
23541     tbContainer : false,
23542     
23543     bodyCls : '',
23544     
23545     toolbarContainer :function() {
23546         return this.wrap.select('.x-html-editor-tb',true).first();
23547     },
23548
23549     /**
23550      * Protected method that will not generally be called directly. It
23551      * is called when the editor creates its toolbar. Override this method if you need to
23552      * add custom toolbar buttons.
23553      * @param {HtmlEditor} editor
23554      */
23555     createToolbar : function(){
23556         Roo.log('renewing');
23557         Roo.log("create toolbars");
23558         
23559         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23560         this.toolbars[0].render(this.toolbarContainer());
23561         
23562         return;
23563         
23564 //        if (!editor.toolbars || !editor.toolbars.length) {
23565 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23566 //        }
23567 //        
23568 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23569 //            editor.toolbars[i] = Roo.factory(
23570 //                    typeof(editor.toolbars[i]) == 'string' ?
23571 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23572 //                Roo.bootstrap.HtmlEditor);
23573 //            editor.toolbars[i].init(editor);
23574 //        }
23575     },
23576
23577      
23578     // private
23579     onRender : function(ct, position)
23580     {
23581        // Roo.log("Call onRender: " + this.xtype);
23582         var _t = this;
23583         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23584       
23585         this.wrap = this.inputEl().wrap({
23586             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23587         });
23588         
23589         this.editorcore.onRender(ct, position);
23590          
23591         if (this.resizable) {
23592             this.resizeEl = new Roo.Resizable(this.wrap, {
23593                 pinned : true,
23594                 wrap: true,
23595                 dynamic : true,
23596                 minHeight : this.height,
23597                 height: this.height,
23598                 handles : this.resizable,
23599                 width: this.width,
23600                 listeners : {
23601                     resize : function(r, w, h) {
23602                         _t.onResize(w,h); // -something
23603                     }
23604                 }
23605             });
23606             
23607         }
23608         this.createToolbar(this);
23609        
23610         
23611         if(!this.width && this.resizable){
23612             this.setSize(this.wrap.getSize());
23613         }
23614         if (this.resizeEl) {
23615             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23616             // should trigger onReize..
23617         }
23618         
23619     },
23620
23621     // private
23622     onResize : function(w, h)
23623     {
23624         Roo.log('resize: ' +w + ',' + h );
23625         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23626         var ew = false;
23627         var eh = false;
23628         
23629         if(this.inputEl() ){
23630             if(typeof w == 'number'){
23631                 var aw = w - this.wrap.getFrameWidth('lr');
23632                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23633                 ew = aw;
23634             }
23635             if(typeof h == 'number'){
23636                  var tbh = -11;  // fixme it needs to tool bar size!
23637                 for (var i =0; i < this.toolbars.length;i++) {
23638                     // fixme - ask toolbars for heights?
23639                     tbh += this.toolbars[i].el.getHeight();
23640                     //if (this.toolbars[i].footer) {
23641                     //    tbh += this.toolbars[i].footer.el.getHeight();
23642                     //}
23643                 }
23644               
23645                 
23646                 
23647                 
23648                 
23649                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23650                 ah -= 5; // knock a few pixes off for look..
23651                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23652                 var eh = ah;
23653             }
23654         }
23655         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23656         this.editorcore.onResize(ew,eh);
23657         
23658     },
23659
23660     /**
23661      * Toggles the editor between standard and source edit mode.
23662      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23663      */
23664     toggleSourceEdit : function(sourceEditMode)
23665     {
23666         this.editorcore.toggleSourceEdit(sourceEditMode);
23667         
23668         if(this.editorcore.sourceEditMode){
23669             Roo.log('editor - showing textarea');
23670             
23671 //            Roo.log('in');
23672 //            Roo.log(this.syncValue());
23673             this.syncValue();
23674             this.inputEl().removeClass(['hide', 'x-hidden']);
23675             this.inputEl().dom.removeAttribute('tabIndex');
23676             this.inputEl().focus();
23677         }else{
23678             Roo.log('editor - hiding textarea');
23679 //            Roo.log('out')
23680 //            Roo.log(this.pushValue()); 
23681             this.pushValue();
23682             
23683             this.inputEl().addClass(['hide', 'x-hidden']);
23684             this.inputEl().dom.setAttribute('tabIndex', -1);
23685             //this.deferFocus();
23686         }
23687          
23688         if(this.resizable){
23689             this.setSize(this.wrap.getSize());
23690         }
23691         
23692         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23693     },
23694  
23695     // private (for BoxComponent)
23696     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23697
23698     // private (for BoxComponent)
23699     getResizeEl : function(){
23700         return this.wrap;
23701     },
23702
23703     // private (for BoxComponent)
23704     getPositionEl : function(){
23705         return this.wrap;
23706     },
23707
23708     // private
23709     initEvents : function(){
23710         this.originalValue = this.getValue();
23711     },
23712
23713 //    /**
23714 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23715 //     * @method
23716 //     */
23717 //    markInvalid : Roo.emptyFn,
23718 //    /**
23719 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23720 //     * @method
23721 //     */
23722 //    clearInvalid : Roo.emptyFn,
23723
23724     setValue : function(v){
23725         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23726         this.editorcore.pushValue();
23727     },
23728
23729      
23730     // private
23731     deferFocus : function(){
23732         this.focus.defer(10, this);
23733     },
23734
23735     // doc'ed in Field
23736     focus : function(){
23737         this.editorcore.focus();
23738         
23739     },
23740       
23741
23742     // private
23743     onDestroy : function(){
23744         
23745         
23746         
23747         if(this.rendered){
23748             
23749             for (var i =0; i < this.toolbars.length;i++) {
23750                 // fixme - ask toolbars for heights?
23751                 this.toolbars[i].onDestroy();
23752             }
23753             
23754             this.wrap.dom.innerHTML = '';
23755             this.wrap.remove();
23756         }
23757     },
23758
23759     // private
23760     onFirstFocus : function(){
23761         //Roo.log("onFirstFocus");
23762         this.editorcore.onFirstFocus();
23763          for (var i =0; i < this.toolbars.length;i++) {
23764             this.toolbars[i].onFirstFocus();
23765         }
23766         
23767     },
23768     
23769     // private
23770     syncValue : function()
23771     {   
23772         this.editorcore.syncValue();
23773     },
23774     
23775     pushValue : function()
23776     {   
23777         this.editorcore.pushValue();
23778     }
23779      
23780     
23781     // hide stuff that is not compatible
23782     /**
23783      * @event blur
23784      * @hide
23785      */
23786     /**
23787      * @event change
23788      * @hide
23789      */
23790     /**
23791      * @event focus
23792      * @hide
23793      */
23794     /**
23795      * @event specialkey
23796      * @hide
23797      */
23798     /**
23799      * @cfg {String} fieldClass @hide
23800      */
23801     /**
23802      * @cfg {String} focusClass @hide
23803      */
23804     /**
23805      * @cfg {String} autoCreate @hide
23806      */
23807     /**
23808      * @cfg {String} inputType @hide
23809      */
23810     /**
23811      * @cfg {String} invalidClass @hide
23812      */
23813     /**
23814      * @cfg {String} invalidText @hide
23815      */
23816     /**
23817      * @cfg {String} msgFx @hide
23818      */
23819     /**
23820      * @cfg {String} validateOnBlur @hide
23821      */
23822 });
23823  
23824     
23825    
23826    
23827    
23828       
23829 Roo.namespace('Roo.bootstrap.htmleditor');
23830 /**
23831  * @class Roo.bootstrap.HtmlEditorToolbar1
23832  * Basic Toolbar
23833  * 
23834  * Usage:
23835  *
23836  new Roo.bootstrap.HtmlEditor({
23837     ....
23838     toolbars : [
23839         new Roo.bootstrap.HtmlEditorToolbar1({
23840             disable : { fonts: 1 , format: 1, ..., ... , ...],
23841             btns : [ .... ]
23842         })
23843     }
23844      
23845  * 
23846  * @cfg {Object} disable List of elements to disable..
23847  * @cfg {Array} btns List of additional buttons.
23848  * 
23849  * 
23850  * NEEDS Extra CSS? 
23851  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23852  */
23853  
23854 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23855 {
23856     
23857     Roo.apply(this, config);
23858     
23859     // default disabled, based on 'good practice'..
23860     this.disable = this.disable || {};
23861     Roo.applyIf(this.disable, {
23862         fontSize : true,
23863         colors : true,
23864         specialElements : true
23865     });
23866     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23867     
23868     this.editor = config.editor;
23869     this.editorcore = config.editor.editorcore;
23870     
23871     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23872     
23873     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23874     // dont call parent... till later.
23875 }
23876 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23877      
23878     bar : true,
23879     
23880     editor : false,
23881     editorcore : false,
23882     
23883     
23884     formats : [
23885         "p" ,  
23886         "h1","h2","h3","h4","h5","h6", 
23887         "pre", "code", 
23888         "abbr", "acronym", "address", "cite", "samp", "var",
23889         'div','span'
23890     ],
23891     
23892     onRender : function(ct, position)
23893     {
23894        // Roo.log("Call onRender: " + this.xtype);
23895         
23896        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23897        Roo.log(this.el);
23898        this.el.dom.style.marginBottom = '0';
23899        var _this = this;
23900        var editorcore = this.editorcore;
23901        var editor= this.editor;
23902        
23903        var children = [];
23904        var btn = function(id,cmd , toggle, handler, html){
23905        
23906             var  event = toggle ? 'toggle' : 'click';
23907        
23908             var a = {
23909                 size : 'sm',
23910                 xtype: 'Button',
23911                 xns: Roo.bootstrap,
23912                 glyphicon : id,
23913                 cmd : id || cmd,
23914                 enableToggle:toggle !== false,
23915                 html : html || '',
23916                 pressed : toggle ? false : null,
23917                 listeners : {}
23918             };
23919             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23920                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23921             };
23922             children.push(a);
23923             return a;
23924        }
23925        
23926     //    var cb_box = function...
23927         
23928         var style = {
23929                 xtype: 'Button',
23930                 size : 'sm',
23931                 xns: Roo.bootstrap,
23932                 glyphicon : 'font',
23933                 //html : 'submit'
23934                 menu : {
23935                     xtype: 'Menu',
23936                     xns: Roo.bootstrap,
23937                     items:  []
23938                 }
23939         };
23940         Roo.each(this.formats, function(f) {
23941             style.menu.items.push({
23942                 xtype :'MenuItem',
23943                 xns: Roo.bootstrap,
23944                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23945                 tagname : f,
23946                 listeners : {
23947                     click : function()
23948                     {
23949                         editorcore.insertTag(this.tagname);
23950                         editor.focus();
23951                     }
23952                 }
23953                 
23954             });
23955         });
23956         children.push(style);   
23957         
23958         btn('bold',false,true);
23959         btn('italic',false,true);
23960         btn('align-left', 'justifyleft',true);
23961         btn('align-center', 'justifycenter',true);
23962         btn('align-right' , 'justifyright',true);
23963         btn('link', false, false, function(btn) {
23964             //Roo.log("create link?");
23965             var url = prompt(this.createLinkText, this.defaultLinkValue);
23966             if(url && url != 'http:/'+'/'){
23967                 this.editorcore.relayCmd('createlink', url);
23968             }
23969         }),
23970         btn('list','insertunorderedlist',true);
23971         btn('pencil', false,true, function(btn){
23972                 Roo.log(this);
23973                 this.toggleSourceEdit(btn.pressed);
23974         });
23975         
23976         if (this.editor.btns.length > 0) {
23977             for (var i = 0; i<this.editor.btns.length; i++) {
23978                 children.push(this.editor.btns[i]);
23979             }
23980         }
23981         
23982         /*
23983         var cog = {
23984                 xtype: 'Button',
23985                 size : 'sm',
23986                 xns: Roo.bootstrap,
23987                 glyphicon : 'cog',
23988                 //html : 'submit'
23989                 menu : {
23990                     xtype: 'Menu',
23991                     xns: Roo.bootstrap,
23992                     items:  []
23993                 }
23994         };
23995         
23996         cog.menu.items.push({
23997             xtype :'MenuItem',
23998             xns: Roo.bootstrap,
23999             html : Clean styles,
24000             tagname : f,
24001             listeners : {
24002                 click : function()
24003                 {
24004                     editorcore.insertTag(this.tagname);
24005                     editor.focus();
24006                 }
24007             }
24008             
24009         });
24010        */
24011         
24012          
24013        this.xtype = 'NavSimplebar';
24014         
24015         for(var i=0;i< children.length;i++) {
24016             
24017             this.buttons.add(this.addxtypeChild(children[i]));
24018             
24019         }
24020         
24021         editor.on('editorevent', this.updateToolbar, this);
24022     },
24023     onBtnClick : function(id)
24024     {
24025        this.editorcore.relayCmd(id);
24026        this.editorcore.focus();
24027     },
24028     
24029     /**
24030      * Protected method that will not generally be called directly. It triggers
24031      * a toolbar update by reading the markup state of the current selection in the editor.
24032      */
24033     updateToolbar: function(){
24034
24035         if(!this.editorcore.activated){
24036             this.editor.onFirstFocus(); // is this neeed?
24037             return;
24038         }
24039
24040         var btns = this.buttons; 
24041         var doc = this.editorcore.doc;
24042         btns.get('bold').setActive(doc.queryCommandState('bold'));
24043         btns.get('italic').setActive(doc.queryCommandState('italic'));
24044         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24045         
24046         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24047         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24048         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24049         
24050         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24051         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24052          /*
24053         
24054         var ans = this.editorcore.getAllAncestors();
24055         if (this.formatCombo) {
24056             
24057             
24058             var store = this.formatCombo.store;
24059             this.formatCombo.setValue("");
24060             for (var i =0; i < ans.length;i++) {
24061                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24062                     // select it..
24063                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24064                     break;
24065                 }
24066             }
24067         }
24068         
24069         
24070         
24071         // hides menus... - so this cant be on a menu...
24072         Roo.bootstrap.MenuMgr.hideAll();
24073         */
24074         Roo.bootstrap.MenuMgr.hideAll();
24075         //this.editorsyncValue();
24076     },
24077     onFirstFocus: function() {
24078         this.buttons.each(function(item){
24079            item.enable();
24080         });
24081     },
24082     toggleSourceEdit : function(sourceEditMode){
24083         
24084           
24085         if(sourceEditMode){
24086             Roo.log("disabling buttons");
24087            this.buttons.each( function(item){
24088                 if(item.cmd != 'pencil'){
24089                     item.disable();
24090                 }
24091             });
24092           
24093         }else{
24094             Roo.log("enabling buttons");
24095             if(this.editorcore.initialized){
24096                 this.buttons.each( function(item){
24097                     item.enable();
24098                 });
24099             }
24100             
24101         }
24102         Roo.log("calling toggole on editor");
24103         // tell the editor that it's been pressed..
24104         this.editor.toggleSourceEdit(sourceEditMode);
24105        
24106     }
24107 });
24108
24109
24110
24111
24112
24113 /**
24114  * @class Roo.bootstrap.Table.AbstractSelectionModel
24115  * @extends Roo.util.Observable
24116  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24117  * implemented by descendant classes.  This class should not be directly instantiated.
24118  * @constructor
24119  */
24120 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24121     this.locked = false;
24122     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24123 };
24124
24125
24126 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24127     /** @ignore Called by the grid automatically. Do not call directly. */
24128     init : function(grid){
24129         this.grid = grid;
24130         this.initEvents();
24131     },
24132
24133     /**
24134      * Locks the selections.
24135      */
24136     lock : function(){
24137         this.locked = true;
24138     },
24139
24140     /**
24141      * Unlocks the selections.
24142      */
24143     unlock : function(){
24144         this.locked = false;
24145     },
24146
24147     /**
24148      * Returns true if the selections are locked.
24149      * @return {Boolean}
24150      */
24151     isLocked : function(){
24152         return this.locked;
24153     }
24154 });
24155 /**
24156  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24157  * @class Roo.bootstrap.Table.RowSelectionModel
24158  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24159  * It supports multiple selections and keyboard selection/navigation. 
24160  * @constructor
24161  * @param {Object} config
24162  */
24163
24164 Roo.bootstrap.Table.RowSelectionModel = function(config){
24165     Roo.apply(this, config);
24166     this.selections = new Roo.util.MixedCollection(false, function(o){
24167         return o.id;
24168     });
24169
24170     this.last = false;
24171     this.lastActive = false;
24172
24173     this.addEvents({
24174         /**
24175              * @event selectionchange
24176              * Fires when the selection changes
24177              * @param {SelectionModel} this
24178              */
24179             "selectionchange" : true,
24180         /**
24181              * @event afterselectionchange
24182              * Fires after the selection changes (eg. by key press or clicking)
24183              * @param {SelectionModel} this
24184              */
24185             "afterselectionchange" : true,
24186         /**
24187              * @event beforerowselect
24188              * Fires when a row is selected being selected, return false to cancel.
24189              * @param {SelectionModel} this
24190              * @param {Number} rowIndex The selected index
24191              * @param {Boolean} keepExisting False if other selections will be cleared
24192              */
24193             "beforerowselect" : true,
24194         /**
24195              * @event rowselect
24196              * Fires when a row is selected.
24197              * @param {SelectionModel} this
24198              * @param {Number} rowIndex The selected index
24199              * @param {Roo.data.Record} r The record
24200              */
24201             "rowselect" : true,
24202         /**
24203              * @event rowdeselect
24204              * Fires when a row is deselected.
24205              * @param {SelectionModel} this
24206              * @param {Number} rowIndex The selected index
24207              */
24208         "rowdeselect" : true
24209     });
24210     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24211     this.locked = false;
24212  };
24213
24214 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24215     /**
24216      * @cfg {Boolean} singleSelect
24217      * True to allow selection of only one row at a time (defaults to false)
24218      */
24219     singleSelect : false,
24220
24221     // private
24222     initEvents : function()
24223     {
24224
24225         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24226         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24227         //}else{ // allow click to work like normal
24228          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24229         //}
24230         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24231         this.grid.on("rowclick", this.handleMouseDown, this);
24232         
24233         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24234             "up" : function(e){
24235                 if(!e.shiftKey){
24236                     this.selectPrevious(e.shiftKey);
24237                 }else if(this.last !== false && this.lastActive !== false){
24238                     var last = this.last;
24239                     this.selectRange(this.last,  this.lastActive-1);
24240                     this.grid.getView().focusRow(this.lastActive);
24241                     if(last !== false){
24242                         this.last = last;
24243                     }
24244                 }else{
24245                     this.selectFirstRow();
24246                 }
24247                 this.fireEvent("afterselectionchange", this);
24248             },
24249             "down" : function(e){
24250                 if(!e.shiftKey){
24251                     this.selectNext(e.shiftKey);
24252                 }else if(this.last !== false && this.lastActive !== false){
24253                     var last = this.last;
24254                     this.selectRange(this.last,  this.lastActive+1);
24255                     this.grid.getView().focusRow(this.lastActive);
24256                     if(last !== false){
24257                         this.last = last;
24258                     }
24259                 }else{
24260                     this.selectFirstRow();
24261                 }
24262                 this.fireEvent("afterselectionchange", this);
24263             },
24264             scope: this
24265         });
24266         this.grid.store.on('load', function(){
24267             this.selections.clear();
24268         },this);
24269         /*
24270         var view = this.grid.view;
24271         view.on("refresh", this.onRefresh, this);
24272         view.on("rowupdated", this.onRowUpdated, this);
24273         view.on("rowremoved", this.onRemove, this);
24274         */
24275     },
24276
24277     // private
24278     onRefresh : function()
24279     {
24280         var ds = this.grid.store, i, v = this.grid.view;
24281         var s = this.selections;
24282         s.each(function(r){
24283             if((i = ds.indexOfId(r.id)) != -1){
24284                 v.onRowSelect(i);
24285             }else{
24286                 s.remove(r);
24287             }
24288         });
24289     },
24290
24291     // private
24292     onRemove : function(v, index, r){
24293         this.selections.remove(r);
24294     },
24295
24296     // private
24297     onRowUpdated : function(v, index, r){
24298         if(this.isSelected(r)){
24299             v.onRowSelect(index);
24300         }
24301     },
24302
24303     /**
24304      * Select records.
24305      * @param {Array} records The records to select
24306      * @param {Boolean} keepExisting (optional) True to keep existing selections
24307      */
24308     selectRecords : function(records, keepExisting)
24309     {
24310         if(!keepExisting){
24311             this.clearSelections();
24312         }
24313             var ds = this.grid.store;
24314         for(var i = 0, len = records.length; i < len; i++){
24315             this.selectRow(ds.indexOf(records[i]), true);
24316         }
24317     },
24318
24319     /**
24320      * Gets the number of selected rows.
24321      * @return {Number}
24322      */
24323     getCount : function(){
24324         return this.selections.length;
24325     },
24326
24327     /**
24328      * Selects the first row in the grid.
24329      */
24330     selectFirstRow : function(){
24331         this.selectRow(0);
24332     },
24333
24334     /**
24335      * Select the last row.
24336      * @param {Boolean} keepExisting (optional) True to keep existing selections
24337      */
24338     selectLastRow : function(keepExisting){
24339         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24340         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24341     },
24342
24343     /**
24344      * Selects the row immediately following the last selected row.
24345      * @param {Boolean} keepExisting (optional) True to keep existing selections
24346      */
24347     selectNext : function(keepExisting)
24348     {
24349             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24350             this.selectRow(this.last+1, keepExisting);
24351             this.grid.getView().focusRow(this.last);
24352         }
24353     },
24354
24355     /**
24356      * Selects the row that precedes the last selected row.
24357      * @param {Boolean} keepExisting (optional) True to keep existing selections
24358      */
24359     selectPrevious : function(keepExisting){
24360         if(this.last){
24361             this.selectRow(this.last-1, keepExisting);
24362             this.grid.getView().focusRow(this.last);
24363         }
24364     },
24365
24366     /**
24367      * Returns the selected records
24368      * @return {Array} Array of selected records
24369      */
24370     getSelections : function(){
24371         return [].concat(this.selections.items);
24372     },
24373
24374     /**
24375      * Returns the first selected record.
24376      * @return {Record}
24377      */
24378     getSelected : function(){
24379         return this.selections.itemAt(0);
24380     },
24381
24382
24383     /**
24384      * Clears all selections.
24385      */
24386     clearSelections : function(fast)
24387     {
24388         if(this.locked) {
24389             return;
24390         }
24391         if(fast !== true){
24392                 var ds = this.grid.store;
24393             var s = this.selections;
24394             s.each(function(r){
24395                 this.deselectRow(ds.indexOfId(r.id));
24396             }, this);
24397             s.clear();
24398         }else{
24399             this.selections.clear();
24400         }
24401         this.last = false;
24402     },
24403
24404
24405     /**
24406      * Selects all rows.
24407      */
24408     selectAll : function(){
24409         if(this.locked) {
24410             return;
24411         }
24412         this.selections.clear();
24413         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24414             this.selectRow(i, true);
24415         }
24416     },
24417
24418     /**
24419      * Returns True if there is a selection.
24420      * @return {Boolean}
24421      */
24422     hasSelection : function(){
24423         return this.selections.length > 0;
24424     },
24425
24426     /**
24427      * Returns True if the specified row is selected.
24428      * @param {Number/Record} record The record or index of the record to check
24429      * @return {Boolean}
24430      */
24431     isSelected : function(index){
24432             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24433         return (r && this.selections.key(r.id) ? true : false);
24434     },
24435
24436     /**
24437      * Returns True if the specified record id is selected.
24438      * @param {String} id The id of record to check
24439      * @return {Boolean}
24440      */
24441     isIdSelected : function(id){
24442         return (this.selections.key(id) ? true : false);
24443     },
24444
24445
24446     // private
24447     handleMouseDBClick : function(e, t){
24448         
24449     },
24450     // private
24451     handleMouseDown : function(e, t)
24452     {
24453             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24454         if(this.isLocked() || rowIndex < 0 ){
24455             return;
24456         };
24457         if(e.shiftKey && this.last !== false){
24458             var last = this.last;
24459             this.selectRange(last, rowIndex, e.ctrlKey);
24460             this.last = last; // reset the last
24461             t.focus();
24462     
24463         }else{
24464             var isSelected = this.isSelected(rowIndex);
24465             //Roo.log("select row:" + rowIndex);
24466             if(isSelected){
24467                 this.deselectRow(rowIndex);
24468             } else {
24469                         this.selectRow(rowIndex, true);
24470             }
24471     
24472             /*
24473                 if(e.button !== 0 && isSelected){
24474                 alert('rowIndex 2: ' + rowIndex);
24475                     view.focusRow(rowIndex);
24476                 }else if(e.ctrlKey && isSelected){
24477                     this.deselectRow(rowIndex);
24478                 }else if(!isSelected){
24479                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24480                     view.focusRow(rowIndex);
24481                 }
24482             */
24483         }
24484         this.fireEvent("afterselectionchange", this);
24485     },
24486     // private
24487     handleDragableRowClick :  function(grid, rowIndex, e) 
24488     {
24489         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24490             this.selectRow(rowIndex, false);
24491             grid.view.focusRow(rowIndex);
24492              this.fireEvent("afterselectionchange", this);
24493         }
24494     },
24495     
24496     /**
24497      * Selects multiple rows.
24498      * @param {Array} rows Array of the indexes of the row to select
24499      * @param {Boolean} keepExisting (optional) True to keep existing selections
24500      */
24501     selectRows : function(rows, keepExisting){
24502         if(!keepExisting){
24503             this.clearSelections();
24504         }
24505         for(var i = 0, len = rows.length; i < len; i++){
24506             this.selectRow(rows[i], true);
24507         }
24508     },
24509
24510     /**
24511      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24512      * @param {Number} startRow The index of the first row in the range
24513      * @param {Number} endRow The index of the last row in the range
24514      * @param {Boolean} keepExisting (optional) True to retain existing selections
24515      */
24516     selectRange : function(startRow, endRow, keepExisting){
24517         if(this.locked) {
24518             return;
24519         }
24520         if(!keepExisting){
24521             this.clearSelections();
24522         }
24523         if(startRow <= endRow){
24524             for(var i = startRow; i <= endRow; i++){
24525                 this.selectRow(i, true);
24526             }
24527         }else{
24528             for(var i = startRow; i >= endRow; i--){
24529                 this.selectRow(i, true);
24530             }
24531         }
24532     },
24533
24534     /**
24535      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24536      * @param {Number} startRow The index of the first row in the range
24537      * @param {Number} endRow The index of the last row in the range
24538      */
24539     deselectRange : function(startRow, endRow, preventViewNotify){
24540         if(this.locked) {
24541             return;
24542         }
24543         for(var i = startRow; i <= endRow; i++){
24544             this.deselectRow(i, preventViewNotify);
24545         }
24546     },
24547
24548     /**
24549      * Selects a row.
24550      * @param {Number} row The index of the row to select
24551      * @param {Boolean} keepExisting (optional) True to keep existing selections
24552      */
24553     selectRow : function(index, keepExisting, preventViewNotify)
24554     {
24555             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24556             return;
24557         }
24558         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24559             if(!keepExisting || this.singleSelect){
24560                 this.clearSelections();
24561             }
24562             
24563             var r = this.grid.store.getAt(index);
24564             //console.log('selectRow - record id :' + r.id);
24565             
24566             this.selections.add(r);
24567             this.last = this.lastActive = index;
24568             if(!preventViewNotify){
24569                 var proxy = new Roo.Element(
24570                                 this.grid.getRowDom(index)
24571                 );
24572                 proxy.addClass('bg-info info');
24573             }
24574             this.fireEvent("rowselect", this, index, r);
24575             this.fireEvent("selectionchange", this);
24576         }
24577     },
24578
24579     /**
24580      * Deselects a row.
24581      * @param {Number} row The index of the row to deselect
24582      */
24583     deselectRow : function(index, preventViewNotify)
24584     {
24585         if(this.locked) {
24586             return;
24587         }
24588         if(this.last == index){
24589             this.last = false;
24590         }
24591         if(this.lastActive == index){
24592             this.lastActive = false;
24593         }
24594         
24595         var r = this.grid.store.getAt(index);
24596         if (!r) {
24597             return;
24598         }
24599         
24600         this.selections.remove(r);
24601         //.console.log('deselectRow - record id :' + r.id);
24602         if(!preventViewNotify){
24603         
24604             var proxy = new Roo.Element(
24605                 this.grid.getRowDom(index)
24606             );
24607             proxy.removeClass('bg-info info');
24608         }
24609         this.fireEvent("rowdeselect", this, index);
24610         this.fireEvent("selectionchange", this);
24611     },
24612
24613     // private
24614     restoreLast : function(){
24615         if(this._last){
24616             this.last = this._last;
24617         }
24618     },
24619
24620     // private
24621     acceptsNav : function(row, col, cm){
24622         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24623     },
24624
24625     // private
24626     onEditorKey : function(field, e){
24627         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24628         if(k == e.TAB){
24629             e.stopEvent();
24630             ed.completeEdit();
24631             if(e.shiftKey){
24632                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24633             }else{
24634                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24635             }
24636         }else if(k == e.ENTER && !e.ctrlKey){
24637             e.stopEvent();
24638             ed.completeEdit();
24639             if(e.shiftKey){
24640                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24641             }else{
24642                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24643             }
24644         }else if(k == e.ESC){
24645             ed.cancelEdit();
24646         }
24647         if(newCell){
24648             g.startEditing(newCell[0], newCell[1]);
24649         }
24650     }
24651 });
24652 /*
24653  * Based on:
24654  * Ext JS Library 1.1.1
24655  * Copyright(c) 2006-2007, Ext JS, LLC.
24656  *
24657  * Originally Released Under LGPL - original licence link has changed is not relivant.
24658  *
24659  * Fork - LGPL
24660  * <script type="text/javascript">
24661  */
24662  
24663 /**
24664  * @class Roo.bootstrap.PagingToolbar
24665  * @extends Roo.bootstrap.NavSimplebar
24666  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24667  * @constructor
24668  * Create a new PagingToolbar
24669  * @param {Object} config The config object
24670  * @param {Roo.data.Store} store
24671  */
24672 Roo.bootstrap.PagingToolbar = function(config)
24673 {
24674     // old args format still supported... - xtype is prefered..
24675         // created from xtype...
24676     
24677     this.ds = config.dataSource;
24678     
24679     if (config.store && !this.ds) {
24680         this.store= Roo.factory(config.store, Roo.data);
24681         this.ds = this.store;
24682         this.ds.xmodule = this.xmodule || false;
24683     }
24684     
24685     this.toolbarItems = [];
24686     if (config.items) {
24687         this.toolbarItems = config.items;
24688     }
24689     
24690     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24691     
24692     this.cursor = 0;
24693     
24694     if (this.ds) { 
24695         this.bind(this.ds);
24696     }
24697     
24698     if (Roo.bootstrap.version == 4) {
24699         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24700     } else {
24701         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24702     }
24703     
24704 };
24705
24706 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24707     /**
24708      * @cfg {Roo.data.Store} dataSource
24709      * The underlying data store providing the paged data
24710      */
24711     /**
24712      * @cfg {String/HTMLElement/Element} container
24713      * container The id or element that will contain the toolbar
24714      */
24715     /**
24716      * @cfg {Boolean} displayInfo
24717      * True to display the displayMsg (defaults to false)
24718      */
24719     /**
24720      * @cfg {Number} pageSize
24721      * The number of records to display per page (defaults to 20)
24722      */
24723     pageSize: 20,
24724     /**
24725      * @cfg {String} displayMsg
24726      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24727      */
24728     displayMsg : 'Displaying {0} - {1} of {2}',
24729     /**
24730      * @cfg {String} emptyMsg
24731      * The message to display when no records are found (defaults to "No data to display")
24732      */
24733     emptyMsg : 'No data to display',
24734     /**
24735      * Customizable piece of the default paging text (defaults to "Page")
24736      * @type String
24737      */
24738     beforePageText : "Page",
24739     /**
24740      * Customizable piece of the default paging text (defaults to "of %0")
24741      * @type String
24742      */
24743     afterPageText : "of {0}",
24744     /**
24745      * Customizable piece of the default paging text (defaults to "First Page")
24746      * @type String
24747      */
24748     firstText : "First Page",
24749     /**
24750      * Customizable piece of the default paging text (defaults to "Previous Page")
24751      * @type String
24752      */
24753     prevText : "Previous Page",
24754     /**
24755      * Customizable piece of the default paging text (defaults to "Next Page")
24756      * @type String
24757      */
24758     nextText : "Next Page",
24759     /**
24760      * Customizable piece of the default paging text (defaults to "Last Page")
24761      * @type String
24762      */
24763     lastText : "Last Page",
24764     /**
24765      * Customizable piece of the default paging text (defaults to "Refresh")
24766      * @type String
24767      */
24768     refreshText : "Refresh",
24769
24770     buttons : false,
24771     // private
24772     onRender : function(ct, position) 
24773     {
24774         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24775         this.navgroup.parentId = this.id;
24776         this.navgroup.onRender(this.el, null);
24777         // add the buttons to the navgroup
24778         
24779         if(this.displayInfo){
24780             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24781             this.displayEl = this.el.select('.x-paging-info', true).first();
24782 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24783 //            this.displayEl = navel.el.select('span',true).first();
24784         }
24785         
24786         var _this = this;
24787         
24788         if(this.buttons){
24789             Roo.each(_this.buttons, function(e){ // this might need to use render????
24790                Roo.factory(e).render(_this.el);
24791             });
24792         }
24793             
24794         Roo.each(_this.toolbarItems, function(e) {
24795             _this.navgroup.addItem(e);
24796         });
24797         
24798         
24799         this.first = this.navgroup.addItem({
24800             tooltip: this.firstText,
24801             cls: "prev btn-outline-secondary",
24802             html : ' <i class="fa fa-step-backward"></i>',
24803             disabled: true,
24804             preventDefault: true,
24805             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24806         });
24807         
24808         this.prev =  this.navgroup.addItem({
24809             tooltip: this.prevText,
24810             cls: "prev btn-outline-secondary",
24811             html : ' <i class="fa fa-backward"></i>',
24812             disabled: true,
24813             preventDefault: true,
24814             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24815         });
24816     //this.addSeparator();
24817         
24818         
24819         var field = this.navgroup.addItem( {
24820             tagtype : 'span',
24821             cls : 'x-paging-position  btn-outline-secondary',
24822              disabled: true,
24823             html : this.beforePageText  +
24824                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24825                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24826          } ); //?? escaped?
24827         
24828         this.field = field.el.select('input', true).first();
24829         this.field.on("keydown", this.onPagingKeydown, this);
24830         this.field.on("focus", function(){this.dom.select();});
24831     
24832     
24833         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24834         //this.field.setHeight(18);
24835         //this.addSeparator();
24836         this.next = this.navgroup.addItem({
24837             tooltip: this.nextText,
24838             cls: "next btn-outline-secondary",
24839             html : ' <i class="fa fa-forward"></i>',
24840             disabled: true,
24841             preventDefault: true,
24842             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24843         });
24844         this.last = this.navgroup.addItem({
24845             tooltip: this.lastText,
24846             html : ' <i class="fa fa-step-forward"></i>',
24847             cls: "next btn-outline-secondary",
24848             disabled: true,
24849             preventDefault: true,
24850             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24851         });
24852     //this.addSeparator();
24853         this.loading = this.navgroup.addItem({
24854             tooltip: this.refreshText,
24855             cls: "btn-outline-secondary",
24856             html : ' <i class="fa fa-refresh"></i>',
24857             preventDefault: true,
24858             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24859         });
24860         
24861     },
24862
24863     // private
24864     updateInfo : function(){
24865         if(this.displayEl){
24866             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24867             var msg = count == 0 ?
24868                 this.emptyMsg :
24869                 String.format(
24870                     this.displayMsg,
24871                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24872                 );
24873             this.displayEl.update(msg);
24874         }
24875     },
24876
24877     // private
24878     onLoad : function(ds, r, o)
24879     {
24880         this.cursor = o.params.start ? o.params.start : 0;
24881         
24882         var d = this.getPageData(),
24883             ap = d.activePage,
24884             ps = d.pages;
24885         
24886         
24887         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24888         this.field.dom.value = ap;
24889         this.first.setDisabled(ap == 1);
24890         this.prev.setDisabled(ap == 1);
24891         this.next.setDisabled(ap == ps);
24892         this.last.setDisabled(ap == ps);
24893         this.loading.enable();
24894         this.updateInfo();
24895     },
24896
24897     // private
24898     getPageData : function(){
24899         var total = this.ds.getTotalCount();
24900         return {
24901             total : total,
24902             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24903             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24904         };
24905     },
24906
24907     // private
24908     onLoadError : function(){
24909         this.loading.enable();
24910     },
24911
24912     // private
24913     onPagingKeydown : function(e){
24914         var k = e.getKey();
24915         var d = this.getPageData();
24916         if(k == e.RETURN){
24917             var v = this.field.dom.value, pageNum;
24918             if(!v || isNaN(pageNum = parseInt(v, 10))){
24919                 this.field.dom.value = d.activePage;
24920                 return;
24921             }
24922             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24923             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24924             e.stopEvent();
24925         }
24926         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))
24927         {
24928           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24929           this.field.dom.value = pageNum;
24930           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24931           e.stopEvent();
24932         }
24933         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24934         {
24935           var v = this.field.dom.value, pageNum; 
24936           var increment = (e.shiftKey) ? 10 : 1;
24937           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24938                 increment *= -1;
24939           }
24940           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24941             this.field.dom.value = d.activePage;
24942             return;
24943           }
24944           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24945           {
24946             this.field.dom.value = parseInt(v, 10) + increment;
24947             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24948             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24949           }
24950           e.stopEvent();
24951         }
24952     },
24953
24954     // private
24955     beforeLoad : function(){
24956         if(this.loading){
24957             this.loading.disable();
24958         }
24959     },
24960
24961     // private
24962     onClick : function(which){
24963         
24964         var ds = this.ds;
24965         if (!ds) {
24966             return;
24967         }
24968         
24969         switch(which){
24970             case "first":
24971                 ds.load({params:{start: 0, limit: this.pageSize}});
24972             break;
24973             case "prev":
24974                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24975             break;
24976             case "next":
24977                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24978             break;
24979             case "last":
24980                 var total = ds.getTotalCount();
24981                 var extra = total % this.pageSize;
24982                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24983                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24984             break;
24985             case "refresh":
24986                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24987             break;
24988         }
24989     },
24990
24991     /**
24992      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24993      * @param {Roo.data.Store} store The data store to unbind
24994      */
24995     unbind : function(ds){
24996         ds.un("beforeload", this.beforeLoad, this);
24997         ds.un("load", this.onLoad, this);
24998         ds.un("loadexception", this.onLoadError, this);
24999         ds.un("remove", this.updateInfo, this);
25000         ds.un("add", this.updateInfo, this);
25001         this.ds = undefined;
25002     },
25003
25004     /**
25005      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25006      * @param {Roo.data.Store} store The data store to bind
25007      */
25008     bind : function(ds){
25009         ds.on("beforeload", this.beforeLoad, this);
25010         ds.on("load", this.onLoad, this);
25011         ds.on("loadexception", this.onLoadError, this);
25012         ds.on("remove", this.updateInfo, this);
25013         ds.on("add", this.updateInfo, this);
25014         this.ds = ds;
25015     }
25016 });/*
25017  * - LGPL
25018  *
25019  * element
25020  * 
25021  */
25022
25023 /**
25024  * @class Roo.bootstrap.MessageBar
25025  * @extends Roo.bootstrap.Component
25026  * Bootstrap MessageBar class
25027  * @cfg {String} html contents of the MessageBar
25028  * @cfg {String} weight (info | success | warning | danger) default info
25029  * @cfg {String} beforeClass insert the bar before the given class
25030  * @cfg {Boolean} closable (true | false) default false
25031  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25032  * 
25033  * @constructor
25034  * Create a new Element
25035  * @param {Object} config The config object
25036  */
25037
25038 Roo.bootstrap.MessageBar = function(config){
25039     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25040 };
25041
25042 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25043     
25044     html: '',
25045     weight: 'info',
25046     closable: false,
25047     fixed: false,
25048     beforeClass: 'bootstrap-sticky-wrap',
25049     
25050     getAutoCreate : function(){
25051         
25052         var cfg = {
25053             tag: 'div',
25054             cls: 'alert alert-dismissable alert-' + this.weight,
25055             cn: [
25056                 {
25057                     tag: 'span',
25058                     cls: 'message',
25059                     html: this.html || ''
25060                 }
25061             ]
25062         };
25063         
25064         if(this.fixed){
25065             cfg.cls += ' alert-messages-fixed';
25066         }
25067         
25068         if(this.closable){
25069             cfg.cn.push({
25070                 tag: 'button',
25071                 cls: 'close',
25072                 html: 'x'
25073             });
25074         }
25075         
25076         return cfg;
25077     },
25078     
25079     onRender : function(ct, position)
25080     {
25081         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25082         
25083         if(!this.el){
25084             var cfg = Roo.apply({},  this.getAutoCreate());
25085             cfg.id = Roo.id();
25086             
25087             if (this.cls) {
25088                 cfg.cls += ' ' + this.cls;
25089             }
25090             if (this.style) {
25091                 cfg.style = this.style;
25092             }
25093             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25094             
25095             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25096         }
25097         
25098         this.el.select('>button.close').on('click', this.hide, this);
25099         
25100     },
25101     
25102     show : function()
25103     {
25104         if (!this.rendered) {
25105             this.render();
25106         }
25107         
25108         this.el.show();
25109         
25110         this.fireEvent('show', this);
25111         
25112     },
25113     
25114     hide : function()
25115     {
25116         if (!this.rendered) {
25117             this.render();
25118         }
25119         
25120         this.el.hide();
25121         
25122         this.fireEvent('hide', this);
25123     },
25124     
25125     update : function()
25126     {
25127 //        var e = this.el.dom.firstChild;
25128 //        
25129 //        if(this.closable){
25130 //            e = e.nextSibling;
25131 //        }
25132 //        
25133 //        e.data = this.html || '';
25134
25135         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25136     }
25137    
25138 });
25139
25140  
25141
25142      /*
25143  * - LGPL
25144  *
25145  * Graph
25146  * 
25147  */
25148
25149
25150 /**
25151  * @class Roo.bootstrap.Graph
25152  * @extends Roo.bootstrap.Component
25153  * Bootstrap Graph class
25154 > Prameters
25155  -sm {number} sm 4
25156  -md {number} md 5
25157  @cfg {String} graphtype  bar | vbar | pie
25158  @cfg {number} g_x coodinator | centre x (pie)
25159  @cfg {number} g_y coodinator | centre y (pie)
25160  @cfg {number} g_r radius (pie)
25161  @cfg {number} g_height height of the chart (respected by all elements in the set)
25162  @cfg {number} g_width width of the chart (respected by all elements in the set)
25163  @cfg {Object} title The title of the chart
25164     
25165  -{Array}  values
25166  -opts (object) options for the chart 
25167      o {
25168      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25169      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25170      o vgutter (number)
25171      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.
25172      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25173      o to
25174      o stretch (boolean)
25175      o }
25176  -opts (object) options for the pie
25177      o{
25178      o cut
25179      o startAngle (number)
25180      o endAngle (number)
25181      } 
25182  *
25183  * @constructor
25184  * Create a new Input
25185  * @param {Object} config The config object
25186  */
25187
25188 Roo.bootstrap.Graph = function(config){
25189     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25190     
25191     this.addEvents({
25192         // img events
25193         /**
25194          * @event click
25195          * The img click event for the img.
25196          * @param {Roo.EventObject} e
25197          */
25198         "click" : true
25199     });
25200 };
25201
25202 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25203     
25204     sm: 4,
25205     md: 5,
25206     graphtype: 'bar',
25207     g_height: 250,
25208     g_width: 400,
25209     g_x: 50,
25210     g_y: 50,
25211     g_r: 30,
25212     opts:{
25213         //g_colors: this.colors,
25214         g_type: 'soft',
25215         g_gutter: '20%'
25216
25217     },
25218     title : false,
25219
25220     getAutoCreate : function(){
25221         
25222         var cfg = {
25223             tag: 'div',
25224             html : null
25225         };
25226         
25227         
25228         return  cfg;
25229     },
25230
25231     onRender : function(ct,position){
25232         
25233         
25234         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25235         
25236         if (typeof(Raphael) == 'undefined') {
25237             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25238             return;
25239         }
25240         
25241         this.raphael = Raphael(this.el.dom);
25242         
25243                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25244                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25245                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25246                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25247                 /*
25248                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25249                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25250                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25251                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25252                 
25253                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25254                 r.barchart(330, 10, 300, 220, data1);
25255                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25256                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25257                 */
25258                 
25259                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25260                 // r.barchart(30, 30, 560, 250,  xdata, {
25261                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25262                 //     axis : "0 0 1 1",
25263                 //     axisxlabels :  xdata
25264                 //     //yvalues : cols,
25265                    
25266                 // });
25267 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25268 //        
25269 //        this.load(null,xdata,{
25270 //                axis : "0 0 1 1",
25271 //                axisxlabels :  xdata
25272 //                });
25273
25274     },
25275
25276     load : function(graphtype,xdata,opts)
25277     {
25278         this.raphael.clear();
25279         if(!graphtype) {
25280             graphtype = this.graphtype;
25281         }
25282         if(!opts){
25283             opts = this.opts;
25284         }
25285         var r = this.raphael,
25286             fin = function () {
25287                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25288             },
25289             fout = function () {
25290                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25291             },
25292             pfin = function() {
25293                 this.sector.stop();
25294                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25295
25296                 if (this.label) {
25297                     this.label[0].stop();
25298                     this.label[0].attr({ r: 7.5 });
25299                     this.label[1].attr({ "font-weight": 800 });
25300                 }
25301             },
25302             pfout = function() {
25303                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25304
25305                 if (this.label) {
25306                     this.label[0].animate({ r: 5 }, 500, "bounce");
25307                     this.label[1].attr({ "font-weight": 400 });
25308                 }
25309             };
25310
25311         switch(graphtype){
25312             case 'bar':
25313                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25314                 break;
25315             case 'hbar':
25316                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25317                 break;
25318             case 'pie':
25319 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25320 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25321 //            
25322                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25323                 
25324                 break;
25325
25326         }
25327         
25328         if(this.title){
25329             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25330         }
25331         
25332     },
25333     
25334     setTitle: function(o)
25335     {
25336         this.title = o;
25337     },
25338     
25339     initEvents: function() {
25340         
25341         if(!this.href){
25342             this.el.on('click', this.onClick, this);
25343         }
25344     },
25345     
25346     onClick : function(e)
25347     {
25348         Roo.log('img onclick');
25349         this.fireEvent('click', this, e);
25350     }
25351    
25352 });
25353
25354  
25355 /*
25356  * - LGPL
25357  *
25358  * numberBox
25359  * 
25360  */
25361 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25362
25363 /**
25364  * @class Roo.bootstrap.dash.NumberBox
25365  * @extends Roo.bootstrap.Component
25366  * Bootstrap NumberBox class
25367  * @cfg {String} headline Box headline
25368  * @cfg {String} content Box content
25369  * @cfg {String} icon Box icon
25370  * @cfg {String} footer Footer text
25371  * @cfg {String} fhref Footer href
25372  * 
25373  * @constructor
25374  * Create a new NumberBox
25375  * @param {Object} config The config object
25376  */
25377
25378
25379 Roo.bootstrap.dash.NumberBox = function(config){
25380     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25381     
25382 };
25383
25384 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25385     
25386     headline : '',
25387     content : '',
25388     icon : '',
25389     footer : '',
25390     fhref : '',
25391     ficon : '',
25392     
25393     getAutoCreate : function(){
25394         
25395         var cfg = {
25396             tag : 'div',
25397             cls : 'small-box ',
25398             cn : [
25399                 {
25400                     tag : 'div',
25401                     cls : 'inner',
25402                     cn :[
25403                         {
25404                             tag : 'h3',
25405                             cls : 'roo-headline',
25406                             html : this.headline
25407                         },
25408                         {
25409                             tag : 'p',
25410                             cls : 'roo-content',
25411                             html : this.content
25412                         }
25413                     ]
25414                 }
25415             ]
25416         };
25417         
25418         if(this.icon){
25419             cfg.cn.push({
25420                 tag : 'div',
25421                 cls : 'icon',
25422                 cn :[
25423                     {
25424                         tag : 'i',
25425                         cls : 'ion ' + this.icon
25426                     }
25427                 ]
25428             });
25429         }
25430         
25431         if(this.footer){
25432             var footer = {
25433                 tag : 'a',
25434                 cls : 'small-box-footer',
25435                 href : this.fhref || '#',
25436                 html : this.footer
25437             };
25438             
25439             cfg.cn.push(footer);
25440             
25441         }
25442         
25443         return  cfg;
25444     },
25445
25446     onRender : function(ct,position){
25447         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25448
25449
25450        
25451                 
25452     },
25453
25454     setHeadline: function (value)
25455     {
25456         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25457     },
25458     
25459     setFooter: function (value, href)
25460     {
25461         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25462         
25463         if(href){
25464             this.el.select('a.small-box-footer',true).first().attr('href', href);
25465         }
25466         
25467     },
25468
25469     setContent: function (value)
25470     {
25471         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25472     },
25473
25474     initEvents: function() 
25475     {   
25476         
25477     }
25478     
25479 });
25480
25481  
25482 /*
25483  * - LGPL
25484  *
25485  * TabBox
25486  * 
25487  */
25488 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25489
25490 /**
25491  * @class Roo.bootstrap.dash.TabBox
25492  * @extends Roo.bootstrap.Component
25493  * Bootstrap TabBox class
25494  * @cfg {String} title Title of the TabBox
25495  * @cfg {String} icon Icon of the TabBox
25496  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25497  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25498  * 
25499  * @constructor
25500  * Create a new TabBox
25501  * @param {Object} config The config object
25502  */
25503
25504
25505 Roo.bootstrap.dash.TabBox = function(config){
25506     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25507     this.addEvents({
25508         // raw events
25509         /**
25510          * @event addpane
25511          * When a pane is added
25512          * @param {Roo.bootstrap.dash.TabPane} pane
25513          */
25514         "addpane" : true,
25515         /**
25516          * @event activatepane
25517          * When a pane is activated
25518          * @param {Roo.bootstrap.dash.TabPane} pane
25519          */
25520         "activatepane" : true
25521         
25522          
25523     });
25524     
25525     this.panes = [];
25526 };
25527
25528 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25529
25530     title : '',
25531     icon : false,
25532     showtabs : true,
25533     tabScrollable : false,
25534     
25535     getChildContainer : function()
25536     {
25537         return this.el.select('.tab-content', true).first();
25538     },
25539     
25540     getAutoCreate : function(){
25541         
25542         var header = {
25543             tag: 'li',
25544             cls: 'pull-left header',
25545             html: this.title,
25546             cn : []
25547         };
25548         
25549         if(this.icon){
25550             header.cn.push({
25551                 tag: 'i',
25552                 cls: 'fa ' + this.icon
25553             });
25554         }
25555         
25556         var h = {
25557             tag: 'ul',
25558             cls: 'nav nav-tabs pull-right',
25559             cn: [
25560                 header
25561             ]
25562         };
25563         
25564         if(this.tabScrollable){
25565             h = {
25566                 tag: 'div',
25567                 cls: 'tab-header',
25568                 cn: [
25569                     {
25570                         tag: 'ul',
25571                         cls: 'nav nav-tabs pull-right',
25572                         cn: [
25573                             header
25574                         ]
25575                     }
25576                 ]
25577             };
25578         }
25579         
25580         var cfg = {
25581             tag: 'div',
25582             cls: 'nav-tabs-custom',
25583             cn: [
25584                 h,
25585                 {
25586                     tag: 'div',
25587                     cls: 'tab-content no-padding',
25588                     cn: []
25589                 }
25590             ]
25591         };
25592
25593         return  cfg;
25594     },
25595     initEvents : function()
25596     {
25597         //Roo.log('add add pane handler');
25598         this.on('addpane', this.onAddPane, this);
25599     },
25600      /**
25601      * Updates the box title
25602      * @param {String} html to set the title to.
25603      */
25604     setTitle : function(value)
25605     {
25606         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25607     },
25608     onAddPane : function(pane)
25609     {
25610         this.panes.push(pane);
25611         //Roo.log('addpane');
25612         //Roo.log(pane);
25613         // tabs are rendere left to right..
25614         if(!this.showtabs){
25615             return;
25616         }
25617         
25618         var ctr = this.el.select('.nav-tabs', true).first();
25619          
25620          
25621         var existing = ctr.select('.nav-tab',true);
25622         var qty = existing.getCount();;
25623         
25624         
25625         var tab = ctr.createChild({
25626             tag : 'li',
25627             cls : 'nav-tab' + (qty ? '' : ' active'),
25628             cn : [
25629                 {
25630                     tag : 'a',
25631                     href:'#',
25632                     html : pane.title
25633                 }
25634             ]
25635         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25636         pane.tab = tab;
25637         
25638         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25639         if (!qty) {
25640             pane.el.addClass('active');
25641         }
25642         
25643                 
25644     },
25645     onTabClick : function(ev,un,ob,pane)
25646     {
25647         //Roo.log('tab - prev default');
25648         ev.preventDefault();
25649         
25650         
25651         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25652         pane.tab.addClass('active');
25653         //Roo.log(pane.title);
25654         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25655         // technically we should have a deactivate event.. but maybe add later.
25656         // and it should not de-activate the selected tab...
25657         this.fireEvent('activatepane', pane);
25658         pane.el.addClass('active');
25659         pane.fireEvent('activate');
25660         
25661         
25662     },
25663     
25664     getActivePane : function()
25665     {
25666         var r = false;
25667         Roo.each(this.panes, function(p) {
25668             if(p.el.hasClass('active')){
25669                 r = p;
25670                 return false;
25671             }
25672             
25673             return;
25674         });
25675         
25676         return r;
25677     }
25678     
25679     
25680 });
25681
25682  
25683 /*
25684  * - LGPL
25685  *
25686  * Tab pane
25687  * 
25688  */
25689 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25690 /**
25691  * @class Roo.bootstrap.TabPane
25692  * @extends Roo.bootstrap.Component
25693  * Bootstrap TabPane class
25694  * @cfg {Boolean} active (false | true) Default false
25695  * @cfg {String} title title of panel
25696
25697  * 
25698  * @constructor
25699  * Create a new TabPane
25700  * @param {Object} config The config object
25701  */
25702
25703 Roo.bootstrap.dash.TabPane = function(config){
25704     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25705     
25706     this.addEvents({
25707         // raw events
25708         /**
25709          * @event activate
25710          * When a pane is activated
25711          * @param {Roo.bootstrap.dash.TabPane} pane
25712          */
25713         "activate" : true
25714          
25715     });
25716 };
25717
25718 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25719     
25720     active : false,
25721     title : '',
25722     
25723     // the tabBox that this is attached to.
25724     tab : false,
25725      
25726     getAutoCreate : function() 
25727     {
25728         var cfg = {
25729             tag: 'div',
25730             cls: 'tab-pane'
25731         };
25732         
25733         if(this.active){
25734             cfg.cls += ' active';
25735         }
25736         
25737         return cfg;
25738     },
25739     initEvents  : function()
25740     {
25741         //Roo.log('trigger add pane handler');
25742         this.parent().fireEvent('addpane', this)
25743     },
25744     
25745      /**
25746      * Updates the tab title 
25747      * @param {String} html to set the title to.
25748      */
25749     setTitle: function(str)
25750     {
25751         if (!this.tab) {
25752             return;
25753         }
25754         this.title = str;
25755         this.tab.select('a', true).first().dom.innerHTML = str;
25756         
25757     }
25758     
25759     
25760     
25761 });
25762
25763  
25764
25765
25766  /*
25767  * - LGPL
25768  *
25769  * menu
25770  * 
25771  */
25772 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25773
25774 /**
25775  * @class Roo.bootstrap.menu.Menu
25776  * @extends Roo.bootstrap.Component
25777  * Bootstrap Menu class - container for Menu
25778  * @cfg {String} html Text of the menu
25779  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25780  * @cfg {String} icon Font awesome icon
25781  * @cfg {String} pos Menu align to (top | bottom) default bottom
25782  * 
25783  * 
25784  * @constructor
25785  * Create a new Menu
25786  * @param {Object} config The config object
25787  */
25788
25789
25790 Roo.bootstrap.menu.Menu = function(config){
25791     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25792     
25793     this.addEvents({
25794         /**
25795          * @event beforeshow
25796          * Fires before this menu is displayed
25797          * @param {Roo.bootstrap.menu.Menu} this
25798          */
25799         beforeshow : true,
25800         /**
25801          * @event beforehide
25802          * Fires before this menu is hidden
25803          * @param {Roo.bootstrap.menu.Menu} this
25804          */
25805         beforehide : true,
25806         /**
25807          * @event show
25808          * Fires after this menu is displayed
25809          * @param {Roo.bootstrap.menu.Menu} this
25810          */
25811         show : true,
25812         /**
25813          * @event hide
25814          * Fires after this menu is hidden
25815          * @param {Roo.bootstrap.menu.Menu} this
25816          */
25817         hide : true,
25818         /**
25819          * @event click
25820          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25821          * @param {Roo.bootstrap.menu.Menu} this
25822          * @param {Roo.EventObject} e
25823          */
25824         click : true
25825     });
25826     
25827 };
25828
25829 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25830     
25831     submenu : false,
25832     html : '',
25833     weight : 'default',
25834     icon : false,
25835     pos : 'bottom',
25836     
25837     
25838     getChildContainer : function() {
25839         if(this.isSubMenu){
25840             return this.el;
25841         }
25842         
25843         return this.el.select('ul.dropdown-menu', true).first();  
25844     },
25845     
25846     getAutoCreate : function()
25847     {
25848         var text = [
25849             {
25850                 tag : 'span',
25851                 cls : 'roo-menu-text',
25852                 html : this.html
25853             }
25854         ];
25855         
25856         if(this.icon){
25857             text.unshift({
25858                 tag : 'i',
25859                 cls : 'fa ' + this.icon
25860             })
25861         }
25862         
25863         
25864         var cfg = {
25865             tag : 'div',
25866             cls : 'btn-group',
25867             cn : [
25868                 {
25869                     tag : 'button',
25870                     cls : 'dropdown-button btn btn-' + this.weight,
25871                     cn : text
25872                 },
25873                 {
25874                     tag : 'button',
25875                     cls : 'dropdown-toggle btn btn-' + this.weight,
25876                     cn : [
25877                         {
25878                             tag : 'span',
25879                             cls : 'caret'
25880                         }
25881                     ]
25882                 },
25883                 {
25884                     tag : 'ul',
25885                     cls : 'dropdown-menu'
25886                 }
25887             ]
25888             
25889         };
25890         
25891         if(this.pos == 'top'){
25892             cfg.cls += ' dropup';
25893         }
25894         
25895         if(this.isSubMenu){
25896             cfg = {
25897                 tag : 'ul',
25898                 cls : 'dropdown-menu'
25899             }
25900         }
25901         
25902         return cfg;
25903     },
25904     
25905     onRender : function(ct, position)
25906     {
25907         this.isSubMenu = ct.hasClass('dropdown-submenu');
25908         
25909         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25910     },
25911     
25912     initEvents : function() 
25913     {
25914         if(this.isSubMenu){
25915             return;
25916         }
25917         
25918         this.hidden = true;
25919         
25920         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25921         this.triggerEl.on('click', this.onTriggerPress, this);
25922         
25923         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25924         this.buttonEl.on('click', this.onClick, this);
25925         
25926     },
25927     
25928     list : function()
25929     {
25930         if(this.isSubMenu){
25931             return this.el;
25932         }
25933         
25934         return this.el.select('ul.dropdown-menu', true).first();
25935     },
25936     
25937     onClick : function(e)
25938     {
25939         this.fireEvent("click", this, e);
25940     },
25941     
25942     onTriggerPress  : function(e)
25943     {   
25944         if (this.isVisible()) {
25945             this.hide();
25946         } else {
25947             this.show();
25948         }
25949     },
25950     
25951     isVisible : function(){
25952         return !this.hidden;
25953     },
25954     
25955     show : function()
25956     {
25957         this.fireEvent("beforeshow", this);
25958         
25959         this.hidden = false;
25960         this.el.addClass('open');
25961         
25962         Roo.get(document).on("mouseup", this.onMouseUp, this);
25963         
25964         this.fireEvent("show", this);
25965         
25966         
25967     },
25968     
25969     hide : function()
25970     {
25971         this.fireEvent("beforehide", this);
25972         
25973         this.hidden = true;
25974         this.el.removeClass('open');
25975         
25976         Roo.get(document).un("mouseup", this.onMouseUp);
25977         
25978         this.fireEvent("hide", this);
25979     },
25980     
25981     onMouseUp : function()
25982     {
25983         this.hide();
25984     }
25985     
25986 });
25987
25988  
25989  /*
25990  * - LGPL
25991  *
25992  * menu item
25993  * 
25994  */
25995 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25996
25997 /**
25998  * @class Roo.bootstrap.menu.Item
25999  * @extends Roo.bootstrap.Component
26000  * Bootstrap MenuItem class
26001  * @cfg {Boolean} submenu (true | false) default false
26002  * @cfg {String} html text of the item
26003  * @cfg {String} href the link
26004  * @cfg {Boolean} disable (true | false) default false
26005  * @cfg {Boolean} preventDefault (true | false) default true
26006  * @cfg {String} icon Font awesome icon
26007  * @cfg {String} pos Submenu align to (left | right) default right 
26008  * 
26009  * 
26010  * @constructor
26011  * Create a new Item
26012  * @param {Object} config The config object
26013  */
26014
26015
26016 Roo.bootstrap.menu.Item = function(config){
26017     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26018     this.addEvents({
26019         /**
26020          * @event mouseover
26021          * Fires when the mouse is hovering over this menu
26022          * @param {Roo.bootstrap.menu.Item} this
26023          * @param {Roo.EventObject} e
26024          */
26025         mouseover : true,
26026         /**
26027          * @event mouseout
26028          * Fires when the mouse exits this menu
26029          * @param {Roo.bootstrap.menu.Item} this
26030          * @param {Roo.EventObject} e
26031          */
26032         mouseout : true,
26033         // raw events
26034         /**
26035          * @event click
26036          * The raw click event for the entire grid.
26037          * @param {Roo.EventObject} e
26038          */
26039         click : true
26040     });
26041 };
26042
26043 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26044     
26045     submenu : false,
26046     href : '',
26047     html : '',
26048     preventDefault: true,
26049     disable : false,
26050     icon : false,
26051     pos : 'right',
26052     
26053     getAutoCreate : function()
26054     {
26055         var text = [
26056             {
26057                 tag : 'span',
26058                 cls : 'roo-menu-item-text',
26059                 html : this.html
26060             }
26061         ];
26062         
26063         if(this.icon){
26064             text.unshift({
26065                 tag : 'i',
26066                 cls : 'fa ' + this.icon
26067             })
26068         }
26069         
26070         var cfg = {
26071             tag : 'li',
26072             cn : [
26073                 {
26074                     tag : 'a',
26075                     href : this.href || '#',
26076                     cn : text
26077                 }
26078             ]
26079         };
26080         
26081         if(this.disable){
26082             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26083         }
26084         
26085         if(this.submenu){
26086             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26087             
26088             if(this.pos == 'left'){
26089                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26090             }
26091         }
26092         
26093         return cfg;
26094     },
26095     
26096     initEvents : function() 
26097     {
26098         this.el.on('mouseover', this.onMouseOver, this);
26099         this.el.on('mouseout', this.onMouseOut, this);
26100         
26101         this.el.select('a', true).first().on('click', this.onClick, this);
26102         
26103     },
26104     
26105     onClick : function(e)
26106     {
26107         if(this.preventDefault){
26108             e.preventDefault();
26109         }
26110         
26111         this.fireEvent("click", this, e);
26112     },
26113     
26114     onMouseOver : function(e)
26115     {
26116         if(this.submenu && this.pos == 'left'){
26117             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26118         }
26119         
26120         this.fireEvent("mouseover", this, e);
26121     },
26122     
26123     onMouseOut : function(e)
26124     {
26125         this.fireEvent("mouseout", this, e);
26126     }
26127 });
26128
26129  
26130
26131  /*
26132  * - LGPL
26133  *
26134  * menu separator
26135  * 
26136  */
26137 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26138
26139 /**
26140  * @class Roo.bootstrap.menu.Separator
26141  * @extends Roo.bootstrap.Component
26142  * Bootstrap Separator class
26143  * 
26144  * @constructor
26145  * Create a new Separator
26146  * @param {Object} config The config object
26147  */
26148
26149
26150 Roo.bootstrap.menu.Separator = function(config){
26151     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26152 };
26153
26154 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26155     
26156     getAutoCreate : function(){
26157         var cfg = {
26158             tag : 'li',
26159             cls: 'divider'
26160         };
26161         
26162         return cfg;
26163     }
26164    
26165 });
26166
26167  
26168
26169  /*
26170  * - LGPL
26171  *
26172  * Tooltip
26173  * 
26174  */
26175
26176 /**
26177  * @class Roo.bootstrap.Tooltip
26178  * Bootstrap Tooltip class
26179  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26180  * to determine which dom element triggers the tooltip.
26181  * 
26182  * It needs to add support for additional attributes like tooltip-position
26183  * 
26184  * @constructor
26185  * Create a new Toolti
26186  * @param {Object} config The config object
26187  */
26188
26189 Roo.bootstrap.Tooltip = function(config){
26190     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26191     
26192     this.alignment = Roo.bootstrap.Tooltip.alignment;
26193     
26194     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26195         this.alignment = config.alignment;
26196     }
26197     
26198 };
26199
26200 Roo.apply(Roo.bootstrap.Tooltip, {
26201     /**
26202      * @function init initialize tooltip monitoring.
26203      * @static
26204      */
26205     currentEl : false,
26206     currentTip : false,
26207     currentRegion : false,
26208     
26209     //  init : delay?
26210     
26211     init : function()
26212     {
26213         Roo.get(document).on('mouseover', this.enter ,this);
26214         Roo.get(document).on('mouseout', this.leave, this);
26215          
26216         
26217         this.currentTip = new Roo.bootstrap.Tooltip();
26218     },
26219     
26220     enter : function(ev)
26221     {
26222         var dom = ev.getTarget();
26223         
26224         //Roo.log(['enter',dom]);
26225         var el = Roo.fly(dom);
26226         if (this.currentEl) {
26227             //Roo.log(dom);
26228             //Roo.log(this.currentEl);
26229             //Roo.log(this.currentEl.contains(dom));
26230             if (this.currentEl == el) {
26231                 return;
26232             }
26233             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26234                 return;
26235             }
26236
26237         }
26238         
26239         if (this.currentTip.el) {
26240             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26241         }    
26242         //Roo.log(ev);
26243         
26244         if(!el || el.dom == document){
26245             return;
26246         }
26247         
26248         var bindEl = el;
26249         
26250         // you can not look for children, as if el is the body.. then everythign is the child..
26251         if (!el.attr('tooltip')) { //
26252             if (!el.select("[tooltip]").elements.length) {
26253                 return;
26254             }
26255             // is the mouse over this child...?
26256             bindEl = el.select("[tooltip]").first();
26257             var xy = ev.getXY();
26258             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26259                 //Roo.log("not in region.");
26260                 return;
26261             }
26262             //Roo.log("child element over..");
26263             
26264         }
26265         this.currentEl = bindEl;
26266         this.currentTip.bind(bindEl);
26267         this.currentRegion = Roo.lib.Region.getRegion(dom);
26268         this.currentTip.enter();
26269         
26270     },
26271     leave : function(ev)
26272     {
26273         var dom = ev.getTarget();
26274         //Roo.log(['leave',dom]);
26275         if (!this.currentEl) {
26276             return;
26277         }
26278         
26279         
26280         if (dom != this.currentEl.dom) {
26281             return;
26282         }
26283         var xy = ev.getXY();
26284         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26285             return;
26286         }
26287         // only activate leave if mouse cursor is outside... bounding box..
26288         
26289         
26290         
26291         
26292         if (this.currentTip) {
26293             this.currentTip.leave();
26294         }
26295         //Roo.log('clear currentEl');
26296         this.currentEl = false;
26297         
26298         
26299     },
26300     alignment : {
26301         'left' : ['r-l', [-2,0], 'right'],
26302         'right' : ['l-r', [2,0], 'left'],
26303         'bottom' : ['t-b', [0,2], 'top'],
26304         'top' : [ 'b-t', [0,-2], 'bottom']
26305     }
26306     
26307 });
26308
26309
26310 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26311     
26312     
26313     bindEl : false,
26314     
26315     delay : null, // can be { show : 300 , hide: 500}
26316     
26317     timeout : null,
26318     
26319     hoverState : null, //???
26320     
26321     placement : 'bottom', 
26322     
26323     alignment : false,
26324     
26325     getAutoCreate : function(){
26326     
26327         var cfg = {
26328            cls : 'tooltip',
26329            role : 'tooltip',
26330            cn : [
26331                 {
26332                     cls : 'tooltip-arrow'
26333                 },
26334                 {
26335                     cls : 'tooltip-inner'
26336                 }
26337            ]
26338         };
26339         
26340         return cfg;
26341     },
26342     bind : function(el)
26343     {
26344         this.bindEl = el;
26345     },
26346       
26347     
26348     enter : function () {
26349        
26350         if (this.timeout != null) {
26351             clearTimeout(this.timeout);
26352         }
26353         
26354         this.hoverState = 'in';
26355          //Roo.log("enter - show");
26356         if (!this.delay || !this.delay.show) {
26357             this.show();
26358             return;
26359         }
26360         var _t = this;
26361         this.timeout = setTimeout(function () {
26362             if (_t.hoverState == 'in') {
26363                 _t.show();
26364             }
26365         }, this.delay.show);
26366     },
26367     leave : function()
26368     {
26369         clearTimeout(this.timeout);
26370     
26371         this.hoverState = 'out';
26372          if (!this.delay || !this.delay.hide) {
26373             this.hide();
26374             return;
26375         }
26376        
26377         var _t = this;
26378         this.timeout = setTimeout(function () {
26379             //Roo.log("leave - timeout");
26380             
26381             if (_t.hoverState == 'out') {
26382                 _t.hide();
26383                 Roo.bootstrap.Tooltip.currentEl = false;
26384             }
26385         }, delay);
26386     },
26387     
26388     show : function (msg)
26389     {
26390         if (!this.el) {
26391             this.render(document.body);
26392         }
26393         // set content.
26394         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26395         
26396         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26397         
26398         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26399         
26400         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26401         
26402         var placement = typeof this.placement == 'function' ?
26403             this.placement.call(this, this.el, on_el) :
26404             this.placement;
26405             
26406         var autoToken = /\s?auto?\s?/i;
26407         var autoPlace = autoToken.test(placement);
26408         if (autoPlace) {
26409             placement = placement.replace(autoToken, '') || 'top';
26410         }
26411         
26412         //this.el.detach()
26413         //this.el.setXY([0,0]);
26414         this.el.show();
26415         //this.el.dom.style.display='block';
26416         
26417         //this.el.appendTo(on_el);
26418         
26419         var p = this.getPosition();
26420         var box = this.el.getBox();
26421         
26422         if (autoPlace) {
26423             // fixme..
26424         }
26425         
26426         var align = this.alignment[placement];
26427         
26428         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26429         
26430         if(placement == 'top' || placement == 'bottom'){
26431             if(xy[0] < 0){
26432                 placement = 'right';
26433             }
26434             
26435             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26436                 placement = 'left';
26437             }
26438             
26439             var scroll = Roo.select('body', true).first().getScroll();
26440             
26441             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26442                 placement = 'top';
26443             }
26444             
26445             align = this.alignment[placement];
26446         }
26447         
26448         this.el.alignTo(this.bindEl, align[0],align[1]);
26449         //var arrow = this.el.select('.arrow',true).first();
26450         //arrow.set(align[2], 
26451         
26452         this.el.addClass(placement);
26453         
26454         this.el.addClass('in fade');
26455         
26456         this.hoverState = null;
26457         
26458         if (this.el.hasClass('fade')) {
26459             // fade it?
26460         }
26461         
26462     },
26463     hide : function()
26464     {
26465          
26466         if (!this.el) {
26467             return;
26468         }
26469         //this.el.setXY([0,0]);
26470         this.el.removeClass('in');
26471         //this.el.hide();
26472         
26473     }
26474     
26475 });
26476  
26477
26478  /*
26479  * - LGPL
26480  *
26481  * Location Picker
26482  * 
26483  */
26484
26485 /**
26486  * @class Roo.bootstrap.LocationPicker
26487  * @extends Roo.bootstrap.Component
26488  * Bootstrap LocationPicker class
26489  * @cfg {Number} latitude Position when init default 0
26490  * @cfg {Number} longitude Position when init default 0
26491  * @cfg {Number} zoom default 15
26492  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26493  * @cfg {Boolean} mapTypeControl default false
26494  * @cfg {Boolean} disableDoubleClickZoom default false
26495  * @cfg {Boolean} scrollwheel default true
26496  * @cfg {Boolean} streetViewControl default false
26497  * @cfg {Number} radius default 0
26498  * @cfg {String} locationName
26499  * @cfg {Boolean} draggable default true
26500  * @cfg {Boolean} enableAutocomplete default false
26501  * @cfg {Boolean} enableReverseGeocode default true
26502  * @cfg {String} markerTitle
26503  * 
26504  * @constructor
26505  * Create a new LocationPicker
26506  * @param {Object} config The config object
26507  */
26508
26509
26510 Roo.bootstrap.LocationPicker = function(config){
26511     
26512     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26513     
26514     this.addEvents({
26515         /**
26516          * @event initial
26517          * Fires when the picker initialized.
26518          * @param {Roo.bootstrap.LocationPicker} this
26519          * @param {Google Location} location
26520          */
26521         initial : true,
26522         /**
26523          * @event positionchanged
26524          * Fires when the picker position changed.
26525          * @param {Roo.bootstrap.LocationPicker} this
26526          * @param {Google Location} location
26527          */
26528         positionchanged : true,
26529         /**
26530          * @event resize
26531          * Fires when the map resize.
26532          * @param {Roo.bootstrap.LocationPicker} this
26533          */
26534         resize : true,
26535         /**
26536          * @event show
26537          * Fires when the map show.
26538          * @param {Roo.bootstrap.LocationPicker} this
26539          */
26540         show : true,
26541         /**
26542          * @event hide
26543          * Fires when the map hide.
26544          * @param {Roo.bootstrap.LocationPicker} this
26545          */
26546         hide : true,
26547         /**
26548          * @event mapClick
26549          * Fires when click the map.
26550          * @param {Roo.bootstrap.LocationPicker} this
26551          * @param {Map event} e
26552          */
26553         mapClick : true,
26554         /**
26555          * @event mapRightClick
26556          * Fires when right click the map.
26557          * @param {Roo.bootstrap.LocationPicker} this
26558          * @param {Map event} e
26559          */
26560         mapRightClick : true,
26561         /**
26562          * @event markerClick
26563          * Fires when click the marker.
26564          * @param {Roo.bootstrap.LocationPicker} this
26565          * @param {Map event} e
26566          */
26567         markerClick : true,
26568         /**
26569          * @event markerRightClick
26570          * Fires when right click the marker.
26571          * @param {Roo.bootstrap.LocationPicker} this
26572          * @param {Map event} e
26573          */
26574         markerRightClick : true,
26575         /**
26576          * @event OverlayViewDraw
26577          * Fires when OverlayView Draw
26578          * @param {Roo.bootstrap.LocationPicker} this
26579          */
26580         OverlayViewDraw : true,
26581         /**
26582          * @event OverlayViewOnAdd
26583          * Fires when OverlayView Draw
26584          * @param {Roo.bootstrap.LocationPicker} this
26585          */
26586         OverlayViewOnAdd : true,
26587         /**
26588          * @event OverlayViewOnRemove
26589          * Fires when OverlayView Draw
26590          * @param {Roo.bootstrap.LocationPicker} this
26591          */
26592         OverlayViewOnRemove : true,
26593         /**
26594          * @event OverlayViewShow
26595          * Fires when OverlayView Draw
26596          * @param {Roo.bootstrap.LocationPicker} this
26597          * @param {Pixel} cpx
26598          */
26599         OverlayViewShow : true,
26600         /**
26601          * @event OverlayViewHide
26602          * Fires when OverlayView Draw
26603          * @param {Roo.bootstrap.LocationPicker} this
26604          */
26605         OverlayViewHide : true,
26606         /**
26607          * @event loadexception
26608          * Fires when load google lib failed.
26609          * @param {Roo.bootstrap.LocationPicker} this
26610          */
26611         loadexception : true
26612     });
26613         
26614 };
26615
26616 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26617     
26618     gMapContext: false,
26619     
26620     latitude: 0,
26621     longitude: 0,
26622     zoom: 15,
26623     mapTypeId: false,
26624     mapTypeControl: false,
26625     disableDoubleClickZoom: false,
26626     scrollwheel: true,
26627     streetViewControl: false,
26628     radius: 0,
26629     locationName: '',
26630     draggable: true,
26631     enableAutocomplete: false,
26632     enableReverseGeocode: true,
26633     markerTitle: '',
26634     
26635     getAutoCreate: function()
26636     {
26637
26638         var cfg = {
26639             tag: 'div',
26640             cls: 'roo-location-picker'
26641         };
26642         
26643         return cfg
26644     },
26645     
26646     initEvents: function(ct, position)
26647     {       
26648         if(!this.el.getWidth() || this.isApplied()){
26649             return;
26650         }
26651         
26652         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26653         
26654         this.initial();
26655     },
26656     
26657     initial: function()
26658     {
26659         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26660             this.fireEvent('loadexception', this);
26661             return;
26662         }
26663         
26664         if(!this.mapTypeId){
26665             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26666         }
26667         
26668         this.gMapContext = this.GMapContext();
26669         
26670         this.initOverlayView();
26671         
26672         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26673         
26674         var _this = this;
26675                 
26676         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26677             _this.setPosition(_this.gMapContext.marker.position);
26678         });
26679         
26680         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26681             _this.fireEvent('mapClick', this, event);
26682             
26683         });
26684
26685         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26686             _this.fireEvent('mapRightClick', this, event);
26687             
26688         });
26689         
26690         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26691             _this.fireEvent('markerClick', this, event);
26692             
26693         });
26694
26695         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26696             _this.fireEvent('markerRightClick', this, event);
26697             
26698         });
26699         
26700         this.setPosition(this.gMapContext.location);
26701         
26702         this.fireEvent('initial', this, this.gMapContext.location);
26703     },
26704     
26705     initOverlayView: function()
26706     {
26707         var _this = this;
26708         
26709         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26710             
26711             draw: function()
26712             {
26713                 _this.fireEvent('OverlayViewDraw', _this);
26714             },
26715             
26716             onAdd: function()
26717             {
26718                 _this.fireEvent('OverlayViewOnAdd', _this);
26719             },
26720             
26721             onRemove: function()
26722             {
26723                 _this.fireEvent('OverlayViewOnRemove', _this);
26724             },
26725             
26726             show: function(cpx)
26727             {
26728                 _this.fireEvent('OverlayViewShow', _this, cpx);
26729             },
26730             
26731             hide: function()
26732             {
26733                 _this.fireEvent('OverlayViewHide', _this);
26734             }
26735             
26736         });
26737     },
26738     
26739     fromLatLngToContainerPixel: function(event)
26740     {
26741         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26742     },
26743     
26744     isApplied: function() 
26745     {
26746         return this.getGmapContext() == false ? false : true;
26747     },
26748     
26749     getGmapContext: function() 
26750     {
26751         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26752     },
26753     
26754     GMapContext: function() 
26755     {
26756         var position = new google.maps.LatLng(this.latitude, this.longitude);
26757         
26758         var _map = new google.maps.Map(this.el.dom, {
26759             center: position,
26760             zoom: this.zoom,
26761             mapTypeId: this.mapTypeId,
26762             mapTypeControl: this.mapTypeControl,
26763             disableDoubleClickZoom: this.disableDoubleClickZoom,
26764             scrollwheel: this.scrollwheel,
26765             streetViewControl: this.streetViewControl,
26766             locationName: this.locationName,
26767             draggable: this.draggable,
26768             enableAutocomplete: this.enableAutocomplete,
26769             enableReverseGeocode: this.enableReverseGeocode
26770         });
26771         
26772         var _marker = new google.maps.Marker({
26773             position: position,
26774             map: _map,
26775             title: this.markerTitle,
26776             draggable: this.draggable
26777         });
26778         
26779         return {
26780             map: _map,
26781             marker: _marker,
26782             circle: null,
26783             location: position,
26784             radius: this.radius,
26785             locationName: this.locationName,
26786             addressComponents: {
26787                 formatted_address: null,
26788                 addressLine1: null,
26789                 addressLine2: null,
26790                 streetName: null,
26791                 streetNumber: null,
26792                 city: null,
26793                 district: null,
26794                 state: null,
26795                 stateOrProvince: null
26796             },
26797             settings: this,
26798             domContainer: this.el.dom,
26799             geodecoder: new google.maps.Geocoder()
26800         };
26801     },
26802     
26803     drawCircle: function(center, radius, options) 
26804     {
26805         if (this.gMapContext.circle != null) {
26806             this.gMapContext.circle.setMap(null);
26807         }
26808         if (radius > 0) {
26809             radius *= 1;
26810             options = Roo.apply({}, options, {
26811                 strokeColor: "#0000FF",
26812                 strokeOpacity: .35,
26813                 strokeWeight: 2,
26814                 fillColor: "#0000FF",
26815                 fillOpacity: .2
26816             });
26817             
26818             options.map = this.gMapContext.map;
26819             options.radius = radius;
26820             options.center = center;
26821             this.gMapContext.circle = new google.maps.Circle(options);
26822             return this.gMapContext.circle;
26823         }
26824         
26825         return null;
26826     },
26827     
26828     setPosition: function(location) 
26829     {
26830         this.gMapContext.location = location;
26831         this.gMapContext.marker.setPosition(location);
26832         this.gMapContext.map.panTo(location);
26833         this.drawCircle(location, this.gMapContext.radius, {});
26834         
26835         var _this = this;
26836         
26837         if (this.gMapContext.settings.enableReverseGeocode) {
26838             this.gMapContext.geodecoder.geocode({
26839                 latLng: this.gMapContext.location
26840             }, function(results, status) {
26841                 
26842                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26843                     _this.gMapContext.locationName = results[0].formatted_address;
26844                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26845                     
26846                     _this.fireEvent('positionchanged', this, location);
26847                 }
26848             });
26849             
26850             return;
26851         }
26852         
26853         this.fireEvent('positionchanged', this, location);
26854     },
26855     
26856     resize: function()
26857     {
26858         google.maps.event.trigger(this.gMapContext.map, "resize");
26859         
26860         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26861         
26862         this.fireEvent('resize', this);
26863     },
26864     
26865     setPositionByLatLng: function(latitude, longitude)
26866     {
26867         this.setPosition(new google.maps.LatLng(latitude, longitude));
26868     },
26869     
26870     getCurrentPosition: function() 
26871     {
26872         return {
26873             latitude: this.gMapContext.location.lat(),
26874             longitude: this.gMapContext.location.lng()
26875         };
26876     },
26877     
26878     getAddressName: function() 
26879     {
26880         return this.gMapContext.locationName;
26881     },
26882     
26883     getAddressComponents: function() 
26884     {
26885         return this.gMapContext.addressComponents;
26886     },
26887     
26888     address_component_from_google_geocode: function(address_components) 
26889     {
26890         var result = {};
26891         
26892         for (var i = 0; i < address_components.length; i++) {
26893             var component = address_components[i];
26894             if (component.types.indexOf("postal_code") >= 0) {
26895                 result.postalCode = component.short_name;
26896             } else if (component.types.indexOf("street_number") >= 0) {
26897                 result.streetNumber = component.short_name;
26898             } else if (component.types.indexOf("route") >= 0) {
26899                 result.streetName = component.short_name;
26900             } else if (component.types.indexOf("neighborhood") >= 0) {
26901                 result.city = component.short_name;
26902             } else if (component.types.indexOf("locality") >= 0) {
26903                 result.city = component.short_name;
26904             } else if (component.types.indexOf("sublocality") >= 0) {
26905                 result.district = component.short_name;
26906             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26907                 result.stateOrProvince = component.short_name;
26908             } else if (component.types.indexOf("country") >= 0) {
26909                 result.country = component.short_name;
26910             }
26911         }
26912         
26913         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26914         result.addressLine2 = "";
26915         return result;
26916     },
26917     
26918     setZoomLevel: function(zoom)
26919     {
26920         this.gMapContext.map.setZoom(zoom);
26921     },
26922     
26923     show: function()
26924     {
26925         if(!this.el){
26926             return;
26927         }
26928         
26929         this.el.show();
26930         
26931         this.resize();
26932         
26933         this.fireEvent('show', this);
26934     },
26935     
26936     hide: function()
26937     {
26938         if(!this.el){
26939             return;
26940         }
26941         
26942         this.el.hide();
26943         
26944         this.fireEvent('hide', this);
26945     }
26946     
26947 });
26948
26949 Roo.apply(Roo.bootstrap.LocationPicker, {
26950     
26951     OverlayView : function(map, options)
26952     {
26953         options = options || {};
26954         
26955         this.setMap(map);
26956     }
26957     
26958     
26959 });/*
26960  * - LGPL
26961  *
26962  * Alert
26963  * 
26964  */
26965
26966 /**
26967  * @class Roo.bootstrap.Alert
26968  * @extends Roo.bootstrap.Component
26969  * Bootstrap Alert class
26970  * @cfg {String} title The title of alert
26971  * @cfg {String} html The content of alert
26972  * @cfg {String} weight (  success | info | warning | danger )
26973  * @cfg {String} faicon font-awesomeicon
26974  * 
26975  * @constructor
26976  * Create a new alert
26977  * @param {Object} config The config object
26978  */
26979
26980
26981 Roo.bootstrap.Alert = function(config){
26982     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26983     
26984 };
26985
26986 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26987     
26988     title: '',
26989     html: '',
26990     weight: false,
26991     faicon: false,
26992     
26993     getAutoCreate : function()
26994     {
26995         
26996         var cfg = {
26997             tag : 'div',
26998             cls : 'alert',
26999             cn : [
27000                 {
27001                     tag : 'i',
27002                     cls : 'roo-alert-icon'
27003                     
27004                 },
27005                 {
27006                     tag : 'b',
27007                     cls : 'roo-alert-title',
27008                     html : this.title
27009                 },
27010                 {
27011                     tag : 'span',
27012                     cls : 'roo-alert-text',
27013                     html : this.html
27014                 }
27015             ]
27016         };
27017         
27018         if(this.faicon){
27019             cfg.cn[0].cls += ' fa ' + this.faicon;
27020         }
27021         
27022         if(this.weight){
27023             cfg.cls += ' alert-' + this.weight;
27024         }
27025         
27026         return cfg;
27027     },
27028     
27029     initEvents: function() 
27030     {
27031         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27032     },
27033     
27034     setTitle : function(str)
27035     {
27036         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27037     },
27038     
27039     setText : function(str)
27040     {
27041         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27042     },
27043     
27044     setWeight : function(weight)
27045     {
27046         if(this.weight){
27047             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27048         }
27049         
27050         this.weight = weight;
27051         
27052         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27053     },
27054     
27055     setIcon : function(icon)
27056     {
27057         if(this.faicon){
27058             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27059         }
27060         
27061         this.faicon = icon;
27062         
27063         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27064     },
27065     
27066     hide: function() 
27067     {
27068         this.el.hide();   
27069     },
27070     
27071     show: function() 
27072     {  
27073         this.el.show();   
27074     }
27075     
27076 });
27077
27078  
27079 /*
27080 * Licence: LGPL
27081 */
27082
27083 /**
27084  * @class Roo.bootstrap.UploadCropbox
27085  * @extends Roo.bootstrap.Component
27086  * Bootstrap UploadCropbox class
27087  * @cfg {String} emptyText show when image has been loaded
27088  * @cfg {String} rotateNotify show when image too small to rotate
27089  * @cfg {Number} errorTimeout default 3000
27090  * @cfg {Number} minWidth default 300
27091  * @cfg {Number} minHeight default 300
27092  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27093  * @cfg {Boolean} isDocument (true|false) default false
27094  * @cfg {String} url action url
27095  * @cfg {String} paramName default 'imageUpload'
27096  * @cfg {String} method default POST
27097  * @cfg {Boolean} loadMask (true|false) default true
27098  * @cfg {Boolean} loadingText default 'Loading...'
27099  * 
27100  * @constructor
27101  * Create a new UploadCropbox
27102  * @param {Object} config The config object
27103  */
27104
27105 Roo.bootstrap.UploadCropbox = function(config){
27106     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27107     
27108     this.addEvents({
27109         /**
27110          * @event beforeselectfile
27111          * Fire before select file
27112          * @param {Roo.bootstrap.UploadCropbox} this
27113          */
27114         "beforeselectfile" : true,
27115         /**
27116          * @event initial
27117          * Fire after initEvent
27118          * @param {Roo.bootstrap.UploadCropbox} this
27119          */
27120         "initial" : true,
27121         /**
27122          * @event crop
27123          * Fire after initEvent
27124          * @param {Roo.bootstrap.UploadCropbox} this
27125          * @param {String} data
27126          */
27127         "crop" : true,
27128         /**
27129          * @event prepare
27130          * Fire when preparing the file data
27131          * @param {Roo.bootstrap.UploadCropbox} this
27132          * @param {Object} file
27133          */
27134         "prepare" : true,
27135         /**
27136          * @event exception
27137          * Fire when get exception
27138          * @param {Roo.bootstrap.UploadCropbox} this
27139          * @param {XMLHttpRequest} xhr
27140          */
27141         "exception" : true,
27142         /**
27143          * @event beforeloadcanvas
27144          * Fire before load the canvas
27145          * @param {Roo.bootstrap.UploadCropbox} this
27146          * @param {String} src
27147          */
27148         "beforeloadcanvas" : true,
27149         /**
27150          * @event trash
27151          * Fire when trash image
27152          * @param {Roo.bootstrap.UploadCropbox} this
27153          */
27154         "trash" : true,
27155         /**
27156          * @event download
27157          * Fire when download the image
27158          * @param {Roo.bootstrap.UploadCropbox} this
27159          */
27160         "download" : true,
27161         /**
27162          * @event footerbuttonclick
27163          * Fire when footerbuttonclick
27164          * @param {Roo.bootstrap.UploadCropbox} this
27165          * @param {String} type
27166          */
27167         "footerbuttonclick" : true,
27168         /**
27169          * @event resize
27170          * Fire when resize
27171          * @param {Roo.bootstrap.UploadCropbox} this
27172          */
27173         "resize" : true,
27174         /**
27175          * @event rotate
27176          * Fire when rotate the image
27177          * @param {Roo.bootstrap.UploadCropbox} this
27178          * @param {String} pos
27179          */
27180         "rotate" : true,
27181         /**
27182          * @event inspect
27183          * Fire when inspect the file
27184          * @param {Roo.bootstrap.UploadCropbox} this
27185          * @param {Object} file
27186          */
27187         "inspect" : true,
27188         /**
27189          * @event upload
27190          * Fire when xhr upload the file
27191          * @param {Roo.bootstrap.UploadCropbox} this
27192          * @param {Object} data
27193          */
27194         "upload" : true,
27195         /**
27196          * @event arrange
27197          * Fire when arrange the file data
27198          * @param {Roo.bootstrap.UploadCropbox} this
27199          * @param {Object} formData
27200          */
27201         "arrange" : true
27202     });
27203     
27204     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27205 };
27206
27207 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27208     
27209     emptyText : 'Click to upload image',
27210     rotateNotify : 'Image is too small to rotate',
27211     errorTimeout : 3000,
27212     scale : 0,
27213     baseScale : 1,
27214     rotate : 0,
27215     dragable : false,
27216     pinching : false,
27217     mouseX : 0,
27218     mouseY : 0,
27219     cropData : false,
27220     minWidth : 300,
27221     minHeight : 300,
27222     file : false,
27223     exif : {},
27224     baseRotate : 1,
27225     cropType : 'image/jpeg',
27226     buttons : false,
27227     canvasLoaded : false,
27228     isDocument : false,
27229     method : 'POST',
27230     paramName : 'imageUpload',
27231     loadMask : true,
27232     loadingText : 'Loading...',
27233     maskEl : false,
27234     
27235     getAutoCreate : function()
27236     {
27237         var cfg = {
27238             tag : 'div',
27239             cls : 'roo-upload-cropbox',
27240             cn : [
27241                 {
27242                     tag : 'input',
27243                     cls : 'roo-upload-cropbox-selector',
27244                     type : 'file'
27245                 },
27246                 {
27247                     tag : 'div',
27248                     cls : 'roo-upload-cropbox-body',
27249                     style : 'cursor:pointer',
27250                     cn : [
27251                         {
27252                             tag : 'div',
27253                             cls : 'roo-upload-cropbox-preview'
27254                         },
27255                         {
27256                             tag : 'div',
27257                             cls : 'roo-upload-cropbox-thumb'
27258                         },
27259                         {
27260                             tag : 'div',
27261                             cls : 'roo-upload-cropbox-empty-notify',
27262                             html : this.emptyText
27263                         },
27264                         {
27265                             tag : 'div',
27266                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27267                             html : this.rotateNotify
27268                         }
27269                     ]
27270                 },
27271                 {
27272                     tag : 'div',
27273                     cls : 'roo-upload-cropbox-footer',
27274                     cn : {
27275                         tag : 'div',
27276                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27277                         cn : []
27278                     }
27279                 }
27280             ]
27281         };
27282         
27283         return cfg;
27284     },
27285     
27286     onRender : function(ct, position)
27287     {
27288         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27289         
27290         if (this.buttons.length) {
27291             
27292             Roo.each(this.buttons, function(bb) {
27293                 
27294                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27295                 
27296                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27297                 
27298             }, this);
27299         }
27300         
27301         if(this.loadMask){
27302             this.maskEl = this.el;
27303         }
27304     },
27305     
27306     initEvents : function()
27307     {
27308         this.urlAPI = (window.createObjectURL && window) || 
27309                                 (window.URL && URL.revokeObjectURL && URL) || 
27310                                 (window.webkitURL && webkitURL);
27311                         
27312         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27313         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27314         
27315         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27316         this.selectorEl.hide();
27317         
27318         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27319         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27320         
27321         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27322         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27323         this.thumbEl.hide();
27324         
27325         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27326         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27327         
27328         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27329         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27330         this.errorEl.hide();
27331         
27332         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27333         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27334         this.footerEl.hide();
27335         
27336         this.setThumbBoxSize();
27337         
27338         this.bind();
27339         
27340         this.resize();
27341         
27342         this.fireEvent('initial', this);
27343     },
27344
27345     bind : function()
27346     {
27347         var _this = this;
27348         
27349         window.addEventListener("resize", function() { _this.resize(); } );
27350         
27351         this.bodyEl.on('click', this.beforeSelectFile, this);
27352         
27353         if(Roo.isTouch){
27354             this.bodyEl.on('touchstart', this.onTouchStart, this);
27355             this.bodyEl.on('touchmove', this.onTouchMove, this);
27356             this.bodyEl.on('touchend', this.onTouchEnd, this);
27357         }
27358         
27359         if(!Roo.isTouch){
27360             this.bodyEl.on('mousedown', this.onMouseDown, this);
27361             this.bodyEl.on('mousemove', this.onMouseMove, this);
27362             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27363             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27364             Roo.get(document).on('mouseup', this.onMouseUp, this);
27365         }
27366         
27367         this.selectorEl.on('change', this.onFileSelected, this);
27368     },
27369     
27370     reset : function()
27371     {    
27372         this.scale = 0;
27373         this.baseScale = 1;
27374         this.rotate = 0;
27375         this.baseRotate = 1;
27376         this.dragable = false;
27377         this.pinching = false;
27378         this.mouseX = 0;
27379         this.mouseY = 0;
27380         this.cropData = false;
27381         this.notifyEl.dom.innerHTML = this.emptyText;
27382         
27383         this.selectorEl.dom.value = '';
27384         
27385     },
27386     
27387     resize : function()
27388     {
27389         if(this.fireEvent('resize', this) != false){
27390             this.setThumbBoxPosition();
27391             this.setCanvasPosition();
27392         }
27393     },
27394     
27395     onFooterButtonClick : function(e, el, o, type)
27396     {
27397         switch (type) {
27398             case 'rotate-left' :
27399                 this.onRotateLeft(e);
27400                 break;
27401             case 'rotate-right' :
27402                 this.onRotateRight(e);
27403                 break;
27404             case 'picture' :
27405                 this.beforeSelectFile(e);
27406                 break;
27407             case 'trash' :
27408                 this.trash(e);
27409                 break;
27410             case 'crop' :
27411                 this.crop(e);
27412                 break;
27413             case 'download' :
27414                 this.download(e);
27415                 break;
27416             default :
27417                 break;
27418         }
27419         
27420         this.fireEvent('footerbuttonclick', this, type);
27421     },
27422     
27423     beforeSelectFile : function(e)
27424     {
27425         e.preventDefault();
27426         
27427         if(this.fireEvent('beforeselectfile', this) != false){
27428             this.selectorEl.dom.click();
27429         }
27430     },
27431     
27432     onFileSelected : function(e)
27433     {
27434         e.preventDefault();
27435         
27436         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27437             return;
27438         }
27439         
27440         var file = this.selectorEl.dom.files[0];
27441         
27442         if(this.fireEvent('inspect', this, file) != false){
27443             this.prepare(file);
27444         }
27445         
27446     },
27447     
27448     trash : function(e)
27449     {
27450         this.fireEvent('trash', this);
27451     },
27452     
27453     download : function(e)
27454     {
27455         this.fireEvent('download', this);
27456     },
27457     
27458     loadCanvas : function(src)
27459     {   
27460         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27461             
27462             this.reset();
27463             
27464             this.imageEl = document.createElement('img');
27465             
27466             var _this = this;
27467             
27468             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27469             
27470             this.imageEl.src = src;
27471         }
27472     },
27473     
27474     onLoadCanvas : function()
27475     {   
27476         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27477         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27478         
27479         this.bodyEl.un('click', this.beforeSelectFile, this);
27480         
27481         this.notifyEl.hide();
27482         this.thumbEl.show();
27483         this.footerEl.show();
27484         
27485         this.baseRotateLevel();
27486         
27487         if(this.isDocument){
27488             this.setThumbBoxSize();
27489         }
27490         
27491         this.setThumbBoxPosition();
27492         
27493         this.baseScaleLevel();
27494         
27495         this.draw();
27496         
27497         this.resize();
27498         
27499         this.canvasLoaded = true;
27500         
27501         if(this.loadMask){
27502             this.maskEl.unmask();
27503         }
27504         
27505     },
27506     
27507     setCanvasPosition : function()
27508     {   
27509         if(!this.canvasEl){
27510             return;
27511         }
27512         
27513         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27514         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27515         
27516         this.previewEl.setLeft(pw);
27517         this.previewEl.setTop(ph);
27518         
27519     },
27520     
27521     onMouseDown : function(e)
27522     {   
27523         e.stopEvent();
27524         
27525         this.dragable = true;
27526         this.pinching = false;
27527         
27528         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27529             this.dragable = false;
27530             return;
27531         }
27532         
27533         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27534         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27535         
27536     },
27537     
27538     onMouseMove : function(e)
27539     {   
27540         e.stopEvent();
27541         
27542         if(!this.canvasLoaded){
27543             return;
27544         }
27545         
27546         if (!this.dragable){
27547             return;
27548         }
27549         
27550         var minX = Math.ceil(this.thumbEl.getLeft(true));
27551         var minY = Math.ceil(this.thumbEl.getTop(true));
27552         
27553         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27554         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27555         
27556         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27557         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27558         
27559         x = x - this.mouseX;
27560         y = y - this.mouseY;
27561         
27562         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27563         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27564         
27565         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27566         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27567         
27568         this.previewEl.setLeft(bgX);
27569         this.previewEl.setTop(bgY);
27570         
27571         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27572         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27573     },
27574     
27575     onMouseUp : function(e)
27576     {   
27577         e.stopEvent();
27578         
27579         this.dragable = false;
27580     },
27581     
27582     onMouseWheel : function(e)
27583     {   
27584         e.stopEvent();
27585         
27586         this.startScale = this.scale;
27587         
27588         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27589         
27590         if(!this.zoomable()){
27591             this.scale = this.startScale;
27592             return;
27593         }
27594         
27595         this.draw();
27596         
27597         return;
27598     },
27599     
27600     zoomable : function()
27601     {
27602         var minScale = this.thumbEl.getWidth() / this.minWidth;
27603         
27604         if(this.minWidth < this.minHeight){
27605             minScale = this.thumbEl.getHeight() / this.minHeight;
27606         }
27607         
27608         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27609         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27610         
27611         if(
27612                 this.isDocument &&
27613                 (this.rotate == 0 || this.rotate == 180) && 
27614                 (
27615                     width > this.imageEl.OriginWidth || 
27616                     height > this.imageEl.OriginHeight ||
27617                     (width < this.minWidth && height < this.minHeight)
27618                 )
27619         ){
27620             return false;
27621         }
27622         
27623         if(
27624                 this.isDocument &&
27625                 (this.rotate == 90 || this.rotate == 270) && 
27626                 (
27627                     width > this.imageEl.OriginWidth || 
27628                     height > this.imageEl.OriginHeight ||
27629                     (width < this.minHeight && height < this.minWidth)
27630                 )
27631         ){
27632             return false;
27633         }
27634         
27635         if(
27636                 !this.isDocument &&
27637                 (this.rotate == 0 || this.rotate == 180) && 
27638                 (
27639                     width < this.minWidth || 
27640                     width > this.imageEl.OriginWidth || 
27641                     height < this.minHeight || 
27642                     height > this.imageEl.OriginHeight
27643                 )
27644         ){
27645             return false;
27646         }
27647         
27648         if(
27649                 !this.isDocument &&
27650                 (this.rotate == 90 || this.rotate == 270) && 
27651                 (
27652                     width < this.minHeight || 
27653                     width > this.imageEl.OriginWidth || 
27654                     height < this.minWidth || 
27655                     height > this.imageEl.OriginHeight
27656                 )
27657         ){
27658             return false;
27659         }
27660         
27661         return true;
27662         
27663     },
27664     
27665     onRotateLeft : function(e)
27666     {   
27667         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27668             
27669             var minScale = this.thumbEl.getWidth() / this.minWidth;
27670             
27671             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27672             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27673             
27674             this.startScale = this.scale;
27675             
27676             while (this.getScaleLevel() < minScale){
27677             
27678                 this.scale = this.scale + 1;
27679                 
27680                 if(!this.zoomable()){
27681                     break;
27682                 }
27683                 
27684                 if(
27685                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27686                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27687                 ){
27688                     continue;
27689                 }
27690                 
27691                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27692
27693                 this.draw();
27694                 
27695                 return;
27696             }
27697             
27698             this.scale = this.startScale;
27699             
27700             this.onRotateFail();
27701             
27702             return false;
27703         }
27704         
27705         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27706
27707         if(this.isDocument){
27708             this.setThumbBoxSize();
27709             this.setThumbBoxPosition();
27710             this.setCanvasPosition();
27711         }
27712         
27713         this.draw();
27714         
27715         this.fireEvent('rotate', this, 'left');
27716         
27717     },
27718     
27719     onRotateRight : function(e)
27720     {
27721         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27722             
27723             var minScale = this.thumbEl.getWidth() / this.minWidth;
27724         
27725             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27726             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27727             
27728             this.startScale = this.scale;
27729             
27730             while (this.getScaleLevel() < minScale){
27731             
27732                 this.scale = this.scale + 1;
27733                 
27734                 if(!this.zoomable()){
27735                     break;
27736                 }
27737                 
27738                 if(
27739                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27740                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27741                 ){
27742                     continue;
27743                 }
27744                 
27745                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27746
27747                 this.draw();
27748                 
27749                 return;
27750             }
27751             
27752             this.scale = this.startScale;
27753             
27754             this.onRotateFail();
27755             
27756             return false;
27757         }
27758         
27759         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27760
27761         if(this.isDocument){
27762             this.setThumbBoxSize();
27763             this.setThumbBoxPosition();
27764             this.setCanvasPosition();
27765         }
27766         
27767         this.draw();
27768         
27769         this.fireEvent('rotate', this, 'right');
27770     },
27771     
27772     onRotateFail : function()
27773     {
27774         this.errorEl.show(true);
27775         
27776         var _this = this;
27777         
27778         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27779     },
27780     
27781     draw : function()
27782     {
27783         this.previewEl.dom.innerHTML = '';
27784         
27785         var canvasEl = document.createElement("canvas");
27786         
27787         var contextEl = canvasEl.getContext("2d");
27788         
27789         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27790         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27791         var center = this.imageEl.OriginWidth / 2;
27792         
27793         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27794             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27795             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27796             center = this.imageEl.OriginHeight / 2;
27797         }
27798         
27799         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27800         
27801         contextEl.translate(center, center);
27802         contextEl.rotate(this.rotate * Math.PI / 180);
27803
27804         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27805         
27806         this.canvasEl = document.createElement("canvas");
27807         
27808         this.contextEl = this.canvasEl.getContext("2d");
27809         
27810         switch (this.rotate) {
27811             case 0 :
27812                 
27813                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27814                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27815                 
27816                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27817                 
27818                 break;
27819             case 90 : 
27820                 
27821                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27822                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27823                 
27824                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27825                     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);
27826                     break;
27827                 }
27828                 
27829                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27830                 
27831                 break;
27832             case 180 :
27833                 
27834                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27835                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27836                 
27837                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27838                     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);
27839                     break;
27840                 }
27841                 
27842                 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);
27843                 
27844                 break;
27845             case 270 :
27846                 
27847                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27848                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27849         
27850                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27851                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27852                     break;
27853                 }
27854                 
27855                 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);
27856                 
27857                 break;
27858             default : 
27859                 break;
27860         }
27861         
27862         this.previewEl.appendChild(this.canvasEl);
27863         
27864         this.setCanvasPosition();
27865     },
27866     
27867     crop : function()
27868     {
27869         if(!this.canvasLoaded){
27870             return;
27871         }
27872         
27873         var imageCanvas = document.createElement("canvas");
27874         
27875         var imageContext = imageCanvas.getContext("2d");
27876         
27877         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27878         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27879         
27880         var center = imageCanvas.width / 2;
27881         
27882         imageContext.translate(center, center);
27883         
27884         imageContext.rotate(this.rotate * Math.PI / 180);
27885         
27886         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27887         
27888         var canvas = document.createElement("canvas");
27889         
27890         var context = canvas.getContext("2d");
27891                 
27892         canvas.width = this.minWidth;
27893         canvas.height = this.minHeight;
27894
27895         switch (this.rotate) {
27896             case 0 :
27897                 
27898                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27899                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27900                 
27901                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27902                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27903                 
27904                 var targetWidth = this.minWidth - 2 * x;
27905                 var targetHeight = this.minHeight - 2 * y;
27906                 
27907                 var scale = 1;
27908                 
27909                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27910                     scale = targetWidth / width;
27911                 }
27912                 
27913                 if(x > 0 && y == 0){
27914                     scale = targetHeight / height;
27915                 }
27916                 
27917                 if(x > 0 && y > 0){
27918                     scale = targetWidth / width;
27919                     
27920                     if(width < height){
27921                         scale = targetHeight / height;
27922                     }
27923                 }
27924                 
27925                 context.scale(scale, scale);
27926                 
27927                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27928                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27929
27930                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27931                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27932
27933                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27934                 
27935                 break;
27936             case 90 : 
27937                 
27938                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27939                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27940                 
27941                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27942                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27943                 
27944                 var targetWidth = this.minWidth - 2 * x;
27945                 var targetHeight = this.minHeight - 2 * y;
27946                 
27947                 var scale = 1;
27948                 
27949                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27950                     scale = targetWidth / width;
27951                 }
27952                 
27953                 if(x > 0 && y == 0){
27954                     scale = targetHeight / height;
27955                 }
27956                 
27957                 if(x > 0 && y > 0){
27958                     scale = targetWidth / width;
27959                     
27960                     if(width < height){
27961                         scale = targetHeight / height;
27962                     }
27963                 }
27964                 
27965                 context.scale(scale, scale);
27966                 
27967                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27968                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27969
27970                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27971                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27972                 
27973                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27974                 
27975                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27976                 
27977                 break;
27978             case 180 :
27979                 
27980                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27981                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27982                 
27983                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27984                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27985                 
27986                 var targetWidth = this.minWidth - 2 * x;
27987                 var targetHeight = this.minHeight - 2 * y;
27988                 
27989                 var scale = 1;
27990                 
27991                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27992                     scale = targetWidth / width;
27993                 }
27994                 
27995                 if(x > 0 && y == 0){
27996                     scale = targetHeight / height;
27997                 }
27998                 
27999                 if(x > 0 && y > 0){
28000                     scale = targetWidth / width;
28001                     
28002                     if(width < height){
28003                         scale = targetHeight / height;
28004                     }
28005                 }
28006                 
28007                 context.scale(scale, scale);
28008                 
28009                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28010                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28011
28012                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28013                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28014
28015                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28016                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28017                 
28018                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28019                 
28020                 break;
28021             case 270 :
28022                 
28023                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28024                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28025                 
28026                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28027                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28028                 
28029                 var targetWidth = this.minWidth - 2 * x;
28030                 var targetHeight = this.minHeight - 2 * y;
28031                 
28032                 var scale = 1;
28033                 
28034                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28035                     scale = targetWidth / width;
28036                 }
28037                 
28038                 if(x > 0 && y == 0){
28039                     scale = targetHeight / height;
28040                 }
28041                 
28042                 if(x > 0 && y > 0){
28043                     scale = targetWidth / width;
28044                     
28045                     if(width < height){
28046                         scale = targetHeight / height;
28047                     }
28048                 }
28049                 
28050                 context.scale(scale, scale);
28051                 
28052                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28053                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28054
28055                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28056                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28057                 
28058                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28059                 
28060                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28061                 
28062                 break;
28063             default : 
28064                 break;
28065         }
28066         
28067         this.cropData = canvas.toDataURL(this.cropType);
28068         
28069         if(this.fireEvent('crop', this, this.cropData) !== false){
28070             this.process(this.file, this.cropData);
28071         }
28072         
28073         return;
28074         
28075     },
28076     
28077     setThumbBoxSize : function()
28078     {
28079         var width, height;
28080         
28081         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28082             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28083             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28084             
28085             this.minWidth = width;
28086             this.minHeight = height;
28087             
28088             if(this.rotate == 90 || this.rotate == 270){
28089                 this.minWidth = height;
28090                 this.minHeight = width;
28091             }
28092         }
28093         
28094         height = 300;
28095         width = Math.ceil(this.minWidth * height / this.minHeight);
28096         
28097         if(this.minWidth > this.minHeight){
28098             width = 300;
28099             height = Math.ceil(this.minHeight * width / this.minWidth);
28100         }
28101         
28102         this.thumbEl.setStyle({
28103             width : width + 'px',
28104             height : height + 'px'
28105         });
28106
28107         return;
28108             
28109     },
28110     
28111     setThumbBoxPosition : function()
28112     {
28113         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28114         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28115         
28116         this.thumbEl.setLeft(x);
28117         this.thumbEl.setTop(y);
28118         
28119     },
28120     
28121     baseRotateLevel : function()
28122     {
28123         this.baseRotate = 1;
28124         
28125         if(
28126                 typeof(this.exif) != 'undefined' &&
28127                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28128                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28129         ){
28130             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28131         }
28132         
28133         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28134         
28135     },
28136     
28137     baseScaleLevel : function()
28138     {
28139         var width, height;
28140         
28141         if(this.isDocument){
28142             
28143             if(this.baseRotate == 6 || this.baseRotate == 8){
28144             
28145                 height = this.thumbEl.getHeight();
28146                 this.baseScale = height / this.imageEl.OriginWidth;
28147
28148                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28149                     width = this.thumbEl.getWidth();
28150                     this.baseScale = width / this.imageEl.OriginHeight;
28151                 }
28152
28153                 return;
28154             }
28155
28156             height = this.thumbEl.getHeight();
28157             this.baseScale = height / this.imageEl.OriginHeight;
28158
28159             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28160                 width = this.thumbEl.getWidth();
28161                 this.baseScale = width / this.imageEl.OriginWidth;
28162             }
28163
28164             return;
28165         }
28166         
28167         if(this.baseRotate == 6 || this.baseRotate == 8){
28168             
28169             width = this.thumbEl.getHeight();
28170             this.baseScale = width / this.imageEl.OriginHeight;
28171             
28172             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28173                 height = this.thumbEl.getWidth();
28174                 this.baseScale = height / this.imageEl.OriginHeight;
28175             }
28176             
28177             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28178                 height = this.thumbEl.getWidth();
28179                 this.baseScale = height / this.imageEl.OriginHeight;
28180                 
28181                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28182                     width = this.thumbEl.getHeight();
28183                     this.baseScale = width / this.imageEl.OriginWidth;
28184                 }
28185             }
28186             
28187             return;
28188         }
28189         
28190         width = this.thumbEl.getWidth();
28191         this.baseScale = width / this.imageEl.OriginWidth;
28192         
28193         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28194             height = this.thumbEl.getHeight();
28195             this.baseScale = height / this.imageEl.OriginHeight;
28196         }
28197         
28198         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28199             
28200             height = this.thumbEl.getHeight();
28201             this.baseScale = height / this.imageEl.OriginHeight;
28202             
28203             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28204                 width = this.thumbEl.getWidth();
28205                 this.baseScale = width / this.imageEl.OriginWidth;
28206             }
28207             
28208         }
28209         
28210         return;
28211     },
28212     
28213     getScaleLevel : function()
28214     {
28215         return this.baseScale * Math.pow(1.1, this.scale);
28216     },
28217     
28218     onTouchStart : function(e)
28219     {
28220         if(!this.canvasLoaded){
28221             this.beforeSelectFile(e);
28222             return;
28223         }
28224         
28225         var touches = e.browserEvent.touches;
28226         
28227         if(!touches){
28228             return;
28229         }
28230         
28231         if(touches.length == 1){
28232             this.onMouseDown(e);
28233             return;
28234         }
28235         
28236         if(touches.length != 2){
28237             return;
28238         }
28239         
28240         var coords = [];
28241         
28242         for(var i = 0, finger; finger = touches[i]; i++){
28243             coords.push(finger.pageX, finger.pageY);
28244         }
28245         
28246         var x = Math.pow(coords[0] - coords[2], 2);
28247         var y = Math.pow(coords[1] - coords[3], 2);
28248         
28249         this.startDistance = Math.sqrt(x + y);
28250         
28251         this.startScale = this.scale;
28252         
28253         this.pinching = true;
28254         this.dragable = false;
28255         
28256     },
28257     
28258     onTouchMove : function(e)
28259     {
28260         if(!this.pinching && !this.dragable){
28261             return;
28262         }
28263         
28264         var touches = e.browserEvent.touches;
28265         
28266         if(!touches){
28267             return;
28268         }
28269         
28270         if(this.dragable){
28271             this.onMouseMove(e);
28272             return;
28273         }
28274         
28275         var coords = [];
28276         
28277         for(var i = 0, finger; finger = touches[i]; i++){
28278             coords.push(finger.pageX, finger.pageY);
28279         }
28280         
28281         var x = Math.pow(coords[0] - coords[2], 2);
28282         var y = Math.pow(coords[1] - coords[3], 2);
28283         
28284         this.endDistance = Math.sqrt(x + y);
28285         
28286         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28287         
28288         if(!this.zoomable()){
28289             this.scale = this.startScale;
28290             return;
28291         }
28292         
28293         this.draw();
28294         
28295     },
28296     
28297     onTouchEnd : function(e)
28298     {
28299         this.pinching = false;
28300         this.dragable = false;
28301         
28302     },
28303     
28304     process : function(file, crop)
28305     {
28306         if(this.loadMask){
28307             this.maskEl.mask(this.loadingText);
28308         }
28309         
28310         this.xhr = new XMLHttpRequest();
28311         
28312         file.xhr = this.xhr;
28313
28314         this.xhr.open(this.method, this.url, true);
28315         
28316         var headers = {
28317             "Accept": "application/json",
28318             "Cache-Control": "no-cache",
28319             "X-Requested-With": "XMLHttpRequest"
28320         };
28321         
28322         for (var headerName in headers) {
28323             var headerValue = headers[headerName];
28324             if (headerValue) {
28325                 this.xhr.setRequestHeader(headerName, headerValue);
28326             }
28327         }
28328         
28329         var _this = this;
28330         
28331         this.xhr.onload = function()
28332         {
28333             _this.xhrOnLoad(_this.xhr);
28334         }
28335         
28336         this.xhr.onerror = function()
28337         {
28338             _this.xhrOnError(_this.xhr);
28339         }
28340         
28341         var formData = new FormData();
28342
28343         formData.append('returnHTML', 'NO');
28344         
28345         if(crop){
28346             formData.append('crop', crop);
28347         }
28348         
28349         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28350             formData.append(this.paramName, file, file.name);
28351         }
28352         
28353         if(typeof(file.filename) != 'undefined'){
28354             formData.append('filename', file.filename);
28355         }
28356         
28357         if(typeof(file.mimetype) != 'undefined'){
28358             formData.append('mimetype', file.mimetype);
28359         }
28360         
28361         if(this.fireEvent('arrange', this, formData) != false){
28362             this.xhr.send(formData);
28363         };
28364     },
28365     
28366     xhrOnLoad : function(xhr)
28367     {
28368         if(this.loadMask){
28369             this.maskEl.unmask();
28370         }
28371         
28372         if (xhr.readyState !== 4) {
28373             this.fireEvent('exception', this, xhr);
28374             return;
28375         }
28376
28377         var response = Roo.decode(xhr.responseText);
28378         
28379         if(!response.success){
28380             this.fireEvent('exception', this, xhr);
28381             return;
28382         }
28383         
28384         var response = Roo.decode(xhr.responseText);
28385         
28386         this.fireEvent('upload', this, response);
28387         
28388     },
28389     
28390     xhrOnError : function()
28391     {
28392         if(this.loadMask){
28393             this.maskEl.unmask();
28394         }
28395         
28396         Roo.log('xhr on error');
28397         
28398         var response = Roo.decode(xhr.responseText);
28399           
28400         Roo.log(response);
28401         
28402     },
28403     
28404     prepare : function(file)
28405     {   
28406         if(this.loadMask){
28407             this.maskEl.mask(this.loadingText);
28408         }
28409         
28410         this.file = false;
28411         this.exif = {};
28412         
28413         if(typeof(file) === 'string'){
28414             this.loadCanvas(file);
28415             return;
28416         }
28417         
28418         if(!file || !this.urlAPI){
28419             return;
28420         }
28421         
28422         this.file = file;
28423         this.cropType = file.type;
28424         
28425         var _this = this;
28426         
28427         if(this.fireEvent('prepare', this, this.file) != false){
28428             
28429             var reader = new FileReader();
28430             
28431             reader.onload = function (e) {
28432                 if (e.target.error) {
28433                     Roo.log(e.target.error);
28434                     return;
28435                 }
28436                 
28437                 var buffer = e.target.result,
28438                     dataView = new DataView(buffer),
28439                     offset = 2,
28440                     maxOffset = dataView.byteLength - 4,
28441                     markerBytes,
28442                     markerLength;
28443                 
28444                 if (dataView.getUint16(0) === 0xffd8) {
28445                     while (offset < maxOffset) {
28446                         markerBytes = dataView.getUint16(offset);
28447                         
28448                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28449                             markerLength = dataView.getUint16(offset + 2) + 2;
28450                             if (offset + markerLength > dataView.byteLength) {
28451                                 Roo.log('Invalid meta data: Invalid segment size.');
28452                                 break;
28453                             }
28454                             
28455                             if(markerBytes == 0xffe1){
28456                                 _this.parseExifData(
28457                                     dataView,
28458                                     offset,
28459                                     markerLength
28460                                 );
28461                             }
28462                             
28463                             offset += markerLength;
28464                             
28465                             continue;
28466                         }
28467                         
28468                         break;
28469                     }
28470                     
28471                 }
28472                 
28473                 var url = _this.urlAPI.createObjectURL(_this.file);
28474                 
28475                 _this.loadCanvas(url);
28476                 
28477                 return;
28478             }
28479             
28480             reader.readAsArrayBuffer(this.file);
28481             
28482         }
28483         
28484     },
28485     
28486     parseExifData : function(dataView, offset, length)
28487     {
28488         var tiffOffset = offset + 10,
28489             littleEndian,
28490             dirOffset;
28491     
28492         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28493             // No Exif data, might be XMP data instead
28494             return;
28495         }
28496         
28497         // Check for the ASCII code for "Exif" (0x45786966):
28498         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28499             // No Exif data, might be XMP data instead
28500             return;
28501         }
28502         if (tiffOffset + 8 > dataView.byteLength) {
28503             Roo.log('Invalid Exif data: Invalid segment size.');
28504             return;
28505         }
28506         // Check for the two null bytes:
28507         if (dataView.getUint16(offset + 8) !== 0x0000) {
28508             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28509             return;
28510         }
28511         // Check the byte alignment:
28512         switch (dataView.getUint16(tiffOffset)) {
28513         case 0x4949:
28514             littleEndian = true;
28515             break;
28516         case 0x4D4D:
28517             littleEndian = false;
28518             break;
28519         default:
28520             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28521             return;
28522         }
28523         // Check for the TIFF tag marker (0x002A):
28524         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28525             Roo.log('Invalid Exif data: Missing TIFF marker.');
28526             return;
28527         }
28528         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28529         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28530         
28531         this.parseExifTags(
28532             dataView,
28533             tiffOffset,
28534             tiffOffset + dirOffset,
28535             littleEndian
28536         );
28537     },
28538     
28539     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28540     {
28541         var tagsNumber,
28542             dirEndOffset,
28543             i;
28544         if (dirOffset + 6 > dataView.byteLength) {
28545             Roo.log('Invalid Exif data: Invalid directory offset.');
28546             return;
28547         }
28548         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28549         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28550         if (dirEndOffset + 4 > dataView.byteLength) {
28551             Roo.log('Invalid Exif data: Invalid directory size.');
28552             return;
28553         }
28554         for (i = 0; i < tagsNumber; i += 1) {
28555             this.parseExifTag(
28556                 dataView,
28557                 tiffOffset,
28558                 dirOffset + 2 + 12 * i, // tag offset
28559                 littleEndian
28560             );
28561         }
28562         // Return the offset to the next directory:
28563         return dataView.getUint32(dirEndOffset, littleEndian);
28564     },
28565     
28566     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28567     {
28568         var tag = dataView.getUint16(offset, littleEndian);
28569         
28570         this.exif[tag] = this.getExifValue(
28571             dataView,
28572             tiffOffset,
28573             offset,
28574             dataView.getUint16(offset + 2, littleEndian), // tag type
28575             dataView.getUint32(offset + 4, littleEndian), // tag length
28576             littleEndian
28577         );
28578     },
28579     
28580     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28581     {
28582         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28583             tagSize,
28584             dataOffset,
28585             values,
28586             i,
28587             str,
28588             c;
28589     
28590         if (!tagType) {
28591             Roo.log('Invalid Exif data: Invalid tag type.');
28592             return;
28593         }
28594         
28595         tagSize = tagType.size * length;
28596         // Determine if the value is contained in the dataOffset bytes,
28597         // or if the value at the dataOffset is a pointer to the actual data:
28598         dataOffset = tagSize > 4 ?
28599                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28600         if (dataOffset + tagSize > dataView.byteLength) {
28601             Roo.log('Invalid Exif data: Invalid data offset.');
28602             return;
28603         }
28604         if (length === 1) {
28605             return tagType.getValue(dataView, dataOffset, littleEndian);
28606         }
28607         values = [];
28608         for (i = 0; i < length; i += 1) {
28609             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28610         }
28611         
28612         if (tagType.ascii) {
28613             str = '';
28614             // Concatenate the chars:
28615             for (i = 0; i < values.length; i += 1) {
28616                 c = values[i];
28617                 // Ignore the terminating NULL byte(s):
28618                 if (c === '\u0000') {
28619                     break;
28620                 }
28621                 str += c;
28622             }
28623             return str;
28624         }
28625         return values;
28626     }
28627     
28628 });
28629
28630 Roo.apply(Roo.bootstrap.UploadCropbox, {
28631     tags : {
28632         'Orientation': 0x0112
28633     },
28634     
28635     Orientation: {
28636             1: 0, //'top-left',
28637 //            2: 'top-right',
28638             3: 180, //'bottom-right',
28639 //            4: 'bottom-left',
28640 //            5: 'left-top',
28641             6: 90, //'right-top',
28642 //            7: 'right-bottom',
28643             8: 270 //'left-bottom'
28644     },
28645     
28646     exifTagTypes : {
28647         // byte, 8-bit unsigned int:
28648         1: {
28649             getValue: function (dataView, dataOffset) {
28650                 return dataView.getUint8(dataOffset);
28651             },
28652             size: 1
28653         },
28654         // ascii, 8-bit byte:
28655         2: {
28656             getValue: function (dataView, dataOffset) {
28657                 return String.fromCharCode(dataView.getUint8(dataOffset));
28658             },
28659             size: 1,
28660             ascii: true
28661         },
28662         // short, 16 bit int:
28663         3: {
28664             getValue: function (dataView, dataOffset, littleEndian) {
28665                 return dataView.getUint16(dataOffset, littleEndian);
28666             },
28667             size: 2
28668         },
28669         // long, 32 bit int:
28670         4: {
28671             getValue: function (dataView, dataOffset, littleEndian) {
28672                 return dataView.getUint32(dataOffset, littleEndian);
28673             },
28674             size: 4
28675         },
28676         // rational = two long values, first is numerator, second is denominator:
28677         5: {
28678             getValue: function (dataView, dataOffset, littleEndian) {
28679                 return dataView.getUint32(dataOffset, littleEndian) /
28680                     dataView.getUint32(dataOffset + 4, littleEndian);
28681             },
28682             size: 8
28683         },
28684         // slong, 32 bit signed int:
28685         9: {
28686             getValue: function (dataView, dataOffset, littleEndian) {
28687                 return dataView.getInt32(dataOffset, littleEndian);
28688             },
28689             size: 4
28690         },
28691         // srational, two slongs, first is numerator, second is denominator:
28692         10: {
28693             getValue: function (dataView, dataOffset, littleEndian) {
28694                 return dataView.getInt32(dataOffset, littleEndian) /
28695                     dataView.getInt32(dataOffset + 4, littleEndian);
28696             },
28697             size: 8
28698         }
28699     },
28700     
28701     footer : {
28702         STANDARD : [
28703             {
28704                 tag : 'div',
28705                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28706                 action : 'rotate-left',
28707                 cn : [
28708                     {
28709                         tag : 'button',
28710                         cls : 'btn btn-default',
28711                         html : '<i class="fa fa-undo"></i>'
28712                     }
28713                 ]
28714             },
28715             {
28716                 tag : 'div',
28717                 cls : 'btn-group roo-upload-cropbox-picture',
28718                 action : 'picture',
28719                 cn : [
28720                     {
28721                         tag : 'button',
28722                         cls : 'btn btn-default',
28723                         html : '<i class="fa fa-picture-o"></i>'
28724                     }
28725                 ]
28726             },
28727             {
28728                 tag : 'div',
28729                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28730                 action : 'rotate-right',
28731                 cn : [
28732                     {
28733                         tag : 'button',
28734                         cls : 'btn btn-default',
28735                         html : '<i class="fa fa-repeat"></i>'
28736                     }
28737                 ]
28738             }
28739         ],
28740         DOCUMENT : [
28741             {
28742                 tag : 'div',
28743                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28744                 action : 'rotate-left',
28745                 cn : [
28746                     {
28747                         tag : 'button',
28748                         cls : 'btn btn-default',
28749                         html : '<i class="fa fa-undo"></i>'
28750                     }
28751                 ]
28752             },
28753             {
28754                 tag : 'div',
28755                 cls : 'btn-group roo-upload-cropbox-download',
28756                 action : 'download',
28757                 cn : [
28758                     {
28759                         tag : 'button',
28760                         cls : 'btn btn-default',
28761                         html : '<i class="fa fa-download"></i>'
28762                     }
28763                 ]
28764             },
28765             {
28766                 tag : 'div',
28767                 cls : 'btn-group roo-upload-cropbox-crop',
28768                 action : 'crop',
28769                 cn : [
28770                     {
28771                         tag : 'button',
28772                         cls : 'btn btn-default',
28773                         html : '<i class="fa fa-crop"></i>'
28774                     }
28775                 ]
28776             },
28777             {
28778                 tag : 'div',
28779                 cls : 'btn-group roo-upload-cropbox-trash',
28780                 action : 'trash',
28781                 cn : [
28782                     {
28783                         tag : 'button',
28784                         cls : 'btn btn-default',
28785                         html : '<i class="fa fa-trash"></i>'
28786                     }
28787                 ]
28788             },
28789             {
28790                 tag : 'div',
28791                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28792                 action : 'rotate-right',
28793                 cn : [
28794                     {
28795                         tag : 'button',
28796                         cls : 'btn btn-default',
28797                         html : '<i class="fa fa-repeat"></i>'
28798                     }
28799                 ]
28800             }
28801         ],
28802         ROTATOR : [
28803             {
28804                 tag : 'div',
28805                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28806                 action : 'rotate-left',
28807                 cn : [
28808                     {
28809                         tag : 'button',
28810                         cls : 'btn btn-default',
28811                         html : '<i class="fa fa-undo"></i>'
28812                     }
28813                 ]
28814             },
28815             {
28816                 tag : 'div',
28817                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28818                 action : 'rotate-right',
28819                 cn : [
28820                     {
28821                         tag : 'button',
28822                         cls : 'btn btn-default',
28823                         html : '<i class="fa fa-repeat"></i>'
28824                     }
28825                 ]
28826             }
28827         ]
28828     }
28829 });
28830
28831 /*
28832 * Licence: LGPL
28833 */
28834
28835 /**
28836  * @class Roo.bootstrap.DocumentManager
28837  * @extends Roo.bootstrap.Component
28838  * Bootstrap DocumentManager class
28839  * @cfg {String} paramName default 'imageUpload'
28840  * @cfg {String} toolTipName default 'filename'
28841  * @cfg {String} method default POST
28842  * @cfg {String} url action url
28843  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28844  * @cfg {Boolean} multiple multiple upload default true
28845  * @cfg {Number} thumbSize default 300
28846  * @cfg {String} fieldLabel
28847  * @cfg {Number} labelWidth default 4
28848  * @cfg {String} labelAlign (left|top) default left
28849  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28850 * @cfg {Number} labellg set the width of label (1-12)
28851  * @cfg {Number} labelmd set the width of label (1-12)
28852  * @cfg {Number} labelsm set the width of label (1-12)
28853  * @cfg {Number} labelxs set the width of label (1-12)
28854  * 
28855  * @constructor
28856  * Create a new DocumentManager
28857  * @param {Object} config The config object
28858  */
28859
28860 Roo.bootstrap.DocumentManager = function(config){
28861     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28862     
28863     this.files = [];
28864     this.delegates = [];
28865     
28866     this.addEvents({
28867         /**
28868          * @event initial
28869          * Fire when initial the DocumentManager
28870          * @param {Roo.bootstrap.DocumentManager} this
28871          */
28872         "initial" : true,
28873         /**
28874          * @event inspect
28875          * inspect selected file
28876          * @param {Roo.bootstrap.DocumentManager} this
28877          * @param {File} file
28878          */
28879         "inspect" : true,
28880         /**
28881          * @event exception
28882          * Fire when xhr load exception
28883          * @param {Roo.bootstrap.DocumentManager} this
28884          * @param {XMLHttpRequest} xhr
28885          */
28886         "exception" : true,
28887         /**
28888          * @event afterupload
28889          * Fire when xhr load exception
28890          * @param {Roo.bootstrap.DocumentManager} this
28891          * @param {XMLHttpRequest} xhr
28892          */
28893         "afterupload" : true,
28894         /**
28895          * @event prepare
28896          * prepare the form data
28897          * @param {Roo.bootstrap.DocumentManager} this
28898          * @param {Object} formData
28899          */
28900         "prepare" : true,
28901         /**
28902          * @event remove
28903          * Fire when remove the file
28904          * @param {Roo.bootstrap.DocumentManager} this
28905          * @param {Object} file
28906          */
28907         "remove" : true,
28908         /**
28909          * @event refresh
28910          * Fire after refresh the file
28911          * @param {Roo.bootstrap.DocumentManager} this
28912          */
28913         "refresh" : true,
28914         /**
28915          * @event click
28916          * Fire after click the image
28917          * @param {Roo.bootstrap.DocumentManager} this
28918          * @param {Object} file
28919          */
28920         "click" : true,
28921         /**
28922          * @event edit
28923          * Fire when upload a image and editable set to true
28924          * @param {Roo.bootstrap.DocumentManager} this
28925          * @param {Object} file
28926          */
28927         "edit" : true,
28928         /**
28929          * @event beforeselectfile
28930          * Fire before select file
28931          * @param {Roo.bootstrap.DocumentManager} this
28932          */
28933         "beforeselectfile" : true,
28934         /**
28935          * @event process
28936          * Fire before process file
28937          * @param {Roo.bootstrap.DocumentManager} this
28938          * @param {Object} file
28939          */
28940         "process" : true,
28941         /**
28942          * @event previewrendered
28943          * Fire when preview rendered
28944          * @param {Roo.bootstrap.DocumentManager} this
28945          * @param {Object} file
28946          */
28947         "previewrendered" : true,
28948         /**
28949          */
28950         "previewResize" : true
28951         
28952     });
28953 };
28954
28955 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28956     
28957     boxes : 0,
28958     inputName : '',
28959     thumbSize : 300,
28960     multiple : true,
28961     files : false,
28962     method : 'POST',
28963     url : '',
28964     paramName : 'imageUpload',
28965     toolTipName : 'filename',
28966     fieldLabel : '',
28967     labelWidth : 4,
28968     labelAlign : 'left',
28969     editable : true,
28970     delegates : false,
28971     xhr : false, 
28972     
28973     labellg : 0,
28974     labelmd : 0,
28975     labelsm : 0,
28976     labelxs : 0,
28977     
28978     getAutoCreate : function()
28979     {   
28980         var managerWidget = {
28981             tag : 'div',
28982             cls : 'roo-document-manager',
28983             cn : [
28984                 {
28985                     tag : 'input',
28986                     cls : 'roo-document-manager-selector',
28987                     type : 'file'
28988                 },
28989                 {
28990                     tag : 'div',
28991                     cls : 'roo-document-manager-uploader',
28992                     cn : [
28993                         {
28994                             tag : 'div',
28995                             cls : 'roo-document-manager-upload-btn',
28996                             html : '<i class="fa fa-plus"></i>'
28997                         }
28998                     ]
28999                     
29000                 }
29001             ]
29002         };
29003         
29004         var content = [
29005             {
29006                 tag : 'div',
29007                 cls : 'column col-md-12',
29008                 cn : managerWidget
29009             }
29010         ];
29011         
29012         if(this.fieldLabel.length){
29013             
29014             content = [
29015                 {
29016                     tag : 'div',
29017                     cls : 'column col-md-12',
29018                     html : this.fieldLabel
29019                 },
29020                 {
29021                     tag : 'div',
29022                     cls : 'column col-md-12',
29023                     cn : managerWidget
29024                 }
29025             ];
29026
29027             if(this.labelAlign == 'left'){
29028                 content = [
29029                     {
29030                         tag : 'div',
29031                         cls : 'column',
29032                         html : this.fieldLabel
29033                     },
29034                     {
29035                         tag : 'div',
29036                         cls : 'column',
29037                         cn : managerWidget
29038                     }
29039                 ];
29040                 
29041                 if(this.labelWidth > 12){
29042                     content[0].style = "width: " + this.labelWidth + 'px';
29043                 }
29044
29045                 if(this.labelWidth < 13 && this.labelmd == 0){
29046                     this.labelmd = this.labelWidth;
29047                 }
29048
29049                 if(this.labellg > 0){
29050                     content[0].cls += ' col-lg-' + this.labellg;
29051                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29052                 }
29053
29054                 if(this.labelmd > 0){
29055                     content[0].cls += ' col-md-' + this.labelmd;
29056                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29057                 }
29058
29059                 if(this.labelsm > 0){
29060                     content[0].cls += ' col-sm-' + this.labelsm;
29061                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29062                 }
29063
29064                 if(this.labelxs > 0){
29065                     content[0].cls += ' col-xs-' + this.labelxs;
29066                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29067                 }
29068                 
29069             }
29070         }
29071         
29072         var cfg = {
29073             tag : 'div',
29074             cls : 'row clearfix',
29075             cn : content
29076         };
29077         
29078         return cfg;
29079         
29080     },
29081     
29082     initEvents : function()
29083     {
29084         this.managerEl = this.el.select('.roo-document-manager', true).first();
29085         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29086         
29087         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29088         this.selectorEl.hide();
29089         
29090         if(this.multiple){
29091             this.selectorEl.attr('multiple', 'multiple');
29092         }
29093         
29094         this.selectorEl.on('change', this.onFileSelected, this);
29095         
29096         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29097         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29098         
29099         this.uploader.on('click', this.onUploaderClick, this);
29100         
29101         this.renderProgressDialog();
29102         
29103         var _this = this;
29104         
29105         window.addEventListener("resize", function() { _this.refresh(); } );
29106         
29107         this.fireEvent('initial', this);
29108     },
29109     
29110     renderProgressDialog : function()
29111     {
29112         var _this = this;
29113         
29114         this.progressDialog = new Roo.bootstrap.Modal({
29115             cls : 'roo-document-manager-progress-dialog',
29116             allow_close : false,
29117             title : '',
29118             buttons : [
29119                 {
29120                     name  :'cancel',
29121                     weight : 'danger',
29122                     html : 'Cancel'
29123                 }
29124             ], 
29125             listeners : { 
29126                 btnclick : function() {
29127                     _this.uploadCancel();
29128                     this.hide();
29129                 }
29130             }
29131         });
29132          
29133         this.progressDialog.render(Roo.get(document.body));
29134          
29135         this.progress = new Roo.bootstrap.Progress({
29136             cls : 'roo-document-manager-progress',
29137             active : true,
29138             striped : true
29139         });
29140         
29141         this.progress.render(this.progressDialog.getChildContainer());
29142         
29143         this.progressBar = new Roo.bootstrap.ProgressBar({
29144             cls : 'roo-document-manager-progress-bar',
29145             aria_valuenow : 0,
29146             aria_valuemin : 0,
29147             aria_valuemax : 12,
29148             panel : 'success'
29149         });
29150         
29151         this.progressBar.render(this.progress.getChildContainer());
29152     },
29153     
29154     onUploaderClick : function(e)
29155     {
29156         e.preventDefault();
29157      
29158         if(this.fireEvent('beforeselectfile', this) != false){
29159             this.selectorEl.dom.click();
29160         }
29161         
29162     },
29163     
29164     onFileSelected : function(e)
29165     {
29166         e.preventDefault();
29167         
29168         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29169             return;
29170         }
29171         
29172         Roo.each(this.selectorEl.dom.files, function(file){
29173             if(this.fireEvent('inspect', this, file) != false){
29174                 this.files.push(file);
29175             }
29176         }, this);
29177         
29178         this.queue();
29179         
29180     },
29181     
29182     queue : function()
29183     {
29184         this.selectorEl.dom.value = '';
29185         
29186         if(!this.files || !this.files.length){
29187             return;
29188         }
29189         
29190         if(this.boxes > 0 && this.files.length > this.boxes){
29191             this.files = this.files.slice(0, this.boxes);
29192         }
29193         
29194         this.uploader.show();
29195         
29196         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29197             this.uploader.hide();
29198         }
29199         
29200         var _this = this;
29201         
29202         var files = [];
29203         
29204         var docs = [];
29205         
29206         Roo.each(this.files, function(file){
29207             
29208             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29209                 var f = this.renderPreview(file);
29210                 files.push(f);
29211                 return;
29212             }
29213             
29214             if(file.type.indexOf('image') != -1){
29215                 this.delegates.push(
29216                     (function(){
29217                         _this.process(file);
29218                     }).createDelegate(this)
29219                 );
29220         
29221                 return;
29222             }
29223             
29224             docs.push(
29225                 (function(){
29226                     _this.process(file);
29227                 }).createDelegate(this)
29228             );
29229             
29230         }, this);
29231         
29232         this.files = files;
29233         
29234         this.delegates = this.delegates.concat(docs);
29235         
29236         if(!this.delegates.length){
29237             this.refresh();
29238             return;
29239         }
29240         
29241         this.progressBar.aria_valuemax = this.delegates.length;
29242         
29243         this.arrange();
29244         
29245         return;
29246     },
29247     
29248     arrange : function()
29249     {
29250         if(!this.delegates.length){
29251             this.progressDialog.hide();
29252             this.refresh();
29253             return;
29254         }
29255         
29256         var delegate = this.delegates.shift();
29257         
29258         this.progressDialog.show();
29259         
29260         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29261         
29262         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29263         
29264         delegate();
29265     },
29266     
29267     refresh : function()
29268     {
29269         this.uploader.show();
29270         
29271         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29272             this.uploader.hide();
29273         }
29274         
29275         Roo.isTouch ? this.closable(false) : this.closable(true);
29276         
29277         this.fireEvent('refresh', this);
29278     },
29279     
29280     onRemove : function(e, el, o)
29281     {
29282         e.preventDefault();
29283         
29284         this.fireEvent('remove', this, o);
29285         
29286     },
29287     
29288     remove : function(o)
29289     {
29290         var files = [];
29291         
29292         Roo.each(this.files, function(file){
29293             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29294                 files.push(file);
29295                 return;
29296             }
29297
29298             o.target.remove();
29299
29300         }, this);
29301         
29302         this.files = files;
29303         
29304         this.refresh();
29305     },
29306     
29307     clear : function()
29308     {
29309         Roo.each(this.files, function(file){
29310             if(!file.target){
29311                 return;
29312             }
29313             
29314             file.target.remove();
29315
29316         }, this);
29317         
29318         this.files = [];
29319         
29320         this.refresh();
29321     },
29322     
29323     onClick : function(e, el, o)
29324     {
29325         e.preventDefault();
29326         
29327         this.fireEvent('click', this, o);
29328         
29329     },
29330     
29331     closable : function(closable)
29332     {
29333         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29334             
29335             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29336             
29337             if(closable){
29338                 el.show();
29339                 return;
29340             }
29341             
29342             el.hide();
29343             
29344         }, this);
29345     },
29346     
29347     xhrOnLoad : function(xhr)
29348     {
29349         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29350             el.remove();
29351         }, this);
29352         
29353         if (xhr.readyState !== 4) {
29354             this.arrange();
29355             this.fireEvent('exception', this, xhr);
29356             return;
29357         }
29358
29359         var response = Roo.decode(xhr.responseText);
29360         
29361         if(!response.success){
29362             this.arrange();
29363             this.fireEvent('exception', this, xhr);
29364             return;
29365         }
29366         
29367         var file = this.renderPreview(response.data);
29368         
29369         this.files.push(file);
29370         
29371         this.arrange();
29372         
29373         this.fireEvent('afterupload', this, xhr);
29374         
29375     },
29376     
29377     xhrOnError : function(xhr)
29378     {
29379         Roo.log('xhr on error');
29380         
29381         var response = Roo.decode(xhr.responseText);
29382           
29383         Roo.log(response);
29384         
29385         this.arrange();
29386     },
29387     
29388     process : function(file)
29389     {
29390         if(this.fireEvent('process', this, file) !== false){
29391             if(this.editable && file.type.indexOf('image') != -1){
29392                 this.fireEvent('edit', this, file);
29393                 return;
29394             }
29395
29396             this.uploadStart(file, false);
29397
29398             return;
29399         }
29400         
29401     },
29402     
29403     uploadStart : function(file, crop)
29404     {
29405         this.xhr = new XMLHttpRequest();
29406         
29407         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29408             this.arrange();
29409             return;
29410         }
29411         
29412         file.xhr = this.xhr;
29413             
29414         this.managerEl.createChild({
29415             tag : 'div',
29416             cls : 'roo-document-manager-loading',
29417             cn : [
29418                 {
29419                     tag : 'div',
29420                     tooltip : file.name,
29421                     cls : 'roo-document-manager-thumb',
29422                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29423                 }
29424             ]
29425
29426         });
29427
29428         this.xhr.open(this.method, this.url, true);
29429         
29430         var headers = {
29431             "Accept": "application/json",
29432             "Cache-Control": "no-cache",
29433             "X-Requested-With": "XMLHttpRequest"
29434         };
29435         
29436         for (var headerName in headers) {
29437             var headerValue = headers[headerName];
29438             if (headerValue) {
29439                 this.xhr.setRequestHeader(headerName, headerValue);
29440             }
29441         }
29442         
29443         var _this = this;
29444         
29445         this.xhr.onload = function()
29446         {
29447             _this.xhrOnLoad(_this.xhr);
29448         }
29449         
29450         this.xhr.onerror = function()
29451         {
29452             _this.xhrOnError(_this.xhr);
29453         }
29454         
29455         var formData = new FormData();
29456
29457         formData.append('returnHTML', 'NO');
29458         
29459         if(crop){
29460             formData.append('crop', crop);
29461         }
29462         
29463         formData.append(this.paramName, file, file.name);
29464         
29465         var options = {
29466             file : file, 
29467             manually : false
29468         };
29469         
29470         if(this.fireEvent('prepare', this, formData, options) != false){
29471             
29472             if(options.manually){
29473                 return;
29474             }
29475             
29476             this.xhr.send(formData);
29477             return;
29478         };
29479         
29480         this.uploadCancel();
29481     },
29482     
29483     uploadCancel : function()
29484     {
29485         if (this.xhr) {
29486             this.xhr.abort();
29487         }
29488         
29489         this.delegates = [];
29490         
29491         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29492             el.remove();
29493         }, this);
29494         
29495         this.arrange();
29496     },
29497     
29498     renderPreview : function(file)
29499     {
29500         if(typeof(file.target) != 'undefined' && file.target){
29501             return file;
29502         }
29503         
29504         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29505         
29506         var previewEl = this.managerEl.createChild({
29507             tag : 'div',
29508             cls : 'roo-document-manager-preview',
29509             cn : [
29510                 {
29511                     tag : 'div',
29512                     tooltip : file[this.toolTipName],
29513                     cls : 'roo-document-manager-thumb',
29514                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29515                 },
29516                 {
29517                     tag : 'button',
29518                     cls : 'close',
29519                     html : '<i class="fa fa-times-circle"></i>'
29520                 }
29521             ]
29522         });
29523
29524         var close = previewEl.select('button.close', true).first();
29525
29526         close.on('click', this.onRemove, this, file);
29527
29528         file.target = previewEl;
29529
29530         var image = previewEl.select('img', true).first();
29531         
29532         var _this = this;
29533         
29534         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29535         
29536         image.on('click', this.onClick, this, file);
29537         
29538         this.fireEvent('previewrendered', this, file);
29539         
29540         return file;
29541         
29542     },
29543     
29544     onPreviewLoad : function(file, image)
29545     {
29546         if(typeof(file.target) == 'undefined' || !file.target){
29547             return;
29548         }
29549         
29550         var width = image.dom.naturalWidth || image.dom.width;
29551         var height = image.dom.naturalHeight || image.dom.height;
29552         
29553         if(!this.previewResize) {
29554             return;
29555         }
29556         
29557         if(width > height){
29558             file.target.addClass('wide');
29559             return;
29560         }
29561         
29562         file.target.addClass('tall');
29563         return;
29564         
29565     },
29566     
29567     uploadFromSource : function(file, crop)
29568     {
29569         this.xhr = new XMLHttpRequest();
29570         
29571         this.managerEl.createChild({
29572             tag : 'div',
29573             cls : 'roo-document-manager-loading',
29574             cn : [
29575                 {
29576                     tag : 'div',
29577                     tooltip : file.name,
29578                     cls : 'roo-document-manager-thumb',
29579                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29580                 }
29581             ]
29582
29583         });
29584
29585         this.xhr.open(this.method, this.url, true);
29586         
29587         var headers = {
29588             "Accept": "application/json",
29589             "Cache-Control": "no-cache",
29590             "X-Requested-With": "XMLHttpRequest"
29591         };
29592         
29593         for (var headerName in headers) {
29594             var headerValue = headers[headerName];
29595             if (headerValue) {
29596                 this.xhr.setRequestHeader(headerName, headerValue);
29597             }
29598         }
29599         
29600         var _this = this;
29601         
29602         this.xhr.onload = function()
29603         {
29604             _this.xhrOnLoad(_this.xhr);
29605         }
29606         
29607         this.xhr.onerror = function()
29608         {
29609             _this.xhrOnError(_this.xhr);
29610         }
29611         
29612         var formData = new FormData();
29613
29614         formData.append('returnHTML', 'NO');
29615         
29616         formData.append('crop', crop);
29617         
29618         if(typeof(file.filename) != 'undefined'){
29619             formData.append('filename', file.filename);
29620         }
29621         
29622         if(typeof(file.mimetype) != 'undefined'){
29623             formData.append('mimetype', file.mimetype);
29624         }
29625         
29626         Roo.log(formData);
29627         
29628         if(this.fireEvent('prepare', this, formData) != false){
29629             this.xhr.send(formData);
29630         };
29631     }
29632 });
29633
29634 /*
29635 * Licence: LGPL
29636 */
29637
29638 /**
29639  * @class Roo.bootstrap.DocumentViewer
29640  * @extends Roo.bootstrap.Component
29641  * Bootstrap DocumentViewer class
29642  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29643  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29644  * 
29645  * @constructor
29646  * Create a new DocumentViewer
29647  * @param {Object} config The config object
29648  */
29649
29650 Roo.bootstrap.DocumentViewer = function(config){
29651     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29652     
29653     this.addEvents({
29654         /**
29655          * @event initial
29656          * Fire after initEvent
29657          * @param {Roo.bootstrap.DocumentViewer} this
29658          */
29659         "initial" : true,
29660         /**
29661          * @event click
29662          * Fire after click
29663          * @param {Roo.bootstrap.DocumentViewer} this
29664          */
29665         "click" : true,
29666         /**
29667          * @event download
29668          * Fire after download button
29669          * @param {Roo.bootstrap.DocumentViewer} this
29670          */
29671         "download" : true,
29672         /**
29673          * @event trash
29674          * Fire after trash button
29675          * @param {Roo.bootstrap.DocumentViewer} this
29676          */
29677         "trash" : true
29678         
29679     });
29680 };
29681
29682 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29683     
29684     showDownload : true,
29685     
29686     showTrash : true,
29687     
29688     getAutoCreate : function()
29689     {
29690         var cfg = {
29691             tag : 'div',
29692             cls : 'roo-document-viewer',
29693             cn : [
29694                 {
29695                     tag : 'div',
29696                     cls : 'roo-document-viewer-body',
29697                     cn : [
29698                         {
29699                             tag : 'div',
29700                             cls : 'roo-document-viewer-thumb',
29701                             cn : [
29702                                 {
29703                                     tag : 'img',
29704                                     cls : 'roo-document-viewer-image'
29705                                 }
29706                             ]
29707                         }
29708                     ]
29709                 },
29710                 {
29711                     tag : 'div',
29712                     cls : 'roo-document-viewer-footer',
29713                     cn : {
29714                         tag : 'div',
29715                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29716                         cn : [
29717                             {
29718                                 tag : 'div',
29719                                 cls : 'btn-group roo-document-viewer-download',
29720                                 cn : [
29721                                     {
29722                                         tag : 'button',
29723                                         cls : 'btn btn-default',
29724                                         html : '<i class="fa fa-download"></i>'
29725                                     }
29726                                 ]
29727                             },
29728                             {
29729                                 tag : 'div',
29730                                 cls : 'btn-group roo-document-viewer-trash',
29731                                 cn : [
29732                                     {
29733                                         tag : 'button',
29734                                         cls : 'btn btn-default',
29735                                         html : '<i class="fa fa-trash"></i>'
29736                                     }
29737                                 ]
29738                             }
29739                         ]
29740                     }
29741                 }
29742             ]
29743         };
29744         
29745         return cfg;
29746     },
29747     
29748     initEvents : function()
29749     {
29750         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29751         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29752         
29753         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29754         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29755         
29756         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29757         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29758         
29759         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29760         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29761         
29762         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29763         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29764         
29765         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29766         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29767         
29768         this.bodyEl.on('click', this.onClick, this);
29769         this.downloadBtn.on('click', this.onDownload, this);
29770         this.trashBtn.on('click', this.onTrash, this);
29771         
29772         this.downloadBtn.hide();
29773         this.trashBtn.hide();
29774         
29775         if(this.showDownload){
29776             this.downloadBtn.show();
29777         }
29778         
29779         if(this.showTrash){
29780             this.trashBtn.show();
29781         }
29782         
29783         if(!this.showDownload && !this.showTrash) {
29784             this.footerEl.hide();
29785         }
29786         
29787     },
29788     
29789     initial : function()
29790     {
29791         this.fireEvent('initial', this);
29792         
29793     },
29794     
29795     onClick : function(e)
29796     {
29797         e.preventDefault();
29798         
29799         this.fireEvent('click', this);
29800     },
29801     
29802     onDownload : function(e)
29803     {
29804         e.preventDefault();
29805         
29806         this.fireEvent('download', this);
29807     },
29808     
29809     onTrash : function(e)
29810     {
29811         e.preventDefault();
29812         
29813         this.fireEvent('trash', this);
29814     }
29815     
29816 });
29817 /*
29818  * - LGPL
29819  *
29820  * nav progress bar
29821  * 
29822  */
29823
29824 /**
29825  * @class Roo.bootstrap.NavProgressBar
29826  * @extends Roo.bootstrap.Component
29827  * Bootstrap NavProgressBar class
29828  * 
29829  * @constructor
29830  * Create a new nav progress bar
29831  * @param {Object} config The config object
29832  */
29833
29834 Roo.bootstrap.NavProgressBar = function(config){
29835     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29836
29837     this.bullets = this.bullets || [];
29838    
29839 //    Roo.bootstrap.NavProgressBar.register(this);
29840      this.addEvents({
29841         /**
29842              * @event changed
29843              * Fires when the active item changes
29844              * @param {Roo.bootstrap.NavProgressBar} this
29845              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29846              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29847          */
29848         'changed': true
29849      });
29850     
29851 };
29852
29853 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29854     
29855     bullets : [],
29856     barItems : [],
29857     
29858     getAutoCreate : function()
29859     {
29860         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29861         
29862         cfg = {
29863             tag : 'div',
29864             cls : 'roo-navigation-bar-group',
29865             cn : [
29866                 {
29867                     tag : 'div',
29868                     cls : 'roo-navigation-top-bar'
29869                 },
29870                 {
29871                     tag : 'div',
29872                     cls : 'roo-navigation-bullets-bar',
29873                     cn : [
29874                         {
29875                             tag : 'ul',
29876                             cls : 'roo-navigation-bar'
29877                         }
29878                     ]
29879                 },
29880                 
29881                 {
29882                     tag : 'div',
29883                     cls : 'roo-navigation-bottom-bar'
29884                 }
29885             ]
29886             
29887         };
29888         
29889         return cfg;
29890         
29891     },
29892     
29893     initEvents: function() 
29894     {
29895         
29896     },
29897     
29898     onRender : function(ct, position) 
29899     {
29900         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29901         
29902         if(this.bullets.length){
29903             Roo.each(this.bullets, function(b){
29904                this.addItem(b);
29905             }, this);
29906         }
29907         
29908         this.format();
29909         
29910     },
29911     
29912     addItem : function(cfg)
29913     {
29914         var item = new Roo.bootstrap.NavProgressItem(cfg);
29915         
29916         item.parentId = this.id;
29917         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29918         
29919         if(cfg.html){
29920             var top = new Roo.bootstrap.Element({
29921                 tag : 'div',
29922                 cls : 'roo-navigation-bar-text'
29923             });
29924             
29925             var bottom = new Roo.bootstrap.Element({
29926                 tag : 'div',
29927                 cls : 'roo-navigation-bar-text'
29928             });
29929             
29930             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29931             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29932             
29933             var topText = new Roo.bootstrap.Element({
29934                 tag : 'span',
29935                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29936             });
29937             
29938             var bottomText = new Roo.bootstrap.Element({
29939                 tag : 'span',
29940                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29941             });
29942             
29943             topText.onRender(top.el, null);
29944             bottomText.onRender(bottom.el, null);
29945             
29946             item.topEl = top;
29947             item.bottomEl = bottom;
29948         }
29949         
29950         this.barItems.push(item);
29951         
29952         return item;
29953     },
29954     
29955     getActive : function()
29956     {
29957         var active = false;
29958         
29959         Roo.each(this.barItems, function(v){
29960             
29961             if (!v.isActive()) {
29962                 return;
29963             }
29964             
29965             active = v;
29966             return false;
29967             
29968         });
29969         
29970         return active;
29971     },
29972     
29973     setActiveItem : function(item)
29974     {
29975         var prev = false;
29976         
29977         Roo.each(this.barItems, function(v){
29978             if (v.rid == item.rid) {
29979                 return ;
29980             }
29981             
29982             if (v.isActive()) {
29983                 v.setActive(false);
29984                 prev = v;
29985             }
29986         });
29987
29988         item.setActive(true);
29989         
29990         this.fireEvent('changed', this, item, prev);
29991     },
29992     
29993     getBarItem: function(rid)
29994     {
29995         var ret = false;
29996         
29997         Roo.each(this.barItems, function(e) {
29998             if (e.rid != rid) {
29999                 return;
30000             }
30001             
30002             ret =  e;
30003             return false;
30004         });
30005         
30006         return ret;
30007     },
30008     
30009     indexOfItem : function(item)
30010     {
30011         var index = false;
30012         
30013         Roo.each(this.barItems, function(v, i){
30014             
30015             if (v.rid != item.rid) {
30016                 return;
30017             }
30018             
30019             index = i;
30020             return false
30021         });
30022         
30023         return index;
30024     },
30025     
30026     setActiveNext : function()
30027     {
30028         var i = this.indexOfItem(this.getActive());
30029         
30030         if (i > this.barItems.length) {
30031             return;
30032         }
30033         
30034         this.setActiveItem(this.barItems[i+1]);
30035     },
30036     
30037     setActivePrev : function()
30038     {
30039         var i = this.indexOfItem(this.getActive());
30040         
30041         if (i  < 1) {
30042             return;
30043         }
30044         
30045         this.setActiveItem(this.barItems[i-1]);
30046     },
30047     
30048     format : function()
30049     {
30050         if(!this.barItems.length){
30051             return;
30052         }
30053      
30054         var width = 100 / this.barItems.length;
30055         
30056         Roo.each(this.barItems, function(i){
30057             i.el.setStyle('width', width + '%');
30058             i.topEl.el.setStyle('width', width + '%');
30059             i.bottomEl.el.setStyle('width', width + '%');
30060         }, this);
30061         
30062     }
30063     
30064 });
30065 /*
30066  * - LGPL
30067  *
30068  * Nav Progress Item
30069  * 
30070  */
30071
30072 /**
30073  * @class Roo.bootstrap.NavProgressItem
30074  * @extends Roo.bootstrap.Component
30075  * Bootstrap NavProgressItem class
30076  * @cfg {String} rid the reference id
30077  * @cfg {Boolean} active (true|false) Is item active default false
30078  * @cfg {Boolean} disabled (true|false) Is item active default false
30079  * @cfg {String} html
30080  * @cfg {String} position (top|bottom) text position default bottom
30081  * @cfg {String} icon show icon instead of number
30082  * 
30083  * @constructor
30084  * Create a new NavProgressItem
30085  * @param {Object} config The config object
30086  */
30087 Roo.bootstrap.NavProgressItem = function(config){
30088     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30089     this.addEvents({
30090         // raw events
30091         /**
30092          * @event click
30093          * The raw click event for the entire grid.
30094          * @param {Roo.bootstrap.NavProgressItem} this
30095          * @param {Roo.EventObject} e
30096          */
30097         "click" : true
30098     });
30099    
30100 };
30101
30102 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30103     
30104     rid : '',
30105     active : false,
30106     disabled : false,
30107     html : '',
30108     position : 'bottom',
30109     icon : false,
30110     
30111     getAutoCreate : function()
30112     {
30113         var iconCls = 'roo-navigation-bar-item-icon';
30114         
30115         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30116         
30117         var cfg = {
30118             tag: 'li',
30119             cls: 'roo-navigation-bar-item',
30120             cn : [
30121                 {
30122                     tag : 'i',
30123                     cls : iconCls
30124                 }
30125             ]
30126         };
30127         
30128         if(this.active){
30129             cfg.cls += ' active';
30130         }
30131         if(this.disabled){
30132             cfg.cls += ' disabled';
30133         }
30134         
30135         return cfg;
30136     },
30137     
30138     disable : function()
30139     {
30140         this.setDisabled(true);
30141     },
30142     
30143     enable : function()
30144     {
30145         this.setDisabled(false);
30146     },
30147     
30148     initEvents: function() 
30149     {
30150         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30151         
30152         this.iconEl.on('click', this.onClick, this);
30153     },
30154     
30155     onClick : function(e)
30156     {
30157         e.preventDefault();
30158         
30159         if(this.disabled){
30160             return;
30161         }
30162         
30163         if(this.fireEvent('click', this, e) === false){
30164             return;
30165         };
30166         
30167         this.parent().setActiveItem(this);
30168     },
30169     
30170     isActive: function () 
30171     {
30172         return this.active;
30173     },
30174     
30175     setActive : function(state)
30176     {
30177         if(this.active == state){
30178             return;
30179         }
30180         
30181         this.active = state;
30182         
30183         if (state) {
30184             this.el.addClass('active');
30185             return;
30186         }
30187         
30188         this.el.removeClass('active');
30189         
30190         return;
30191     },
30192     
30193     setDisabled : function(state)
30194     {
30195         if(this.disabled == state){
30196             return;
30197         }
30198         
30199         this.disabled = state;
30200         
30201         if (state) {
30202             this.el.addClass('disabled');
30203             return;
30204         }
30205         
30206         this.el.removeClass('disabled');
30207     },
30208     
30209     tooltipEl : function()
30210     {
30211         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30212     }
30213 });
30214  
30215
30216  /*
30217  * - LGPL
30218  *
30219  * FieldLabel
30220  * 
30221  */
30222
30223 /**
30224  * @class Roo.bootstrap.FieldLabel
30225  * @extends Roo.bootstrap.Component
30226  * Bootstrap FieldLabel class
30227  * @cfg {String} html contents of the element
30228  * @cfg {String} tag tag of the element default label
30229  * @cfg {String} cls class of the element
30230  * @cfg {String} target label target 
30231  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30232  * @cfg {String} invalidClass default "text-warning"
30233  * @cfg {String} validClass default "text-success"
30234  * @cfg {String} iconTooltip default "This field is required"
30235  * @cfg {String} indicatorpos (left|right) default left
30236  * 
30237  * @constructor
30238  * Create a new FieldLabel
30239  * @param {Object} config The config object
30240  */
30241
30242 Roo.bootstrap.FieldLabel = function(config){
30243     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30244     
30245     this.addEvents({
30246             /**
30247              * @event invalid
30248              * Fires after the field has been marked as invalid.
30249              * @param {Roo.form.FieldLabel} this
30250              * @param {String} msg The validation message
30251              */
30252             invalid : true,
30253             /**
30254              * @event valid
30255              * Fires after the field has been validated with no errors.
30256              * @param {Roo.form.FieldLabel} this
30257              */
30258             valid : true
30259         });
30260 };
30261
30262 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30263     
30264     tag: 'label',
30265     cls: '',
30266     html: '',
30267     target: '',
30268     allowBlank : true,
30269     invalidClass : 'has-warning',
30270     validClass : 'has-success',
30271     iconTooltip : 'This field is required',
30272     indicatorpos : 'left',
30273     
30274     getAutoCreate : function(){
30275         
30276         var cls = "";
30277         if (!this.allowBlank) {
30278             cls  = "visible";
30279         }
30280         
30281         var cfg = {
30282             tag : this.tag,
30283             cls : 'roo-bootstrap-field-label ' + this.cls,
30284             for : this.target,
30285             cn : [
30286                 {
30287                     tag : 'i',
30288                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30289                     tooltip : this.iconTooltip
30290                 },
30291                 {
30292                     tag : 'span',
30293                     html : this.html
30294                 }
30295             ] 
30296         };
30297         
30298         if(this.indicatorpos == 'right'){
30299             var cfg = {
30300                 tag : this.tag,
30301                 cls : 'roo-bootstrap-field-label ' + this.cls,
30302                 for : this.target,
30303                 cn : [
30304                     {
30305                         tag : 'span',
30306                         html : this.html
30307                     },
30308                     {
30309                         tag : 'i',
30310                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30311                         tooltip : this.iconTooltip
30312                     }
30313                 ] 
30314             };
30315         }
30316         
30317         return cfg;
30318     },
30319     
30320     initEvents: function() 
30321     {
30322         Roo.bootstrap.Element.superclass.initEvents.call(this);
30323         
30324         this.indicator = this.indicatorEl();
30325         
30326         if(this.indicator){
30327             this.indicator.removeClass('visible');
30328             this.indicator.addClass('invisible');
30329         }
30330         
30331         Roo.bootstrap.FieldLabel.register(this);
30332     },
30333     
30334     indicatorEl : function()
30335     {
30336         var indicator = this.el.select('i.roo-required-indicator',true).first();
30337         
30338         if(!indicator){
30339             return false;
30340         }
30341         
30342         return indicator;
30343         
30344     },
30345     
30346     /**
30347      * Mark this field as valid
30348      */
30349     markValid : function()
30350     {
30351         if(this.indicator){
30352             this.indicator.removeClass('visible');
30353             this.indicator.addClass('invisible');
30354         }
30355         
30356         this.el.removeClass(this.invalidClass);
30357         
30358         this.el.addClass(this.validClass);
30359         
30360         this.fireEvent('valid', this);
30361     },
30362     
30363     /**
30364      * Mark this field as invalid
30365      * @param {String} msg The validation message
30366      */
30367     markInvalid : function(msg)
30368     {
30369         if(this.indicator){
30370             this.indicator.removeClass('invisible');
30371             this.indicator.addClass('visible');
30372         }
30373         
30374         this.el.removeClass(this.validClass);
30375         
30376         this.el.addClass(this.invalidClass);
30377         
30378         this.fireEvent('invalid', this, msg);
30379     }
30380     
30381    
30382 });
30383
30384 Roo.apply(Roo.bootstrap.FieldLabel, {
30385     
30386     groups: {},
30387     
30388      /**
30389     * register a FieldLabel Group
30390     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30391     */
30392     register : function(label)
30393     {
30394         if(this.groups.hasOwnProperty(label.target)){
30395             return;
30396         }
30397      
30398         this.groups[label.target] = label;
30399         
30400     },
30401     /**
30402     * fetch a FieldLabel Group based on the target
30403     * @param {string} target
30404     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30405     */
30406     get: function(target) {
30407         if (typeof(this.groups[target]) == 'undefined') {
30408             return false;
30409         }
30410         
30411         return this.groups[target] ;
30412     }
30413 });
30414
30415  
30416
30417  /*
30418  * - LGPL
30419  *
30420  * page DateSplitField.
30421  * 
30422  */
30423
30424
30425 /**
30426  * @class Roo.bootstrap.DateSplitField
30427  * @extends Roo.bootstrap.Component
30428  * Bootstrap DateSplitField class
30429  * @cfg {string} fieldLabel - the label associated
30430  * @cfg {Number} labelWidth set the width of label (0-12)
30431  * @cfg {String} labelAlign (top|left)
30432  * @cfg {Boolean} dayAllowBlank (true|false) default false
30433  * @cfg {Boolean} monthAllowBlank (true|false) default false
30434  * @cfg {Boolean} yearAllowBlank (true|false) default false
30435  * @cfg {string} dayPlaceholder 
30436  * @cfg {string} monthPlaceholder
30437  * @cfg {string} yearPlaceholder
30438  * @cfg {string} dayFormat default 'd'
30439  * @cfg {string} monthFormat default 'm'
30440  * @cfg {string} yearFormat default 'Y'
30441  * @cfg {Number} labellg set the width of label (1-12)
30442  * @cfg {Number} labelmd set the width of label (1-12)
30443  * @cfg {Number} labelsm set the width of label (1-12)
30444  * @cfg {Number} labelxs set the width of label (1-12)
30445
30446  *     
30447  * @constructor
30448  * Create a new DateSplitField
30449  * @param {Object} config The config object
30450  */
30451
30452 Roo.bootstrap.DateSplitField = function(config){
30453     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30454     
30455     this.addEvents({
30456         // raw events
30457          /**
30458          * @event years
30459          * getting the data of years
30460          * @param {Roo.bootstrap.DateSplitField} this
30461          * @param {Object} years
30462          */
30463         "years" : true,
30464         /**
30465          * @event days
30466          * getting the data of days
30467          * @param {Roo.bootstrap.DateSplitField} this
30468          * @param {Object} days
30469          */
30470         "days" : true,
30471         /**
30472          * @event invalid
30473          * Fires after the field has been marked as invalid.
30474          * @param {Roo.form.Field} this
30475          * @param {String} msg The validation message
30476          */
30477         invalid : true,
30478        /**
30479          * @event valid
30480          * Fires after the field has been validated with no errors.
30481          * @param {Roo.form.Field} this
30482          */
30483         valid : true
30484     });
30485 };
30486
30487 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30488     
30489     fieldLabel : '',
30490     labelAlign : 'top',
30491     labelWidth : 3,
30492     dayAllowBlank : false,
30493     monthAllowBlank : false,
30494     yearAllowBlank : false,
30495     dayPlaceholder : '',
30496     monthPlaceholder : '',
30497     yearPlaceholder : '',
30498     dayFormat : 'd',
30499     monthFormat : 'm',
30500     yearFormat : 'Y',
30501     isFormField : true,
30502     labellg : 0,
30503     labelmd : 0,
30504     labelsm : 0,
30505     labelxs : 0,
30506     
30507     getAutoCreate : function()
30508     {
30509         var cfg = {
30510             tag : 'div',
30511             cls : 'row roo-date-split-field-group',
30512             cn : [
30513                 {
30514                     tag : 'input',
30515                     type : 'hidden',
30516                     cls : 'form-hidden-field roo-date-split-field-group-value',
30517                     name : this.name
30518                 }
30519             ]
30520         };
30521         
30522         var labelCls = 'col-md-12';
30523         var contentCls = 'col-md-4';
30524         
30525         if(this.fieldLabel){
30526             
30527             var label = {
30528                 tag : 'div',
30529                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30530                 cn : [
30531                     {
30532                         tag : 'label',
30533                         html : this.fieldLabel
30534                     }
30535                 ]
30536             };
30537             
30538             if(this.labelAlign == 'left'){
30539             
30540                 if(this.labelWidth > 12){
30541                     label.style = "width: " + this.labelWidth + 'px';
30542                 }
30543
30544                 if(this.labelWidth < 13 && this.labelmd == 0){
30545                     this.labelmd = this.labelWidth;
30546                 }
30547
30548                 if(this.labellg > 0){
30549                     labelCls = ' col-lg-' + this.labellg;
30550                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30551                 }
30552
30553                 if(this.labelmd > 0){
30554                     labelCls = ' col-md-' + this.labelmd;
30555                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30556                 }
30557
30558                 if(this.labelsm > 0){
30559                     labelCls = ' col-sm-' + this.labelsm;
30560                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30561                 }
30562
30563                 if(this.labelxs > 0){
30564                     labelCls = ' col-xs-' + this.labelxs;
30565                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30566                 }
30567             }
30568             
30569             label.cls += ' ' + labelCls;
30570             
30571             cfg.cn.push(label);
30572         }
30573         
30574         Roo.each(['day', 'month', 'year'], function(t){
30575             cfg.cn.push({
30576                 tag : 'div',
30577                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30578             });
30579         }, this);
30580         
30581         return cfg;
30582     },
30583     
30584     inputEl: function ()
30585     {
30586         return this.el.select('.roo-date-split-field-group-value', true).first();
30587     },
30588     
30589     onRender : function(ct, position) 
30590     {
30591         var _this = this;
30592         
30593         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30594         
30595         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30596         
30597         this.dayField = new Roo.bootstrap.ComboBox({
30598             allowBlank : this.dayAllowBlank,
30599             alwaysQuery : true,
30600             displayField : 'value',
30601             editable : false,
30602             fieldLabel : '',
30603             forceSelection : true,
30604             mode : 'local',
30605             placeholder : this.dayPlaceholder,
30606             selectOnFocus : true,
30607             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30608             triggerAction : 'all',
30609             typeAhead : true,
30610             valueField : 'value',
30611             store : new Roo.data.SimpleStore({
30612                 data : (function() {    
30613                     var days = [];
30614                     _this.fireEvent('days', _this, days);
30615                     return days;
30616                 })(),
30617                 fields : [ 'value' ]
30618             }),
30619             listeners : {
30620                 select : function (_self, record, index)
30621                 {
30622                     _this.setValue(_this.getValue());
30623                 }
30624             }
30625         });
30626
30627         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30628         
30629         this.monthField = new Roo.bootstrap.MonthField({
30630             after : '<i class=\"fa fa-calendar\"></i>',
30631             allowBlank : this.monthAllowBlank,
30632             placeholder : this.monthPlaceholder,
30633             readOnly : true,
30634             listeners : {
30635                 render : function (_self)
30636                 {
30637                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30638                         e.preventDefault();
30639                         _self.focus();
30640                     });
30641                 },
30642                 select : function (_self, oldvalue, newvalue)
30643                 {
30644                     _this.setValue(_this.getValue());
30645                 }
30646             }
30647         });
30648         
30649         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30650         
30651         this.yearField = new Roo.bootstrap.ComboBox({
30652             allowBlank : this.yearAllowBlank,
30653             alwaysQuery : true,
30654             displayField : 'value',
30655             editable : false,
30656             fieldLabel : '',
30657             forceSelection : true,
30658             mode : 'local',
30659             placeholder : this.yearPlaceholder,
30660             selectOnFocus : true,
30661             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30662             triggerAction : 'all',
30663             typeAhead : true,
30664             valueField : 'value',
30665             store : new Roo.data.SimpleStore({
30666                 data : (function() {
30667                     var years = [];
30668                     _this.fireEvent('years', _this, years);
30669                     return years;
30670                 })(),
30671                 fields : [ 'value' ]
30672             }),
30673             listeners : {
30674                 select : function (_self, record, index)
30675                 {
30676                     _this.setValue(_this.getValue());
30677                 }
30678             }
30679         });
30680
30681         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30682     },
30683     
30684     setValue : function(v, format)
30685     {
30686         this.inputEl.dom.value = v;
30687         
30688         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30689         
30690         var d = Date.parseDate(v, f);
30691         
30692         if(!d){
30693             this.validate();
30694             return;
30695         }
30696         
30697         this.setDay(d.format(this.dayFormat));
30698         this.setMonth(d.format(this.monthFormat));
30699         this.setYear(d.format(this.yearFormat));
30700         
30701         this.validate();
30702         
30703         return;
30704     },
30705     
30706     setDay : function(v)
30707     {
30708         this.dayField.setValue(v);
30709         this.inputEl.dom.value = this.getValue();
30710         this.validate();
30711         return;
30712     },
30713     
30714     setMonth : function(v)
30715     {
30716         this.monthField.setValue(v, true);
30717         this.inputEl.dom.value = this.getValue();
30718         this.validate();
30719         return;
30720     },
30721     
30722     setYear : function(v)
30723     {
30724         this.yearField.setValue(v);
30725         this.inputEl.dom.value = this.getValue();
30726         this.validate();
30727         return;
30728     },
30729     
30730     getDay : function()
30731     {
30732         return this.dayField.getValue();
30733     },
30734     
30735     getMonth : function()
30736     {
30737         return this.monthField.getValue();
30738     },
30739     
30740     getYear : function()
30741     {
30742         return this.yearField.getValue();
30743     },
30744     
30745     getValue : function()
30746     {
30747         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30748         
30749         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30750         
30751         return date;
30752     },
30753     
30754     reset : function()
30755     {
30756         this.setDay('');
30757         this.setMonth('');
30758         this.setYear('');
30759         this.inputEl.dom.value = '';
30760         this.validate();
30761         return;
30762     },
30763     
30764     validate : function()
30765     {
30766         var d = this.dayField.validate();
30767         var m = this.monthField.validate();
30768         var y = this.yearField.validate();
30769         
30770         var valid = true;
30771         
30772         if(
30773                 (!this.dayAllowBlank && !d) ||
30774                 (!this.monthAllowBlank && !m) ||
30775                 (!this.yearAllowBlank && !y)
30776         ){
30777             valid = false;
30778         }
30779         
30780         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30781             return valid;
30782         }
30783         
30784         if(valid){
30785             this.markValid();
30786             return valid;
30787         }
30788         
30789         this.markInvalid();
30790         
30791         return valid;
30792     },
30793     
30794     markValid : function()
30795     {
30796         
30797         var label = this.el.select('label', true).first();
30798         var icon = this.el.select('i.fa-star', true).first();
30799
30800         if(label && icon){
30801             icon.remove();
30802         }
30803         
30804         this.fireEvent('valid', this);
30805     },
30806     
30807      /**
30808      * Mark this field as invalid
30809      * @param {String} msg The validation message
30810      */
30811     markInvalid : function(msg)
30812     {
30813         
30814         var label = this.el.select('label', true).first();
30815         var icon = this.el.select('i.fa-star', true).first();
30816
30817         if(label && !icon){
30818             this.el.select('.roo-date-split-field-label', true).createChild({
30819                 tag : 'i',
30820                 cls : 'text-danger fa fa-lg fa-star',
30821                 tooltip : 'This field is required',
30822                 style : 'margin-right:5px;'
30823             }, label, true);
30824         }
30825         
30826         this.fireEvent('invalid', this, msg);
30827     },
30828     
30829     clearInvalid : function()
30830     {
30831         var label = this.el.select('label', true).first();
30832         var icon = this.el.select('i.fa-star', true).first();
30833
30834         if(label && icon){
30835             icon.remove();
30836         }
30837         
30838         this.fireEvent('valid', this);
30839     },
30840     
30841     getName: function()
30842     {
30843         return this.name;
30844     }
30845     
30846 });
30847
30848  /**
30849  *
30850  * This is based on 
30851  * http://masonry.desandro.com
30852  *
30853  * The idea is to render all the bricks based on vertical width...
30854  *
30855  * The original code extends 'outlayer' - we might need to use that....
30856  * 
30857  */
30858
30859
30860 /**
30861  * @class Roo.bootstrap.LayoutMasonry
30862  * @extends Roo.bootstrap.Component
30863  * Bootstrap Layout Masonry class
30864  * 
30865  * @constructor
30866  * Create a new Element
30867  * @param {Object} config The config object
30868  */
30869
30870 Roo.bootstrap.LayoutMasonry = function(config){
30871     
30872     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30873     
30874     this.bricks = [];
30875     
30876     Roo.bootstrap.LayoutMasonry.register(this);
30877     
30878     this.addEvents({
30879         // raw events
30880         /**
30881          * @event layout
30882          * Fire after layout the items
30883          * @param {Roo.bootstrap.LayoutMasonry} this
30884          * @param {Roo.EventObject} e
30885          */
30886         "layout" : true
30887     });
30888     
30889 };
30890
30891 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30892     
30893     /**
30894      * @cfg {Boolean} isLayoutInstant = no animation?
30895      */   
30896     isLayoutInstant : false, // needed?
30897    
30898     /**
30899      * @cfg {Number} boxWidth  width of the columns
30900      */   
30901     boxWidth : 450,
30902     
30903       /**
30904      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30905      */   
30906     boxHeight : 0,
30907     
30908     /**
30909      * @cfg {Number} padWidth padding below box..
30910      */   
30911     padWidth : 10, 
30912     
30913     /**
30914      * @cfg {Number} gutter gutter width..
30915      */   
30916     gutter : 10,
30917     
30918      /**
30919      * @cfg {Number} maxCols maximum number of columns
30920      */   
30921     
30922     maxCols: 0,
30923     
30924     /**
30925      * @cfg {Boolean} isAutoInitial defalut true
30926      */   
30927     isAutoInitial : true, 
30928     
30929     containerWidth: 0,
30930     
30931     /**
30932      * @cfg {Boolean} isHorizontal defalut false
30933      */   
30934     isHorizontal : false, 
30935
30936     currentSize : null,
30937     
30938     tag: 'div',
30939     
30940     cls: '',
30941     
30942     bricks: null, //CompositeElement
30943     
30944     cols : 1,
30945     
30946     _isLayoutInited : false,
30947     
30948 //    isAlternative : false, // only use for vertical layout...
30949     
30950     /**
30951      * @cfg {Number} alternativePadWidth padding below box..
30952      */   
30953     alternativePadWidth : 50,
30954     
30955     selectedBrick : [],
30956     
30957     getAutoCreate : function(){
30958         
30959         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30960         
30961         var cfg = {
30962             tag: this.tag,
30963             cls: 'blog-masonary-wrapper ' + this.cls,
30964             cn : {
30965                 cls : 'mas-boxes masonary'
30966             }
30967         };
30968         
30969         return cfg;
30970     },
30971     
30972     getChildContainer: function( )
30973     {
30974         if (this.boxesEl) {
30975             return this.boxesEl;
30976         }
30977         
30978         this.boxesEl = this.el.select('.mas-boxes').first();
30979         
30980         return this.boxesEl;
30981     },
30982     
30983     
30984     initEvents : function()
30985     {
30986         var _this = this;
30987         
30988         if(this.isAutoInitial){
30989             Roo.log('hook children rendered');
30990             this.on('childrenrendered', function() {
30991                 Roo.log('children rendered');
30992                 _this.initial();
30993             } ,this);
30994         }
30995     },
30996     
30997     initial : function()
30998     {
30999         this.selectedBrick = [];
31000         
31001         this.currentSize = this.el.getBox(true);
31002         
31003         Roo.EventManager.onWindowResize(this.resize, this); 
31004
31005         if(!this.isAutoInitial){
31006             this.layout();
31007             return;
31008         }
31009         
31010         this.layout();
31011         
31012         return;
31013         //this.layout.defer(500,this);
31014         
31015     },
31016     
31017     resize : function()
31018     {
31019         var cs = this.el.getBox(true);
31020         
31021         if (
31022                 this.currentSize.width == cs.width && 
31023                 this.currentSize.x == cs.x && 
31024                 this.currentSize.height == cs.height && 
31025                 this.currentSize.y == cs.y 
31026         ) {
31027             Roo.log("no change in with or X or Y");
31028             return;
31029         }
31030         
31031         this.currentSize = cs;
31032         
31033         this.layout();
31034         
31035     },
31036     
31037     layout : function()
31038     {   
31039         this._resetLayout();
31040         
31041         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31042         
31043         this.layoutItems( isInstant );
31044       
31045         this._isLayoutInited = true;
31046         
31047         this.fireEvent('layout', this);
31048         
31049     },
31050     
31051     _resetLayout : function()
31052     {
31053         if(this.isHorizontal){
31054             this.horizontalMeasureColumns();
31055             return;
31056         }
31057         
31058         this.verticalMeasureColumns();
31059         
31060     },
31061     
31062     verticalMeasureColumns : function()
31063     {
31064         this.getContainerWidth();
31065         
31066 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31067 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31068 //            return;
31069 //        }
31070         
31071         var boxWidth = this.boxWidth + this.padWidth;
31072         
31073         if(this.containerWidth < this.boxWidth){
31074             boxWidth = this.containerWidth
31075         }
31076         
31077         var containerWidth = this.containerWidth;
31078         
31079         var cols = Math.floor(containerWidth / boxWidth);
31080         
31081         this.cols = Math.max( cols, 1 );
31082         
31083         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31084         
31085         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31086         
31087         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31088         
31089         this.colWidth = boxWidth + avail - this.padWidth;
31090         
31091         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31092         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31093     },
31094     
31095     horizontalMeasureColumns : function()
31096     {
31097         this.getContainerWidth();
31098         
31099         var boxWidth = this.boxWidth;
31100         
31101         if(this.containerWidth < boxWidth){
31102             boxWidth = this.containerWidth;
31103         }
31104         
31105         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31106         
31107         this.el.setHeight(boxWidth);
31108         
31109     },
31110     
31111     getContainerWidth : function()
31112     {
31113         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31114     },
31115     
31116     layoutItems : function( isInstant )
31117     {
31118         Roo.log(this.bricks);
31119         
31120         var items = Roo.apply([], this.bricks);
31121         
31122         if(this.isHorizontal){
31123             this._horizontalLayoutItems( items , isInstant );
31124             return;
31125         }
31126         
31127 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31128 //            this._verticalAlternativeLayoutItems( items , isInstant );
31129 //            return;
31130 //        }
31131         
31132         this._verticalLayoutItems( items , isInstant );
31133         
31134     },
31135     
31136     _verticalLayoutItems : function ( items , isInstant)
31137     {
31138         if ( !items || !items.length ) {
31139             return;
31140         }
31141         
31142         var standard = [
31143             ['xs', 'xs', 'xs', 'tall'],
31144             ['xs', 'xs', 'tall'],
31145             ['xs', 'xs', 'sm'],
31146             ['xs', 'xs', 'xs'],
31147             ['xs', 'tall'],
31148             ['xs', 'sm'],
31149             ['xs', 'xs'],
31150             ['xs'],
31151             
31152             ['sm', 'xs', 'xs'],
31153             ['sm', 'xs'],
31154             ['sm'],
31155             
31156             ['tall', 'xs', 'xs', 'xs'],
31157             ['tall', 'xs', 'xs'],
31158             ['tall', 'xs'],
31159             ['tall']
31160             
31161         ];
31162         
31163         var queue = [];
31164         
31165         var boxes = [];
31166         
31167         var box = [];
31168         
31169         Roo.each(items, function(item, k){
31170             
31171             switch (item.size) {
31172                 // these layouts take up a full box,
31173                 case 'md' :
31174                 case 'md-left' :
31175                 case 'md-right' :
31176                 case 'wide' :
31177                     
31178                     if(box.length){
31179                         boxes.push(box);
31180                         box = [];
31181                     }
31182                     
31183                     boxes.push([item]);
31184                     
31185                     break;
31186                     
31187                 case 'xs' :
31188                 case 'sm' :
31189                 case 'tall' :
31190                     
31191                     box.push(item);
31192                     
31193                     break;
31194                 default :
31195                     break;
31196                     
31197             }
31198             
31199         }, this);
31200         
31201         if(box.length){
31202             boxes.push(box);
31203             box = [];
31204         }
31205         
31206         var filterPattern = function(box, length)
31207         {
31208             if(!box.length){
31209                 return;
31210             }
31211             
31212             var match = false;
31213             
31214             var pattern = box.slice(0, length);
31215             
31216             var format = [];
31217             
31218             Roo.each(pattern, function(i){
31219                 format.push(i.size);
31220             }, this);
31221             
31222             Roo.each(standard, function(s){
31223                 
31224                 if(String(s) != String(format)){
31225                     return;
31226                 }
31227                 
31228                 match = true;
31229                 return false;
31230                 
31231             }, this);
31232             
31233             if(!match && length == 1){
31234                 return;
31235             }
31236             
31237             if(!match){
31238                 filterPattern(box, length - 1);
31239                 return;
31240             }
31241                 
31242             queue.push(pattern);
31243
31244             box = box.slice(length, box.length);
31245
31246             filterPattern(box, 4);
31247
31248             return;
31249             
31250         }
31251         
31252         Roo.each(boxes, function(box, k){
31253             
31254             if(!box.length){
31255                 return;
31256             }
31257             
31258             if(box.length == 1){
31259                 queue.push(box);
31260                 return;
31261             }
31262             
31263             filterPattern(box, 4);
31264             
31265         }, this);
31266         
31267         this._processVerticalLayoutQueue( queue, isInstant );
31268         
31269     },
31270     
31271 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31272 //    {
31273 //        if ( !items || !items.length ) {
31274 //            return;
31275 //        }
31276 //
31277 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31278 //        
31279 //    },
31280     
31281     _horizontalLayoutItems : function ( items , isInstant)
31282     {
31283         if ( !items || !items.length || items.length < 3) {
31284             return;
31285         }
31286         
31287         items.reverse();
31288         
31289         var eItems = items.slice(0, 3);
31290         
31291         items = items.slice(3, items.length);
31292         
31293         var standard = [
31294             ['xs', 'xs', 'xs', 'wide'],
31295             ['xs', 'xs', 'wide'],
31296             ['xs', 'xs', 'sm'],
31297             ['xs', 'xs', 'xs'],
31298             ['xs', 'wide'],
31299             ['xs', 'sm'],
31300             ['xs', 'xs'],
31301             ['xs'],
31302             
31303             ['sm', 'xs', 'xs'],
31304             ['sm', 'xs'],
31305             ['sm'],
31306             
31307             ['wide', 'xs', 'xs', 'xs'],
31308             ['wide', 'xs', 'xs'],
31309             ['wide', 'xs'],
31310             ['wide'],
31311             
31312             ['wide-thin']
31313         ];
31314         
31315         var queue = [];
31316         
31317         var boxes = [];
31318         
31319         var box = [];
31320         
31321         Roo.each(items, function(item, k){
31322             
31323             switch (item.size) {
31324                 case 'md' :
31325                 case 'md-left' :
31326                 case 'md-right' :
31327                 case 'tall' :
31328                     
31329                     if(box.length){
31330                         boxes.push(box);
31331                         box = [];
31332                     }
31333                     
31334                     boxes.push([item]);
31335                     
31336                     break;
31337                     
31338                 case 'xs' :
31339                 case 'sm' :
31340                 case 'wide' :
31341                 case 'wide-thin' :
31342                     
31343                     box.push(item);
31344                     
31345                     break;
31346                 default :
31347                     break;
31348                     
31349             }
31350             
31351         }, this);
31352         
31353         if(box.length){
31354             boxes.push(box);
31355             box = [];
31356         }
31357         
31358         var filterPattern = function(box, length)
31359         {
31360             if(!box.length){
31361                 return;
31362             }
31363             
31364             var match = false;
31365             
31366             var pattern = box.slice(0, length);
31367             
31368             var format = [];
31369             
31370             Roo.each(pattern, function(i){
31371                 format.push(i.size);
31372             }, this);
31373             
31374             Roo.each(standard, function(s){
31375                 
31376                 if(String(s) != String(format)){
31377                     return;
31378                 }
31379                 
31380                 match = true;
31381                 return false;
31382                 
31383             }, this);
31384             
31385             if(!match && length == 1){
31386                 return;
31387             }
31388             
31389             if(!match){
31390                 filterPattern(box, length - 1);
31391                 return;
31392             }
31393                 
31394             queue.push(pattern);
31395
31396             box = box.slice(length, box.length);
31397
31398             filterPattern(box, 4);
31399
31400             return;
31401             
31402         }
31403         
31404         Roo.each(boxes, function(box, k){
31405             
31406             if(!box.length){
31407                 return;
31408             }
31409             
31410             if(box.length == 1){
31411                 queue.push(box);
31412                 return;
31413             }
31414             
31415             filterPattern(box, 4);
31416             
31417         }, this);
31418         
31419         
31420         var prune = [];
31421         
31422         var pos = this.el.getBox(true);
31423         
31424         var minX = pos.x;
31425         
31426         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31427         
31428         var hit_end = false;
31429         
31430         Roo.each(queue, function(box){
31431             
31432             if(hit_end){
31433                 
31434                 Roo.each(box, function(b){
31435                 
31436                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31437                     b.el.hide();
31438
31439                 }, this);
31440
31441                 return;
31442             }
31443             
31444             var mx = 0;
31445             
31446             Roo.each(box, function(b){
31447                 
31448                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31449                 b.el.show();
31450
31451                 mx = Math.max(mx, b.x);
31452                 
31453             }, this);
31454             
31455             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31456             
31457             if(maxX < minX){
31458                 
31459                 Roo.each(box, function(b){
31460                 
31461                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31462                     b.el.hide();
31463                     
31464                 }, this);
31465                 
31466                 hit_end = true;
31467                 
31468                 return;
31469             }
31470             
31471             prune.push(box);
31472             
31473         }, this);
31474         
31475         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31476     },
31477     
31478     /** Sets position of item in DOM
31479     * @param {Element} item
31480     * @param {Number} x - horizontal position
31481     * @param {Number} y - vertical position
31482     * @param {Boolean} isInstant - disables transitions
31483     */
31484     _processVerticalLayoutQueue : function( queue, isInstant )
31485     {
31486         var pos = this.el.getBox(true);
31487         var x = pos.x;
31488         var y = pos.y;
31489         var maxY = [];
31490         
31491         for (var i = 0; i < this.cols; i++){
31492             maxY[i] = pos.y;
31493         }
31494         
31495         Roo.each(queue, function(box, k){
31496             
31497             var col = k % this.cols;
31498             
31499             Roo.each(box, function(b,kk){
31500                 
31501                 b.el.position('absolute');
31502                 
31503                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31504                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31505                 
31506                 if(b.size == 'md-left' || b.size == 'md-right'){
31507                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31508                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31509                 }
31510                 
31511                 b.el.setWidth(width);
31512                 b.el.setHeight(height);
31513                 // iframe?
31514                 b.el.select('iframe',true).setSize(width,height);
31515                 
31516             }, this);
31517             
31518             for (var i = 0; i < this.cols; i++){
31519                 
31520                 if(maxY[i] < maxY[col]){
31521                     col = i;
31522                     continue;
31523                 }
31524                 
31525                 col = Math.min(col, i);
31526                 
31527             }
31528             
31529             x = pos.x + col * (this.colWidth + this.padWidth);
31530             
31531             y = maxY[col];
31532             
31533             var positions = [];
31534             
31535             switch (box.length){
31536                 case 1 :
31537                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31538                     break;
31539                 case 2 :
31540                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31541                     break;
31542                 case 3 :
31543                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31544                     break;
31545                 case 4 :
31546                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31547                     break;
31548                 default :
31549                     break;
31550             }
31551             
31552             Roo.each(box, function(b,kk){
31553                 
31554                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31555                 
31556                 var sz = b.el.getSize();
31557                 
31558                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31559                 
31560             }, this);
31561             
31562         }, this);
31563         
31564         var mY = 0;
31565         
31566         for (var i = 0; i < this.cols; i++){
31567             mY = Math.max(mY, maxY[i]);
31568         }
31569         
31570         this.el.setHeight(mY - pos.y);
31571         
31572     },
31573     
31574 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31575 //    {
31576 //        var pos = this.el.getBox(true);
31577 //        var x = pos.x;
31578 //        var y = pos.y;
31579 //        var maxX = pos.right;
31580 //        
31581 //        var maxHeight = 0;
31582 //        
31583 //        Roo.each(items, function(item, k){
31584 //            
31585 //            var c = k % 2;
31586 //            
31587 //            item.el.position('absolute');
31588 //                
31589 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31590 //
31591 //            item.el.setWidth(width);
31592 //
31593 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31594 //
31595 //            item.el.setHeight(height);
31596 //            
31597 //            if(c == 0){
31598 //                item.el.setXY([x, y], isInstant ? false : true);
31599 //            } else {
31600 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31601 //            }
31602 //            
31603 //            y = y + height + this.alternativePadWidth;
31604 //            
31605 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31606 //            
31607 //        }, this);
31608 //        
31609 //        this.el.setHeight(maxHeight);
31610 //        
31611 //    },
31612     
31613     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31614     {
31615         var pos = this.el.getBox(true);
31616         
31617         var minX = pos.x;
31618         var minY = pos.y;
31619         
31620         var maxX = pos.right;
31621         
31622         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31623         
31624         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31625         
31626         Roo.each(queue, function(box, k){
31627             
31628             Roo.each(box, function(b, kk){
31629                 
31630                 b.el.position('absolute');
31631                 
31632                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31633                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31634                 
31635                 if(b.size == 'md-left' || b.size == 'md-right'){
31636                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31637                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31638                 }
31639                 
31640                 b.el.setWidth(width);
31641                 b.el.setHeight(height);
31642                 
31643             }, this);
31644             
31645             if(!box.length){
31646                 return;
31647             }
31648             
31649             var positions = [];
31650             
31651             switch (box.length){
31652                 case 1 :
31653                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31654                     break;
31655                 case 2 :
31656                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31657                     break;
31658                 case 3 :
31659                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31660                     break;
31661                 case 4 :
31662                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31663                     break;
31664                 default :
31665                     break;
31666             }
31667             
31668             Roo.each(box, function(b,kk){
31669                 
31670                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31671                 
31672                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31673                 
31674             }, this);
31675             
31676         }, this);
31677         
31678     },
31679     
31680     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31681     {
31682         Roo.each(eItems, function(b,k){
31683             
31684             b.size = (k == 0) ? 'sm' : 'xs';
31685             b.x = (k == 0) ? 2 : 1;
31686             b.y = (k == 0) ? 2 : 1;
31687             
31688             b.el.position('absolute');
31689             
31690             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31691                 
31692             b.el.setWidth(width);
31693             
31694             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31695             
31696             b.el.setHeight(height);
31697             
31698         }, this);
31699
31700         var positions = [];
31701         
31702         positions.push({
31703             x : maxX - this.unitWidth * 2 - this.gutter,
31704             y : minY
31705         });
31706         
31707         positions.push({
31708             x : maxX - this.unitWidth,
31709             y : minY + (this.unitWidth + this.gutter) * 2
31710         });
31711         
31712         positions.push({
31713             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31714             y : minY
31715         });
31716         
31717         Roo.each(eItems, function(b,k){
31718             
31719             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31720
31721         }, this);
31722         
31723     },
31724     
31725     getVerticalOneBoxColPositions : function(x, y, box)
31726     {
31727         var pos = [];
31728         
31729         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31730         
31731         if(box[0].size == 'md-left'){
31732             rand = 0;
31733         }
31734         
31735         if(box[0].size == 'md-right'){
31736             rand = 1;
31737         }
31738         
31739         pos.push({
31740             x : x + (this.unitWidth + this.gutter) * rand,
31741             y : y
31742         });
31743         
31744         return pos;
31745     },
31746     
31747     getVerticalTwoBoxColPositions : function(x, y, box)
31748     {
31749         var pos = [];
31750         
31751         if(box[0].size == 'xs'){
31752             
31753             pos.push({
31754                 x : x,
31755                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31756             });
31757
31758             pos.push({
31759                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31760                 y : y
31761             });
31762             
31763             return pos;
31764             
31765         }
31766         
31767         pos.push({
31768             x : x,
31769             y : y
31770         });
31771
31772         pos.push({
31773             x : x + (this.unitWidth + this.gutter) * 2,
31774             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31775         });
31776         
31777         return pos;
31778         
31779     },
31780     
31781     getVerticalThreeBoxColPositions : function(x, y, box)
31782     {
31783         var pos = [];
31784         
31785         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31786             
31787             pos.push({
31788                 x : x,
31789                 y : y
31790             });
31791
31792             pos.push({
31793                 x : x + (this.unitWidth + this.gutter) * 1,
31794                 y : y
31795             });
31796             
31797             pos.push({
31798                 x : x + (this.unitWidth + this.gutter) * 2,
31799                 y : y
31800             });
31801             
31802             return pos;
31803             
31804         }
31805         
31806         if(box[0].size == 'xs' && box[1].size == 'xs'){
31807             
31808             pos.push({
31809                 x : x,
31810                 y : y
31811             });
31812
31813             pos.push({
31814                 x : x,
31815                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31816             });
31817             
31818             pos.push({
31819                 x : x + (this.unitWidth + this.gutter) * 1,
31820                 y : y
31821             });
31822             
31823             return pos;
31824             
31825         }
31826         
31827         pos.push({
31828             x : x,
31829             y : y
31830         });
31831
31832         pos.push({
31833             x : x + (this.unitWidth + this.gutter) * 2,
31834             y : y
31835         });
31836
31837         pos.push({
31838             x : x + (this.unitWidth + this.gutter) * 2,
31839             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31840         });
31841             
31842         return pos;
31843         
31844     },
31845     
31846     getVerticalFourBoxColPositions : function(x, y, box)
31847     {
31848         var pos = [];
31849         
31850         if(box[0].size == 'xs'){
31851             
31852             pos.push({
31853                 x : x,
31854                 y : y
31855             });
31856
31857             pos.push({
31858                 x : x,
31859                 y : y + (this.unitHeight + this.gutter) * 1
31860             });
31861             
31862             pos.push({
31863                 x : x,
31864                 y : y + (this.unitHeight + this.gutter) * 2
31865             });
31866             
31867             pos.push({
31868                 x : x + (this.unitWidth + this.gutter) * 1,
31869                 y : y
31870             });
31871             
31872             return pos;
31873             
31874         }
31875         
31876         pos.push({
31877             x : x,
31878             y : y
31879         });
31880
31881         pos.push({
31882             x : x + (this.unitWidth + this.gutter) * 2,
31883             y : y
31884         });
31885
31886         pos.push({
31887             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31888             y : y + (this.unitHeight + this.gutter) * 1
31889         });
31890
31891         pos.push({
31892             x : x + (this.unitWidth + this.gutter) * 2,
31893             y : y + (this.unitWidth + this.gutter) * 2
31894         });
31895
31896         return pos;
31897         
31898     },
31899     
31900     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31901     {
31902         var pos = [];
31903         
31904         if(box[0].size == 'md-left'){
31905             pos.push({
31906                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31907                 y : minY
31908             });
31909             
31910             return pos;
31911         }
31912         
31913         if(box[0].size == 'md-right'){
31914             pos.push({
31915                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31916                 y : minY + (this.unitWidth + this.gutter) * 1
31917             });
31918             
31919             return pos;
31920         }
31921         
31922         var rand = Math.floor(Math.random() * (4 - box[0].y));
31923         
31924         pos.push({
31925             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31926             y : minY + (this.unitWidth + this.gutter) * rand
31927         });
31928         
31929         return pos;
31930         
31931     },
31932     
31933     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31934     {
31935         var pos = [];
31936         
31937         if(box[0].size == 'xs'){
31938             
31939             pos.push({
31940                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31941                 y : minY
31942             });
31943
31944             pos.push({
31945                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31946                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31947             });
31948             
31949             return pos;
31950             
31951         }
31952         
31953         pos.push({
31954             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31955             y : minY
31956         });
31957
31958         pos.push({
31959             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31960             y : minY + (this.unitWidth + this.gutter) * 2
31961         });
31962         
31963         return pos;
31964         
31965     },
31966     
31967     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31968     {
31969         var pos = [];
31970         
31971         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31972             
31973             pos.push({
31974                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31975                 y : minY
31976             });
31977
31978             pos.push({
31979                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31980                 y : minY + (this.unitWidth + this.gutter) * 1
31981             });
31982             
31983             pos.push({
31984                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31985                 y : minY + (this.unitWidth + this.gutter) * 2
31986             });
31987             
31988             return pos;
31989             
31990         }
31991         
31992         if(box[0].size == 'xs' && box[1].size == 'xs'){
31993             
31994             pos.push({
31995                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31996                 y : minY
31997             });
31998
31999             pos.push({
32000                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32001                 y : minY
32002             });
32003             
32004             pos.push({
32005                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32006                 y : minY + (this.unitWidth + this.gutter) * 1
32007             });
32008             
32009             return pos;
32010             
32011         }
32012         
32013         pos.push({
32014             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32015             y : minY
32016         });
32017
32018         pos.push({
32019             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32020             y : minY + (this.unitWidth + this.gutter) * 2
32021         });
32022
32023         pos.push({
32024             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32025             y : minY + (this.unitWidth + this.gutter) * 2
32026         });
32027             
32028         return pos;
32029         
32030     },
32031     
32032     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32033     {
32034         var pos = [];
32035         
32036         if(box[0].size == 'xs'){
32037             
32038             pos.push({
32039                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32040                 y : minY
32041             });
32042
32043             pos.push({
32044                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32045                 y : minY
32046             });
32047             
32048             pos.push({
32049                 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),
32050                 y : minY
32051             });
32052             
32053             pos.push({
32054                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32055                 y : minY + (this.unitWidth + this.gutter) * 1
32056             });
32057             
32058             return pos;
32059             
32060         }
32061         
32062         pos.push({
32063             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32064             y : minY
32065         });
32066         
32067         pos.push({
32068             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32069             y : minY + (this.unitWidth + this.gutter) * 2
32070         });
32071         
32072         pos.push({
32073             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32074             y : minY + (this.unitWidth + this.gutter) * 2
32075         });
32076         
32077         pos.push({
32078             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),
32079             y : minY + (this.unitWidth + this.gutter) * 2
32080         });
32081
32082         return pos;
32083         
32084     },
32085     
32086     /**
32087     * remove a Masonry Brick
32088     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32089     */
32090     removeBrick : function(brick_id)
32091     {
32092         if (!brick_id) {
32093             return;
32094         }
32095         
32096         for (var i = 0; i<this.bricks.length; i++) {
32097             if (this.bricks[i].id == brick_id) {
32098                 this.bricks.splice(i,1);
32099                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32100                 this.initial();
32101             }
32102         }
32103     },
32104     
32105     /**
32106     * adds a Masonry Brick
32107     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32108     */
32109     addBrick : function(cfg)
32110     {
32111         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32112         //this.register(cn);
32113         cn.parentId = this.id;
32114         cn.render(this.el);
32115         return cn;
32116     },
32117     
32118     /**
32119     * register a Masonry Brick
32120     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32121     */
32122     
32123     register : function(brick)
32124     {
32125         this.bricks.push(brick);
32126         brick.masonryId = this.id;
32127     },
32128     
32129     /**
32130     * clear all the Masonry Brick
32131     */
32132     clearAll : function()
32133     {
32134         this.bricks = [];
32135         //this.getChildContainer().dom.innerHTML = "";
32136         this.el.dom.innerHTML = '';
32137     },
32138     
32139     getSelected : function()
32140     {
32141         if (!this.selectedBrick) {
32142             return false;
32143         }
32144         
32145         return this.selectedBrick;
32146     }
32147 });
32148
32149 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32150     
32151     groups: {},
32152      /**
32153     * register a Masonry Layout
32154     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32155     */
32156     
32157     register : function(layout)
32158     {
32159         this.groups[layout.id] = layout;
32160     },
32161     /**
32162     * fetch a  Masonry Layout based on the masonry layout ID
32163     * @param {string} the masonry layout to add
32164     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32165     */
32166     
32167     get: function(layout_id) {
32168         if (typeof(this.groups[layout_id]) == 'undefined') {
32169             return false;
32170         }
32171         return this.groups[layout_id] ;
32172     }
32173     
32174     
32175     
32176 });
32177
32178  
32179
32180  /**
32181  *
32182  * This is based on 
32183  * http://masonry.desandro.com
32184  *
32185  * The idea is to render all the bricks based on vertical width...
32186  *
32187  * The original code extends 'outlayer' - we might need to use that....
32188  * 
32189  */
32190
32191
32192 /**
32193  * @class Roo.bootstrap.LayoutMasonryAuto
32194  * @extends Roo.bootstrap.Component
32195  * Bootstrap Layout Masonry class
32196  * 
32197  * @constructor
32198  * Create a new Element
32199  * @param {Object} config The config object
32200  */
32201
32202 Roo.bootstrap.LayoutMasonryAuto = function(config){
32203     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32204 };
32205
32206 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32207     
32208       /**
32209      * @cfg {Boolean} isFitWidth  - resize the width..
32210      */   
32211     isFitWidth : false,  // options..
32212     /**
32213      * @cfg {Boolean} isOriginLeft = left align?
32214      */   
32215     isOriginLeft : true,
32216     /**
32217      * @cfg {Boolean} isOriginTop = top align?
32218      */   
32219     isOriginTop : false,
32220     /**
32221      * @cfg {Boolean} isLayoutInstant = no animation?
32222      */   
32223     isLayoutInstant : false, // needed?
32224     /**
32225      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32226      */   
32227     isResizingContainer : true,
32228     /**
32229      * @cfg {Number} columnWidth  width of the columns 
32230      */   
32231     
32232     columnWidth : 0,
32233     
32234     /**
32235      * @cfg {Number} maxCols maximum number of columns
32236      */   
32237     
32238     maxCols: 0,
32239     /**
32240      * @cfg {Number} padHeight padding below box..
32241      */   
32242     
32243     padHeight : 10, 
32244     
32245     /**
32246      * @cfg {Boolean} isAutoInitial defalut true
32247      */   
32248     
32249     isAutoInitial : true, 
32250     
32251     // private?
32252     gutter : 0,
32253     
32254     containerWidth: 0,
32255     initialColumnWidth : 0,
32256     currentSize : null,
32257     
32258     colYs : null, // array.
32259     maxY : 0,
32260     padWidth: 10,
32261     
32262     
32263     tag: 'div',
32264     cls: '',
32265     bricks: null, //CompositeElement
32266     cols : 0, // array?
32267     // element : null, // wrapped now this.el
32268     _isLayoutInited : null, 
32269     
32270     
32271     getAutoCreate : function(){
32272         
32273         var cfg = {
32274             tag: this.tag,
32275             cls: 'blog-masonary-wrapper ' + this.cls,
32276             cn : {
32277                 cls : 'mas-boxes masonary'
32278             }
32279         };
32280         
32281         return cfg;
32282     },
32283     
32284     getChildContainer: function( )
32285     {
32286         if (this.boxesEl) {
32287             return this.boxesEl;
32288         }
32289         
32290         this.boxesEl = this.el.select('.mas-boxes').first();
32291         
32292         return this.boxesEl;
32293     },
32294     
32295     
32296     initEvents : function()
32297     {
32298         var _this = this;
32299         
32300         if(this.isAutoInitial){
32301             Roo.log('hook children rendered');
32302             this.on('childrenrendered', function() {
32303                 Roo.log('children rendered');
32304                 _this.initial();
32305             } ,this);
32306         }
32307         
32308     },
32309     
32310     initial : function()
32311     {
32312         this.reloadItems();
32313
32314         this.currentSize = this.el.getBox(true);
32315
32316         /// was window resize... - let's see if this works..
32317         Roo.EventManager.onWindowResize(this.resize, this); 
32318
32319         if(!this.isAutoInitial){
32320             this.layout();
32321             return;
32322         }
32323         
32324         this.layout.defer(500,this);
32325     },
32326     
32327     reloadItems: function()
32328     {
32329         this.bricks = this.el.select('.masonry-brick', true);
32330         
32331         this.bricks.each(function(b) {
32332             //Roo.log(b.getSize());
32333             if (!b.attr('originalwidth')) {
32334                 b.attr('originalwidth',  b.getSize().width);
32335             }
32336             
32337         });
32338         
32339         Roo.log(this.bricks.elements.length);
32340     },
32341     
32342     resize : function()
32343     {
32344         Roo.log('resize');
32345         var cs = this.el.getBox(true);
32346         
32347         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32348             Roo.log("no change in with or X");
32349             return;
32350         }
32351         this.currentSize = cs;
32352         this.layout();
32353     },
32354     
32355     layout : function()
32356     {
32357          Roo.log('layout');
32358         this._resetLayout();
32359         //this._manageStamps();
32360       
32361         // don't animate first layout
32362         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32363         this.layoutItems( isInstant );
32364       
32365         // flag for initalized
32366         this._isLayoutInited = true;
32367     },
32368     
32369     layoutItems : function( isInstant )
32370     {
32371         //var items = this._getItemsForLayout( this.items );
32372         // original code supports filtering layout items.. we just ignore it..
32373         
32374         this._layoutItems( this.bricks , isInstant );
32375       
32376         this._postLayout();
32377     },
32378     _layoutItems : function ( items , isInstant)
32379     {
32380        //this.fireEvent( 'layout', this, items );
32381     
32382
32383         if ( !items || !items.elements.length ) {
32384           // no items, emit event with empty array
32385             return;
32386         }
32387
32388         var queue = [];
32389         items.each(function(item) {
32390             Roo.log("layout item");
32391             Roo.log(item);
32392             // get x/y object from method
32393             var position = this._getItemLayoutPosition( item );
32394             // enqueue
32395             position.item = item;
32396             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32397             queue.push( position );
32398         }, this);
32399       
32400         this._processLayoutQueue( queue );
32401     },
32402     /** Sets position of item in DOM
32403     * @param {Element} item
32404     * @param {Number} x - horizontal position
32405     * @param {Number} y - vertical position
32406     * @param {Boolean} isInstant - disables transitions
32407     */
32408     _processLayoutQueue : function( queue )
32409     {
32410         for ( var i=0, len = queue.length; i < len; i++ ) {
32411             var obj = queue[i];
32412             obj.item.position('absolute');
32413             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32414         }
32415     },
32416       
32417     
32418     /**
32419     * Any logic you want to do after each layout,
32420     * i.e. size the container
32421     */
32422     _postLayout : function()
32423     {
32424         this.resizeContainer();
32425     },
32426     
32427     resizeContainer : function()
32428     {
32429         if ( !this.isResizingContainer ) {
32430             return;
32431         }
32432         var size = this._getContainerSize();
32433         if ( size ) {
32434             this.el.setSize(size.width,size.height);
32435             this.boxesEl.setSize(size.width,size.height);
32436         }
32437     },
32438     
32439     
32440     
32441     _resetLayout : function()
32442     {
32443         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32444         this.colWidth = this.el.getWidth();
32445         //this.gutter = this.el.getWidth(); 
32446         
32447         this.measureColumns();
32448
32449         // reset column Y
32450         var i = this.cols;
32451         this.colYs = [];
32452         while (i--) {
32453             this.colYs.push( 0 );
32454         }
32455     
32456         this.maxY = 0;
32457     },
32458
32459     measureColumns : function()
32460     {
32461         this.getContainerWidth();
32462       // if columnWidth is 0, default to outerWidth of first item
32463         if ( !this.columnWidth ) {
32464             var firstItem = this.bricks.first();
32465             Roo.log(firstItem);
32466             this.columnWidth  = this.containerWidth;
32467             if (firstItem && firstItem.attr('originalwidth') ) {
32468                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32469             }
32470             // columnWidth fall back to item of first element
32471             Roo.log("set column width?");
32472                         this.initialColumnWidth = this.columnWidth  ;
32473
32474             // if first elem has no width, default to size of container
32475             
32476         }
32477         
32478         
32479         if (this.initialColumnWidth) {
32480             this.columnWidth = this.initialColumnWidth;
32481         }
32482         
32483         
32484             
32485         // column width is fixed at the top - however if container width get's smaller we should
32486         // reduce it...
32487         
32488         // this bit calcs how man columns..
32489             
32490         var columnWidth = this.columnWidth += this.gutter;
32491       
32492         // calculate columns
32493         var containerWidth = this.containerWidth + this.gutter;
32494         
32495         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32496         // fix rounding errors, typically with gutters
32497         var excess = columnWidth - containerWidth % columnWidth;
32498         
32499         
32500         // if overshoot is less than a pixel, round up, otherwise floor it
32501         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32502         cols = Math[ mathMethod ]( cols );
32503         this.cols = Math.max( cols, 1 );
32504         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32505         
32506          // padding positioning..
32507         var totalColWidth = this.cols * this.columnWidth;
32508         var padavail = this.containerWidth - totalColWidth;
32509         // so for 2 columns - we need 3 'pads'
32510         
32511         var padNeeded = (1+this.cols) * this.padWidth;
32512         
32513         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32514         
32515         this.columnWidth += padExtra
32516         //this.padWidth = Math.floor(padavail /  ( this.cols));
32517         
32518         // adjust colum width so that padding is fixed??
32519         
32520         // we have 3 columns ... total = width * 3
32521         // we have X left over... that should be used by 
32522         
32523         //if (this.expandC) {
32524             
32525         //}
32526         
32527         
32528         
32529     },
32530     
32531     getContainerWidth : function()
32532     {
32533        /* // container is parent if fit width
32534         var container = this.isFitWidth ? this.element.parentNode : this.element;
32535         // check that this.size and size are there
32536         // IE8 triggers resize on body size change, so they might not be
32537         
32538         var size = getSize( container );  //FIXME
32539         this.containerWidth = size && size.innerWidth; //FIXME
32540         */
32541          
32542         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32543         
32544     },
32545     
32546     _getItemLayoutPosition : function( item )  // what is item?
32547     {
32548         // we resize the item to our columnWidth..
32549       
32550         item.setWidth(this.columnWidth);
32551         item.autoBoxAdjust  = false;
32552         
32553         var sz = item.getSize();
32554  
32555         // how many columns does this brick span
32556         var remainder = this.containerWidth % this.columnWidth;
32557         
32558         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32559         // round if off by 1 pixel, otherwise use ceil
32560         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32561         colSpan = Math.min( colSpan, this.cols );
32562         
32563         // normally this should be '1' as we dont' currently allow multi width columns..
32564         
32565         var colGroup = this._getColGroup( colSpan );
32566         // get the minimum Y value from the columns
32567         var minimumY = Math.min.apply( Math, colGroup );
32568         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32569         
32570         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32571          
32572         // position the brick
32573         var position = {
32574             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32575             y: this.currentSize.y + minimumY + this.padHeight
32576         };
32577         
32578         Roo.log(position);
32579         // apply setHeight to necessary columns
32580         var setHeight = minimumY + sz.height + this.padHeight;
32581         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32582         
32583         var setSpan = this.cols + 1 - colGroup.length;
32584         for ( var i = 0; i < setSpan; i++ ) {
32585           this.colYs[ shortColIndex + i ] = setHeight ;
32586         }
32587       
32588         return position;
32589     },
32590     
32591     /**
32592      * @param {Number} colSpan - number of columns the element spans
32593      * @returns {Array} colGroup
32594      */
32595     _getColGroup : function( colSpan )
32596     {
32597         if ( colSpan < 2 ) {
32598           // if brick spans only one column, use all the column Ys
32599           return this.colYs;
32600         }
32601       
32602         var colGroup = [];
32603         // how many different places could this brick fit horizontally
32604         var groupCount = this.cols + 1 - colSpan;
32605         // for each group potential horizontal position
32606         for ( var i = 0; i < groupCount; i++ ) {
32607           // make an array of colY values for that one group
32608           var groupColYs = this.colYs.slice( i, i + colSpan );
32609           // and get the max value of the array
32610           colGroup[i] = Math.max.apply( Math, groupColYs );
32611         }
32612         return colGroup;
32613     },
32614     /*
32615     _manageStamp : function( stamp )
32616     {
32617         var stampSize =  stamp.getSize();
32618         var offset = stamp.getBox();
32619         // get the columns that this stamp affects
32620         var firstX = this.isOriginLeft ? offset.x : offset.right;
32621         var lastX = firstX + stampSize.width;
32622         var firstCol = Math.floor( firstX / this.columnWidth );
32623         firstCol = Math.max( 0, firstCol );
32624         
32625         var lastCol = Math.floor( lastX / this.columnWidth );
32626         // lastCol should not go over if multiple of columnWidth #425
32627         lastCol -= lastX % this.columnWidth ? 0 : 1;
32628         lastCol = Math.min( this.cols - 1, lastCol );
32629         
32630         // set colYs to bottom of the stamp
32631         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32632             stampSize.height;
32633             
32634         for ( var i = firstCol; i <= lastCol; i++ ) {
32635           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32636         }
32637     },
32638     */
32639     
32640     _getContainerSize : function()
32641     {
32642         this.maxY = Math.max.apply( Math, this.colYs );
32643         var size = {
32644             height: this.maxY
32645         };
32646       
32647         if ( this.isFitWidth ) {
32648             size.width = this._getContainerFitWidth();
32649         }
32650       
32651         return size;
32652     },
32653     
32654     _getContainerFitWidth : function()
32655     {
32656         var unusedCols = 0;
32657         // count unused columns
32658         var i = this.cols;
32659         while ( --i ) {
32660           if ( this.colYs[i] !== 0 ) {
32661             break;
32662           }
32663           unusedCols++;
32664         }
32665         // fit container to columns that have been used
32666         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32667     },
32668     
32669     needsResizeLayout : function()
32670     {
32671         var previousWidth = this.containerWidth;
32672         this.getContainerWidth();
32673         return previousWidth !== this.containerWidth;
32674     }
32675  
32676 });
32677
32678  
32679
32680  /*
32681  * - LGPL
32682  *
32683  * element
32684  * 
32685  */
32686
32687 /**
32688  * @class Roo.bootstrap.MasonryBrick
32689  * @extends Roo.bootstrap.Component
32690  * Bootstrap MasonryBrick class
32691  * 
32692  * @constructor
32693  * Create a new MasonryBrick
32694  * @param {Object} config The config object
32695  */
32696
32697 Roo.bootstrap.MasonryBrick = function(config){
32698     
32699     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32700     
32701     Roo.bootstrap.MasonryBrick.register(this);
32702     
32703     this.addEvents({
32704         // raw events
32705         /**
32706          * @event click
32707          * When a MasonryBrick is clcik
32708          * @param {Roo.bootstrap.MasonryBrick} this
32709          * @param {Roo.EventObject} e
32710          */
32711         "click" : true
32712     });
32713 };
32714
32715 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32716     
32717     /**
32718      * @cfg {String} title
32719      */   
32720     title : '',
32721     /**
32722      * @cfg {String} html
32723      */   
32724     html : '',
32725     /**
32726      * @cfg {String} bgimage
32727      */   
32728     bgimage : '',
32729     /**
32730      * @cfg {String} videourl
32731      */   
32732     videourl : '',
32733     /**
32734      * @cfg {String} cls
32735      */   
32736     cls : '',
32737     /**
32738      * @cfg {String} href
32739      */   
32740     href : '',
32741     /**
32742      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32743      */   
32744     size : 'xs',
32745     
32746     /**
32747      * @cfg {String} placetitle (center|bottom)
32748      */   
32749     placetitle : '',
32750     
32751     /**
32752      * @cfg {Boolean} isFitContainer defalut true
32753      */   
32754     isFitContainer : true, 
32755     
32756     /**
32757      * @cfg {Boolean} preventDefault defalut false
32758      */   
32759     preventDefault : false, 
32760     
32761     /**
32762      * @cfg {Boolean} inverse defalut false
32763      */   
32764     maskInverse : false, 
32765     
32766     getAutoCreate : function()
32767     {
32768         if(!this.isFitContainer){
32769             return this.getSplitAutoCreate();
32770         }
32771         
32772         var cls = 'masonry-brick masonry-brick-full';
32773         
32774         if(this.href.length){
32775             cls += ' masonry-brick-link';
32776         }
32777         
32778         if(this.bgimage.length){
32779             cls += ' masonry-brick-image';
32780         }
32781         
32782         if(this.maskInverse){
32783             cls += ' mask-inverse';
32784         }
32785         
32786         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32787             cls += ' enable-mask';
32788         }
32789         
32790         if(this.size){
32791             cls += ' masonry-' + this.size + '-brick';
32792         }
32793         
32794         if(this.placetitle.length){
32795             
32796             switch (this.placetitle) {
32797                 case 'center' :
32798                     cls += ' masonry-center-title';
32799                     break;
32800                 case 'bottom' :
32801                     cls += ' masonry-bottom-title';
32802                     break;
32803                 default:
32804                     break;
32805             }
32806             
32807         } else {
32808             if(!this.html.length && !this.bgimage.length){
32809                 cls += ' masonry-center-title';
32810             }
32811
32812             if(!this.html.length && this.bgimage.length){
32813                 cls += ' masonry-bottom-title';
32814             }
32815         }
32816         
32817         if(this.cls){
32818             cls += ' ' + this.cls;
32819         }
32820         
32821         var cfg = {
32822             tag: (this.href.length) ? 'a' : 'div',
32823             cls: cls,
32824             cn: [
32825                 {
32826                     tag: 'div',
32827                     cls: 'masonry-brick-mask'
32828                 },
32829                 {
32830                     tag: 'div',
32831                     cls: 'masonry-brick-paragraph',
32832                     cn: []
32833                 }
32834             ]
32835         };
32836         
32837         if(this.href.length){
32838             cfg.href = this.href;
32839         }
32840         
32841         var cn = cfg.cn[1].cn;
32842         
32843         if(this.title.length){
32844             cn.push({
32845                 tag: 'h4',
32846                 cls: 'masonry-brick-title',
32847                 html: this.title
32848             });
32849         }
32850         
32851         if(this.html.length){
32852             cn.push({
32853                 tag: 'p',
32854                 cls: 'masonry-brick-text',
32855                 html: this.html
32856             });
32857         }
32858         
32859         if (!this.title.length && !this.html.length) {
32860             cfg.cn[1].cls += ' hide';
32861         }
32862         
32863         if(this.bgimage.length){
32864             cfg.cn.push({
32865                 tag: 'img',
32866                 cls: 'masonry-brick-image-view',
32867                 src: this.bgimage
32868             });
32869         }
32870         
32871         if(this.videourl.length){
32872             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32873             // youtube support only?
32874             cfg.cn.push({
32875                 tag: 'iframe',
32876                 cls: 'masonry-brick-image-view',
32877                 src: vurl,
32878                 frameborder : 0,
32879                 allowfullscreen : true
32880             });
32881         }
32882         
32883         return cfg;
32884         
32885     },
32886     
32887     getSplitAutoCreate : function()
32888     {
32889         var cls = 'masonry-brick masonry-brick-split';
32890         
32891         if(this.href.length){
32892             cls += ' masonry-brick-link';
32893         }
32894         
32895         if(this.bgimage.length){
32896             cls += ' masonry-brick-image';
32897         }
32898         
32899         if(this.size){
32900             cls += ' masonry-' + this.size + '-brick';
32901         }
32902         
32903         switch (this.placetitle) {
32904             case 'center' :
32905                 cls += ' masonry-center-title';
32906                 break;
32907             case 'bottom' :
32908                 cls += ' masonry-bottom-title';
32909                 break;
32910             default:
32911                 if(!this.bgimage.length){
32912                     cls += ' masonry-center-title';
32913                 }
32914
32915                 if(this.bgimage.length){
32916                     cls += ' masonry-bottom-title';
32917                 }
32918                 break;
32919         }
32920         
32921         if(this.cls){
32922             cls += ' ' + this.cls;
32923         }
32924         
32925         var cfg = {
32926             tag: (this.href.length) ? 'a' : 'div',
32927             cls: cls,
32928             cn: [
32929                 {
32930                     tag: 'div',
32931                     cls: 'masonry-brick-split-head',
32932                     cn: [
32933                         {
32934                             tag: 'div',
32935                             cls: 'masonry-brick-paragraph',
32936                             cn: []
32937                         }
32938                     ]
32939                 },
32940                 {
32941                     tag: 'div',
32942                     cls: 'masonry-brick-split-body',
32943                     cn: []
32944                 }
32945             ]
32946         };
32947         
32948         if(this.href.length){
32949             cfg.href = this.href;
32950         }
32951         
32952         if(this.title.length){
32953             cfg.cn[0].cn[0].cn.push({
32954                 tag: 'h4',
32955                 cls: 'masonry-brick-title',
32956                 html: this.title
32957             });
32958         }
32959         
32960         if(this.html.length){
32961             cfg.cn[1].cn.push({
32962                 tag: 'p',
32963                 cls: 'masonry-brick-text',
32964                 html: this.html
32965             });
32966         }
32967
32968         if(this.bgimage.length){
32969             cfg.cn[0].cn.push({
32970                 tag: 'img',
32971                 cls: 'masonry-brick-image-view',
32972                 src: this.bgimage
32973             });
32974         }
32975         
32976         if(this.videourl.length){
32977             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32978             // youtube support only?
32979             cfg.cn[0].cn.cn.push({
32980                 tag: 'iframe',
32981                 cls: 'masonry-brick-image-view',
32982                 src: vurl,
32983                 frameborder : 0,
32984                 allowfullscreen : true
32985             });
32986         }
32987         
32988         return cfg;
32989     },
32990     
32991     initEvents: function() 
32992     {
32993         switch (this.size) {
32994             case 'xs' :
32995                 this.x = 1;
32996                 this.y = 1;
32997                 break;
32998             case 'sm' :
32999                 this.x = 2;
33000                 this.y = 2;
33001                 break;
33002             case 'md' :
33003             case 'md-left' :
33004             case 'md-right' :
33005                 this.x = 3;
33006                 this.y = 3;
33007                 break;
33008             case 'tall' :
33009                 this.x = 2;
33010                 this.y = 3;
33011                 break;
33012             case 'wide' :
33013                 this.x = 3;
33014                 this.y = 2;
33015                 break;
33016             case 'wide-thin' :
33017                 this.x = 3;
33018                 this.y = 1;
33019                 break;
33020                         
33021             default :
33022                 break;
33023         }
33024         
33025         if(Roo.isTouch){
33026             this.el.on('touchstart', this.onTouchStart, this);
33027             this.el.on('touchmove', this.onTouchMove, this);
33028             this.el.on('touchend', this.onTouchEnd, this);
33029             this.el.on('contextmenu', this.onContextMenu, this);
33030         } else {
33031             this.el.on('mouseenter'  ,this.enter, this);
33032             this.el.on('mouseleave', this.leave, this);
33033             this.el.on('click', this.onClick, this);
33034         }
33035         
33036         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33037             this.parent().bricks.push(this);   
33038         }
33039         
33040     },
33041     
33042     onClick: function(e, el)
33043     {
33044         var time = this.endTimer - this.startTimer;
33045         // Roo.log(e.preventDefault());
33046         if(Roo.isTouch){
33047             if(time > 1000){
33048                 e.preventDefault();
33049                 return;
33050             }
33051         }
33052         
33053         if(!this.preventDefault){
33054             return;
33055         }
33056         
33057         e.preventDefault();
33058         
33059         if (this.activeClass != '') {
33060             this.selectBrick();
33061         }
33062         
33063         this.fireEvent('click', this, e);
33064     },
33065     
33066     enter: function(e, el)
33067     {
33068         e.preventDefault();
33069         
33070         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33071             return;
33072         }
33073         
33074         if(this.bgimage.length && this.html.length){
33075             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33076         }
33077     },
33078     
33079     leave: function(e, el)
33080     {
33081         e.preventDefault();
33082         
33083         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33084             return;
33085         }
33086         
33087         if(this.bgimage.length && this.html.length){
33088             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33089         }
33090     },
33091     
33092     onTouchStart: function(e, el)
33093     {
33094 //        e.preventDefault();
33095         
33096         this.touchmoved = false;
33097         
33098         if(!this.isFitContainer){
33099             return;
33100         }
33101         
33102         if(!this.bgimage.length || !this.html.length){
33103             return;
33104         }
33105         
33106         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33107         
33108         this.timer = new Date().getTime();
33109         
33110     },
33111     
33112     onTouchMove: function(e, el)
33113     {
33114         this.touchmoved = true;
33115     },
33116     
33117     onContextMenu : function(e,el)
33118     {
33119         e.preventDefault();
33120         e.stopPropagation();
33121         return false;
33122     },
33123     
33124     onTouchEnd: function(e, el)
33125     {
33126 //        e.preventDefault();
33127         
33128         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33129         
33130             this.leave(e,el);
33131             
33132             return;
33133         }
33134         
33135         if(!this.bgimage.length || !this.html.length){
33136             
33137             if(this.href.length){
33138                 window.location.href = this.href;
33139             }
33140             
33141             return;
33142         }
33143         
33144         if(!this.isFitContainer){
33145             return;
33146         }
33147         
33148         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33149         
33150         window.location.href = this.href;
33151     },
33152     
33153     //selection on single brick only
33154     selectBrick : function() {
33155         
33156         if (!this.parentId) {
33157             return;
33158         }
33159         
33160         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33161         var index = m.selectedBrick.indexOf(this.id);
33162         
33163         if ( index > -1) {
33164             m.selectedBrick.splice(index,1);
33165             this.el.removeClass(this.activeClass);
33166             return;
33167         }
33168         
33169         for(var i = 0; i < m.selectedBrick.length; i++) {
33170             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33171             b.el.removeClass(b.activeClass);
33172         }
33173         
33174         m.selectedBrick = [];
33175         
33176         m.selectedBrick.push(this.id);
33177         this.el.addClass(this.activeClass);
33178         return;
33179     },
33180     
33181     isSelected : function(){
33182         return this.el.hasClass(this.activeClass);
33183         
33184     }
33185 });
33186
33187 Roo.apply(Roo.bootstrap.MasonryBrick, {
33188     
33189     //groups: {},
33190     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33191      /**
33192     * register a Masonry Brick
33193     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33194     */
33195     
33196     register : function(brick)
33197     {
33198         //this.groups[brick.id] = brick;
33199         this.groups.add(brick.id, brick);
33200     },
33201     /**
33202     * fetch a  masonry brick based on the masonry brick ID
33203     * @param {string} the masonry brick to add
33204     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33205     */
33206     
33207     get: function(brick_id) 
33208     {
33209         // if (typeof(this.groups[brick_id]) == 'undefined') {
33210         //     return false;
33211         // }
33212         // return this.groups[brick_id] ;
33213         
33214         if(this.groups.key(brick_id)) {
33215             return this.groups.key(brick_id);
33216         }
33217         
33218         return false;
33219     }
33220     
33221     
33222     
33223 });
33224
33225  /*
33226  * - LGPL
33227  *
33228  * element
33229  * 
33230  */
33231
33232 /**
33233  * @class Roo.bootstrap.Brick
33234  * @extends Roo.bootstrap.Component
33235  * Bootstrap Brick class
33236  * 
33237  * @constructor
33238  * Create a new Brick
33239  * @param {Object} config The config object
33240  */
33241
33242 Roo.bootstrap.Brick = function(config){
33243     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33244     
33245     this.addEvents({
33246         // raw events
33247         /**
33248          * @event click
33249          * When a Brick is click
33250          * @param {Roo.bootstrap.Brick} this
33251          * @param {Roo.EventObject} e
33252          */
33253         "click" : true
33254     });
33255 };
33256
33257 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33258     
33259     /**
33260      * @cfg {String} title
33261      */   
33262     title : '',
33263     /**
33264      * @cfg {String} html
33265      */   
33266     html : '',
33267     /**
33268      * @cfg {String} bgimage
33269      */   
33270     bgimage : '',
33271     /**
33272      * @cfg {String} cls
33273      */   
33274     cls : '',
33275     /**
33276      * @cfg {String} href
33277      */   
33278     href : '',
33279     /**
33280      * @cfg {String} video
33281      */   
33282     video : '',
33283     /**
33284      * @cfg {Boolean} square
33285      */   
33286     square : true,
33287     
33288     getAutoCreate : function()
33289     {
33290         var cls = 'roo-brick';
33291         
33292         if(this.href.length){
33293             cls += ' roo-brick-link';
33294         }
33295         
33296         if(this.bgimage.length){
33297             cls += ' roo-brick-image';
33298         }
33299         
33300         if(!this.html.length && !this.bgimage.length){
33301             cls += ' roo-brick-center-title';
33302         }
33303         
33304         if(!this.html.length && this.bgimage.length){
33305             cls += ' roo-brick-bottom-title';
33306         }
33307         
33308         if(this.cls){
33309             cls += ' ' + this.cls;
33310         }
33311         
33312         var cfg = {
33313             tag: (this.href.length) ? 'a' : 'div',
33314             cls: cls,
33315             cn: [
33316                 {
33317                     tag: 'div',
33318                     cls: 'roo-brick-paragraph',
33319                     cn: []
33320                 }
33321             ]
33322         };
33323         
33324         if(this.href.length){
33325             cfg.href = this.href;
33326         }
33327         
33328         var cn = cfg.cn[0].cn;
33329         
33330         if(this.title.length){
33331             cn.push({
33332                 tag: 'h4',
33333                 cls: 'roo-brick-title',
33334                 html: this.title
33335             });
33336         }
33337         
33338         if(this.html.length){
33339             cn.push({
33340                 tag: 'p',
33341                 cls: 'roo-brick-text',
33342                 html: this.html
33343             });
33344         } else {
33345             cn.cls += ' hide';
33346         }
33347         
33348         if(this.bgimage.length){
33349             cfg.cn.push({
33350                 tag: 'img',
33351                 cls: 'roo-brick-image-view',
33352                 src: this.bgimage
33353             });
33354         }
33355         
33356         return cfg;
33357     },
33358     
33359     initEvents: function() 
33360     {
33361         if(this.title.length || this.html.length){
33362             this.el.on('mouseenter'  ,this.enter, this);
33363             this.el.on('mouseleave', this.leave, this);
33364         }
33365         
33366         Roo.EventManager.onWindowResize(this.resize, this); 
33367         
33368         if(this.bgimage.length){
33369             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33370             this.imageEl.on('load', this.onImageLoad, this);
33371             return;
33372         }
33373         
33374         this.resize();
33375     },
33376     
33377     onImageLoad : function()
33378     {
33379         this.resize();
33380     },
33381     
33382     resize : function()
33383     {
33384         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33385         
33386         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33387         
33388         if(this.bgimage.length){
33389             var image = this.el.select('.roo-brick-image-view', true).first();
33390             
33391             image.setWidth(paragraph.getWidth());
33392             
33393             if(this.square){
33394                 image.setHeight(paragraph.getWidth());
33395             }
33396             
33397             this.el.setHeight(image.getHeight());
33398             paragraph.setHeight(image.getHeight());
33399             
33400         }
33401         
33402     },
33403     
33404     enter: function(e, el)
33405     {
33406         e.preventDefault();
33407         
33408         if(this.bgimage.length){
33409             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33410             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33411         }
33412     },
33413     
33414     leave: function(e, el)
33415     {
33416         e.preventDefault();
33417         
33418         if(this.bgimage.length){
33419             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33420             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33421         }
33422     }
33423     
33424 });
33425
33426  
33427
33428  /*
33429  * - LGPL
33430  *
33431  * Number field 
33432  */
33433
33434 /**
33435  * @class Roo.bootstrap.NumberField
33436  * @extends Roo.bootstrap.Input
33437  * Bootstrap NumberField class
33438  * 
33439  * 
33440  * 
33441  * 
33442  * @constructor
33443  * Create a new NumberField
33444  * @param {Object} config The config object
33445  */
33446
33447 Roo.bootstrap.NumberField = function(config){
33448     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33449 };
33450
33451 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33452     
33453     /**
33454      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33455      */
33456     allowDecimals : true,
33457     /**
33458      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33459      */
33460     decimalSeparator : ".",
33461     /**
33462      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33463      */
33464     decimalPrecision : 2,
33465     /**
33466      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33467      */
33468     allowNegative : true,
33469     
33470     /**
33471      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33472      */
33473     allowZero: true,
33474     /**
33475      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33476      */
33477     minValue : Number.NEGATIVE_INFINITY,
33478     /**
33479      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33480      */
33481     maxValue : Number.MAX_VALUE,
33482     /**
33483      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33484      */
33485     minText : "The minimum value for this field is {0}",
33486     /**
33487      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33488      */
33489     maxText : "The maximum value for this field is {0}",
33490     /**
33491      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33492      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33493      */
33494     nanText : "{0} is not a valid number",
33495     /**
33496      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33497      */
33498     thousandsDelimiter : false,
33499     /**
33500      * @cfg {String} valueAlign alignment of value
33501      */
33502     valueAlign : "left",
33503
33504     getAutoCreate : function()
33505     {
33506         var hiddenInput = {
33507             tag: 'input',
33508             type: 'hidden',
33509             id: Roo.id(),
33510             cls: 'hidden-number-input'
33511         };
33512         
33513         if (this.name) {
33514             hiddenInput.name = this.name;
33515         }
33516         
33517         this.name = '';
33518         
33519         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33520         
33521         this.name = hiddenInput.name;
33522         
33523         if(cfg.cn.length > 0) {
33524             cfg.cn.push(hiddenInput);
33525         }
33526         
33527         return cfg;
33528     },
33529
33530     // private
33531     initEvents : function()
33532     {   
33533         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33534         
33535         var allowed = "0123456789";
33536         
33537         if(this.allowDecimals){
33538             allowed += this.decimalSeparator;
33539         }
33540         
33541         if(this.allowNegative){
33542             allowed += "-";
33543         }
33544         
33545         if(this.thousandsDelimiter) {
33546             allowed += ",";
33547         }
33548         
33549         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33550         
33551         var keyPress = function(e){
33552             
33553             var k = e.getKey();
33554             
33555             var c = e.getCharCode();
33556             
33557             if(
33558                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33559                     allowed.indexOf(String.fromCharCode(c)) === -1
33560             ){
33561                 e.stopEvent();
33562                 return;
33563             }
33564             
33565             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33566                 return;
33567             }
33568             
33569             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33570                 e.stopEvent();
33571             }
33572         };
33573         
33574         this.el.on("keypress", keyPress, this);
33575     },
33576     
33577     validateValue : function(value)
33578     {
33579         
33580         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33581             return false;
33582         }
33583         
33584         var num = this.parseValue(value);
33585         
33586         if(isNaN(num)){
33587             this.markInvalid(String.format(this.nanText, value));
33588             return false;
33589         }
33590         
33591         if(num < this.minValue){
33592             this.markInvalid(String.format(this.minText, this.minValue));
33593             return false;
33594         }
33595         
33596         if(num > this.maxValue){
33597             this.markInvalid(String.format(this.maxText, this.maxValue));
33598             return false;
33599         }
33600         
33601         return true;
33602     },
33603
33604     getValue : function()
33605     {
33606         var v = this.hiddenEl().getValue();
33607         
33608         return this.fixPrecision(this.parseValue(v));
33609     },
33610
33611     parseValue : function(value)
33612     {
33613         if(this.thousandsDelimiter) {
33614             value += "";
33615             r = new RegExp(",", "g");
33616             value = value.replace(r, "");
33617         }
33618         
33619         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33620         return isNaN(value) ? '' : value;
33621     },
33622
33623     fixPrecision : function(value)
33624     {
33625         if(this.thousandsDelimiter) {
33626             value += "";
33627             r = new RegExp(",", "g");
33628             value = value.replace(r, "");
33629         }
33630         
33631         var nan = isNaN(value);
33632         
33633         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33634             return nan ? '' : value;
33635         }
33636         return parseFloat(value).toFixed(this.decimalPrecision);
33637     },
33638
33639     setValue : function(v)
33640     {
33641         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33642         
33643         this.value = v;
33644         
33645         if(this.rendered){
33646             
33647             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33648             
33649             this.inputEl().dom.value = (v == '') ? '' :
33650                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33651             
33652             if(!this.allowZero && v === '0') {
33653                 this.hiddenEl().dom.value = '';
33654                 this.inputEl().dom.value = '';
33655             }
33656             
33657             this.validate();
33658         }
33659     },
33660
33661     decimalPrecisionFcn : function(v)
33662     {
33663         return Math.floor(v);
33664     },
33665
33666     beforeBlur : function()
33667     {
33668         var v = this.parseValue(this.getRawValue());
33669         
33670         if(v || v === 0 || v === ''){
33671             this.setValue(v);
33672         }
33673     },
33674     
33675     hiddenEl : function()
33676     {
33677         return this.el.select('input.hidden-number-input',true).first();
33678     }
33679     
33680 });
33681
33682  
33683
33684 /*
33685 * Licence: LGPL
33686 */
33687
33688 /**
33689  * @class Roo.bootstrap.DocumentSlider
33690  * @extends Roo.bootstrap.Component
33691  * Bootstrap DocumentSlider class
33692  * 
33693  * @constructor
33694  * Create a new DocumentViewer
33695  * @param {Object} config The config object
33696  */
33697
33698 Roo.bootstrap.DocumentSlider = function(config){
33699     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33700     
33701     this.files = [];
33702     
33703     this.addEvents({
33704         /**
33705          * @event initial
33706          * Fire after initEvent
33707          * @param {Roo.bootstrap.DocumentSlider} this
33708          */
33709         "initial" : true,
33710         /**
33711          * @event update
33712          * Fire after update
33713          * @param {Roo.bootstrap.DocumentSlider} this
33714          */
33715         "update" : true,
33716         /**
33717          * @event click
33718          * Fire after click
33719          * @param {Roo.bootstrap.DocumentSlider} this
33720          */
33721         "click" : true
33722     });
33723 };
33724
33725 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33726     
33727     files : false,
33728     
33729     indicator : 0,
33730     
33731     getAutoCreate : function()
33732     {
33733         var cfg = {
33734             tag : 'div',
33735             cls : 'roo-document-slider',
33736             cn : [
33737                 {
33738                     tag : 'div',
33739                     cls : 'roo-document-slider-header',
33740                     cn : [
33741                         {
33742                             tag : 'div',
33743                             cls : 'roo-document-slider-header-title'
33744                         }
33745                     ]
33746                 },
33747                 {
33748                     tag : 'div',
33749                     cls : 'roo-document-slider-body',
33750                     cn : [
33751                         {
33752                             tag : 'div',
33753                             cls : 'roo-document-slider-prev',
33754                             cn : [
33755                                 {
33756                                     tag : 'i',
33757                                     cls : 'fa fa-chevron-left'
33758                                 }
33759                             ]
33760                         },
33761                         {
33762                             tag : 'div',
33763                             cls : 'roo-document-slider-thumb',
33764                             cn : [
33765                                 {
33766                                     tag : 'img',
33767                                     cls : 'roo-document-slider-image'
33768                                 }
33769                             ]
33770                         },
33771                         {
33772                             tag : 'div',
33773                             cls : 'roo-document-slider-next',
33774                             cn : [
33775                                 {
33776                                     tag : 'i',
33777                                     cls : 'fa fa-chevron-right'
33778                                 }
33779                             ]
33780                         }
33781                     ]
33782                 }
33783             ]
33784         };
33785         
33786         return cfg;
33787     },
33788     
33789     initEvents : function()
33790     {
33791         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33792         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33793         
33794         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33795         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33796         
33797         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33798         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33799         
33800         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33801         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33802         
33803         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33804         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33805         
33806         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33807         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33808         
33809         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33810         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33811         
33812         this.thumbEl.on('click', this.onClick, this);
33813         
33814         this.prevIndicator.on('click', this.prev, this);
33815         
33816         this.nextIndicator.on('click', this.next, this);
33817         
33818     },
33819     
33820     initial : function()
33821     {
33822         if(this.files.length){
33823             this.indicator = 1;
33824             this.update()
33825         }
33826         
33827         this.fireEvent('initial', this);
33828     },
33829     
33830     update : function()
33831     {
33832         this.imageEl.attr('src', this.files[this.indicator - 1]);
33833         
33834         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33835         
33836         this.prevIndicator.show();
33837         
33838         if(this.indicator == 1){
33839             this.prevIndicator.hide();
33840         }
33841         
33842         this.nextIndicator.show();
33843         
33844         if(this.indicator == this.files.length){
33845             this.nextIndicator.hide();
33846         }
33847         
33848         this.thumbEl.scrollTo('top');
33849         
33850         this.fireEvent('update', this);
33851     },
33852     
33853     onClick : function(e)
33854     {
33855         e.preventDefault();
33856         
33857         this.fireEvent('click', this);
33858     },
33859     
33860     prev : function(e)
33861     {
33862         e.preventDefault();
33863         
33864         this.indicator = Math.max(1, this.indicator - 1);
33865         
33866         this.update();
33867     },
33868     
33869     next : function(e)
33870     {
33871         e.preventDefault();
33872         
33873         this.indicator = Math.min(this.files.length, this.indicator + 1);
33874         
33875         this.update();
33876     }
33877 });
33878 /*
33879  * - LGPL
33880  *
33881  * RadioSet
33882  *
33883  *
33884  */
33885
33886 /**
33887  * @class Roo.bootstrap.RadioSet
33888  * @extends Roo.bootstrap.Input
33889  * Bootstrap RadioSet class
33890  * @cfg {String} indicatorpos (left|right) default left
33891  * @cfg {Boolean} inline (true|false) inline the element (default true)
33892  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33893  * @constructor
33894  * Create a new RadioSet
33895  * @param {Object} config The config object
33896  */
33897
33898 Roo.bootstrap.RadioSet = function(config){
33899     
33900     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33901     
33902     this.radioes = [];
33903     
33904     Roo.bootstrap.RadioSet.register(this);
33905     
33906     this.addEvents({
33907         /**
33908         * @event check
33909         * Fires when the element is checked or unchecked.
33910         * @param {Roo.bootstrap.RadioSet} this This radio
33911         * @param {Roo.bootstrap.Radio} item The checked item
33912         */
33913        check : true,
33914        /**
33915         * @event click
33916         * Fires when the element is click.
33917         * @param {Roo.bootstrap.RadioSet} this This radio set
33918         * @param {Roo.bootstrap.Radio} item The checked item
33919         * @param {Roo.EventObject} e The event object
33920         */
33921        click : true
33922     });
33923     
33924 };
33925
33926 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33927
33928     radioes : false,
33929     
33930     inline : true,
33931     
33932     weight : '',
33933     
33934     indicatorpos : 'left',
33935     
33936     getAutoCreate : function()
33937     {
33938         var label = {
33939             tag : 'label',
33940             cls : 'roo-radio-set-label',
33941             cn : [
33942                 {
33943                     tag : 'span',
33944                     html : this.fieldLabel
33945                 }
33946             ]
33947         };
33948         
33949         if(this.indicatorpos == 'left'){
33950             label.cn.unshift({
33951                 tag : 'i',
33952                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33953                 tooltip : 'This field is required'
33954             });
33955         } else {
33956             label.cn.push({
33957                 tag : 'i',
33958                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33959                 tooltip : 'This field is required'
33960             });
33961         }
33962         
33963         var items = {
33964             tag : 'div',
33965             cls : 'roo-radio-set-items'
33966         };
33967         
33968         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33969         
33970         if (align === 'left' && this.fieldLabel.length) {
33971             
33972             items = {
33973                 cls : "roo-radio-set-right", 
33974                 cn: [
33975                     items
33976                 ]
33977             };
33978             
33979             if(this.labelWidth > 12){
33980                 label.style = "width: " + this.labelWidth + 'px';
33981             }
33982             
33983             if(this.labelWidth < 13 && this.labelmd == 0){
33984                 this.labelmd = this.labelWidth;
33985             }
33986             
33987             if(this.labellg > 0){
33988                 label.cls += ' col-lg-' + this.labellg;
33989                 items.cls += ' col-lg-' + (12 - this.labellg);
33990             }
33991             
33992             if(this.labelmd > 0){
33993                 label.cls += ' col-md-' + this.labelmd;
33994                 items.cls += ' col-md-' + (12 - this.labelmd);
33995             }
33996             
33997             if(this.labelsm > 0){
33998                 label.cls += ' col-sm-' + this.labelsm;
33999                 items.cls += ' col-sm-' + (12 - this.labelsm);
34000             }
34001             
34002             if(this.labelxs > 0){
34003                 label.cls += ' col-xs-' + this.labelxs;
34004                 items.cls += ' col-xs-' + (12 - this.labelxs);
34005             }
34006         }
34007         
34008         var cfg = {
34009             tag : 'div',
34010             cls : 'roo-radio-set',
34011             cn : [
34012                 {
34013                     tag : 'input',
34014                     cls : 'roo-radio-set-input',
34015                     type : 'hidden',
34016                     name : this.name,
34017                     value : this.value ? this.value :  ''
34018                 },
34019                 label,
34020                 items
34021             ]
34022         };
34023         
34024         if(this.weight.length){
34025             cfg.cls += ' roo-radio-' + this.weight;
34026         }
34027         
34028         if(this.inline) {
34029             cfg.cls += ' roo-radio-set-inline';
34030         }
34031         
34032         var settings=this;
34033         ['xs','sm','md','lg'].map(function(size){
34034             if (settings[size]) {
34035                 cfg.cls += ' col-' + size + '-' + settings[size];
34036             }
34037         });
34038         
34039         return cfg;
34040         
34041     },
34042
34043     initEvents : function()
34044     {
34045         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34046         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34047         
34048         if(!this.fieldLabel.length){
34049             this.labelEl.hide();
34050         }
34051         
34052         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34053         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34054         
34055         this.indicator = this.indicatorEl();
34056         
34057         if(this.indicator){
34058             this.indicator.addClass('invisible');
34059         }
34060         
34061         this.originalValue = this.getValue();
34062         
34063     },
34064     
34065     inputEl: function ()
34066     {
34067         return this.el.select('.roo-radio-set-input', true).first();
34068     },
34069     
34070     getChildContainer : function()
34071     {
34072         return this.itemsEl;
34073     },
34074     
34075     register : function(item)
34076     {
34077         this.radioes.push(item);
34078         
34079     },
34080     
34081     validate : function()
34082     {   
34083         if(this.getVisibilityEl().hasClass('hidden')){
34084             return true;
34085         }
34086         
34087         var valid = false;
34088         
34089         Roo.each(this.radioes, function(i){
34090             if(!i.checked){
34091                 return;
34092             }
34093             
34094             valid = true;
34095             return false;
34096         });
34097         
34098         if(this.allowBlank) {
34099             return true;
34100         }
34101         
34102         if(this.disabled || valid){
34103             this.markValid();
34104             return true;
34105         }
34106         
34107         this.markInvalid();
34108         return false;
34109         
34110     },
34111     
34112     markValid : function()
34113     {
34114         if(this.labelEl.isVisible(true)){
34115             this.indicatorEl().removeClass('visible');
34116             this.indicatorEl().addClass('invisible');
34117         }
34118         
34119         this.el.removeClass([this.invalidClass, this.validClass]);
34120         this.el.addClass(this.validClass);
34121         
34122         this.fireEvent('valid', this);
34123     },
34124     
34125     markInvalid : function(msg)
34126     {
34127         if(this.allowBlank || this.disabled){
34128             return;
34129         }
34130         
34131         if(this.labelEl.isVisible(true)){
34132             this.indicatorEl().removeClass('invisible');
34133             this.indicatorEl().addClass('visible');
34134         }
34135         
34136         this.el.removeClass([this.invalidClass, this.validClass]);
34137         this.el.addClass(this.invalidClass);
34138         
34139         this.fireEvent('invalid', this, msg);
34140         
34141     },
34142     
34143     setValue : function(v, suppressEvent)
34144     {   
34145         if(this.value === v){
34146             return;
34147         }
34148         
34149         this.value = v;
34150         
34151         if(this.rendered){
34152             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34153         }
34154         
34155         Roo.each(this.radioes, function(i){
34156             i.checked = false;
34157             i.el.removeClass('checked');
34158         });
34159         
34160         Roo.each(this.radioes, function(i){
34161             
34162             if(i.value === v || i.value.toString() === v.toString()){
34163                 i.checked = true;
34164                 i.el.addClass('checked');
34165                 
34166                 if(suppressEvent !== true){
34167                     this.fireEvent('check', this, i);
34168                 }
34169                 
34170                 return false;
34171             }
34172             
34173         }, this);
34174         
34175         this.validate();
34176     },
34177     
34178     clearInvalid : function(){
34179         
34180         if(!this.el || this.preventMark){
34181             return;
34182         }
34183         
34184         this.el.removeClass([this.invalidClass]);
34185         
34186         this.fireEvent('valid', this);
34187     }
34188     
34189 });
34190
34191 Roo.apply(Roo.bootstrap.RadioSet, {
34192     
34193     groups: {},
34194     
34195     register : function(set)
34196     {
34197         this.groups[set.name] = set;
34198     },
34199     
34200     get: function(name) 
34201     {
34202         if (typeof(this.groups[name]) == 'undefined') {
34203             return false;
34204         }
34205         
34206         return this.groups[name] ;
34207     }
34208     
34209 });
34210 /*
34211  * Based on:
34212  * Ext JS Library 1.1.1
34213  * Copyright(c) 2006-2007, Ext JS, LLC.
34214  *
34215  * Originally Released Under LGPL - original licence link has changed is not relivant.
34216  *
34217  * Fork - LGPL
34218  * <script type="text/javascript">
34219  */
34220
34221
34222 /**
34223  * @class Roo.bootstrap.SplitBar
34224  * @extends Roo.util.Observable
34225  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34226  * <br><br>
34227  * Usage:
34228  * <pre><code>
34229 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34230                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34231 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34232 split.minSize = 100;
34233 split.maxSize = 600;
34234 split.animate = true;
34235 split.on('moved', splitterMoved);
34236 </code></pre>
34237  * @constructor
34238  * Create a new SplitBar
34239  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34240  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34241  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34242  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34243                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34244                         position of the SplitBar).
34245  */
34246 Roo.bootstrap.SplitBar = function(cfg){
34247     
34248     /** @private */
34249     
34250     //{
34251     //  dragElement : elm
34252     //  resizingElement: el,
34253         // optional..
34254     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34255     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34256         // existingProxy ???
34257     //}
34258     
34259     this.el = Roo.get(cfg.dragElement, true);
34260     this.el.dom.unselectable = "on";
34261     /** @private */
34262     this.resizingEl = Roo.get(cfg.resizingElement, true);
34263
34264     /**
34265      * @private
34266      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34267      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34268      * @type Number
34269      */
34270     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34271     
34272     /**
34273      * The minimum size of the resizing element. (Defaults to 0)
34274      * @type Number
34275      */
34276     this.minSize = 0;
34277     
34278     /**
34279      * The maximum size of the resizing element. (Defaults to 2000)
34280      * @type Number
34281      */
34282     this.maxSize = 2000;
34283     
34284     /**
34285      * Whether to animate the transition to the new size
34286      * @type Boolean
34287      */
34288     this.animate = false;
34289     
34290     /**
34291      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34292      * @type Boolean
34293      */
34294     this.useShim = false;
34295     
34296     /** @private */
34297     this.shim = null;
34298     
34299     if(!cfg.existingProxy){
34300         /** @private */
34301         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34302     }else{
34303         this.proxy = Roo.get(cfg.existingProxy).dom;
34304     }
34305     /** @private */
34306     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34307     
34308     /** @private */
34309     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34310     
34311     /** @private */
34312     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34313     
34314     /** @private */
34315     this.dragSpecs = {};
34316     
34317     /**
34318      * @private The adapter to use to positon and resize elements
34319      */
34320     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34321     this.adapter.init(this);
34322     
34323     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34324         /** @private */
34325         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34326         this.el.addClass("roo-splitbar-h");
34327     }else{
34328         /** @private */
34329         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34330         this.el.addClass("roo-splitbar-v");
34331     }
34332     
34333     this.addEvents({
34334         /**
34335          * @event resize
34336          * Fires when the splitter is moved (alias for {@link #event-moved})
34337          * @param {Roo.bootstrap.SplitBar} this
34338          * @param {Number} newSize the new width or height
34339          */
34340         "resize" : true,
34341         /**
34342          * @event moved
34343          * Fires when the splitter is moved
34344          * @param {Roo.bootstrap.SplitBar} this
34345          * @param {Number} newSize the new width or height
34346          */
34347         "moved" : true,
34348         /**
34349          * @event beforeresize
34350          * Fires before the splitter is dragged
34351          * @param {Roo.bootstrap.SplitBar} this
34352          */
34353         "beforeresize" : true,
34354
34355         "beforeapply" : true
34356     });
34357
34358     Roo.util.Observable.call(this);
34359 };
34360
34361 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34362     onStartProxyDrag : function(x, y){
34363         this.fireEvent("beforeresize", this);
34364         if(!this.overlay){
34365             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34366             o.unselectable();
34367             o.enableDisplayMode("block");
34368             // all splitbars share the same overlay
34369             Roo.bootstrap.SplitBar.prototype.overlay = o;
34370         }
34371         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34372         this.overlay.show();
34373         Roo.get(this.proxy).setDisplayed("block");
34374         var size = this.adapter.getElementSize(this);
34375         this.activeMinSize = this.getMinimumSize();;
34376         this.activeMaxSize = this.getMaximumSize();;
34377         var c1 = size - this.activeMinSize;
34378         var c2 = Math.max(this.activeMaxSize - size, 0);
34379         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34380             this.dd.resetConstraints();
34381             this.dd.setXConstraint(
34382                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34383                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34384             );
34385             this.dd.setYConstraint(0, 0);
34386         }else{
34387             this.dd.resetConstraints();
34388             this.dd.setXConstraint(0, 0);
34389             this.dd.setYConstraint(
34390                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34391                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34392             );
34393          }
34394         this.dragSpecs.startSize = size;
34395         this.dragSpecs.startPoint = [x, y];
34396         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34397     },
34398     
34399     /** 
34400      * @private Called after the drag operation by the DDProxy
34401      */
34402     onEndProxyDrag : function(e){
34403         Roo.get(this.proxy).setDisplayed(false);
34404         var endPoint = Roo.lib.Event.getXY(e);
34405         if(this.overlay){
34406             this.overlay.hide();
34407         }
34408         var newSize;
34409         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34410             newSize = this.dragSpecs.startSize + 
34411                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34412                     endPoint[0] - this.dragSpecs.startPoint[0] :
34413                     this.dragSpecs.startPoint[0] - endPoint[0]
34414                 );
34415         }else{
34416             newSize = this.dragSpecs.startSize + 
34417                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34418                     endPoint[1] - this.dragSpecs.startPoint[1] :
34419                     this.dragSpecs.startPoint[1] - endPoint[1]
34420                 );
34421         }
34422         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34423         if(newSize != this.dragSpecs.startSize){
34424             if(this.fireEvent('beforeapply', this, newSize) !== false){
34425                 this.adapter.setElementSize(this, newSize);
34426                 this.fireEvent("moved", this, newSize);
34427                 this.fireEvent("resize", this, newSize);
34428             }
34429         }
34430     },
34431     
34432     /**
34433      * Get the adapter this SplitBar uses
34434      * @return The adapter object
34435      */
34436     getAdapter : function(){
34437         return this.adapter;
34438     },
34439     
34440     /**
34441      * Set the adapter this SplitBar uses
34442      * @param {Object} adapter A SplitBar adapter object
34443      */
34444     setAdapter : function(adapter){
34445         this.adapter = adapter;
34446         this.adapter.init(this);
34447     },
34448     
34449     /**
34450      * Gets the minimum size for the resizing element
34451      * @return {Number} The minimum size
34452      */
34453     getMinimumSize : function(){
34454         return this.minSize;
34455     },
34456     
34457     /**
34458      * Sets the minimum size for the resizing element
34459      * @param {Number} minSize The minimum size
34460      */
34461     setMinimumSize : function(minSize){
34462         this.minSize = minSize;
34463     },
34464     
34465     /**
34466      * Gets the maximum size for the resizing element
34467      * @return {Number} The maximum size
34468      */
34469     getMaximumSize : function(){
34470         return this.maxSize;
34471     },
34472     
34473     /**
34474      * Sets the maximum size for the resizing element
34475      * @param {Number} maxSize The maximum size
34476      */
34477     setMaximumSize : function(maxSize){
34478         this.maxSize = maxSize;
34479     },
34480     
34481     /**
34482      * Sets the initialize size for the resizing element
34483      * @param {Number} size The initial size
34484      */
34485     setCurrentSize : function(size){
34486         var oldAnimate = this.animate;
34487         this.animate = false;
34488         this.adapter.setElementSize(this, size);
34489         this.animate = oldAnimate;
34490     },
34491     
34492     /**
34493      * Destroy this splitbar. 
34494      * @param {Boolean} removeEl True to remove the element
34495      */
34496     destroy : function(removeEl){
34497         if(this.shim){
34498             this.shim.remove();
34499         }
34500         this.dd.unreg();
34501         this.proxy.parentNode.removeChild(this.proxy);
34502         if(removeEl){
34503             this.el.remove();
34504         }
34505     }
34506 });
34507
34508 /**
34509  * @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.
34510  */
34511 Roo.bootstrap.SplitBar.createProxy = function(dir){
34512     var proxy = new Roo.Element(document.createElement("div"));
34513     proxy.unselectable();
34514     var cls = 'roo-splitbar-proxy';
34515     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34516     document.body.appendChild(proxy.dom);
34517     return proxy.dom;
34518 };
34519
34520 /** 
34521  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34522  * Default Adapter. It assumes the splitter and resizing element are not positioned
34523  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34524  */
34525 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34526 };
34527
34528 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34529     // do nothing for now
34530     init : function(s){
34531     
34532     },
34533     /**
34534      * Called before drag operations to get the current size of the resizing element. 
34535      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34536      */
34537      getElementSize : function(s){
34538         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34539             return s.resizingEl.getWidth();
34540         }else{
34541             return s.resizingEl.getHeight();
34542         }
34543     },
34544     
34545     /**
34546      * Called after drag operations to set the size of the resizing element.
34547      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34548      * @param {Number} newSize The new size to set
34549      * @param {Function} onComplete A function to be invoked when resizing is complete
34550      */
34551     setElementSize : function(s, newSize, onComplete){
34552         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34553             if(!s.animate){
34554                 s.resizingEl.setWidth(newSize);
34555                 if(onComplete){
34556                     onComplete(s, newSize);
34557                 }
34558             }else{
34559                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34560             }
34561         }else{
34562             
34563             if(!s.animate){
34564                 s.resizingEl.setHeight(newSize);
34565                 if(onComplete){
34566                     onComplete(s, newSize);
34567                 }
34568             }else{
34569                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34570             }
34571         }
34572     }
34573 };
34574
34575 /** 
34576  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34577  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34578  * Adapter that  moves the splitter element to align with the resized sizing element. 
34579  * Used with an absolute positioned SplitBar.
34580  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34581  * document.body, make sure you assign an id to the body element.
34582  */
34583 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34584     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34585     this.container = Roo.get(container);
34586 };
34587
34588 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34589     init : function(s){
34590         this.basic.init(s);
34591     },
34592     
34593     getElementSize : function(s){
34594         return this.basic.getElementSize(s);
34595     },
34596     
34597     setElementSize : function(s, newSize, onComplete){
34598         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34599     },
34600     
34601     moveSplitter : function(s){
34602         var yes = Roo.bootstrap.SplitBar;
34603         switch(s.placement){
34604             case yes.LEFT:
34605                 s.el.setX(s.resizingEl.getRight());
34606                 break;
34607             case yes.RIGHT:
34608                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34609                 break;
34610             case yes.TOP:
34611                 s.el.setY(s.resizingEl.getBottom());
34612                 break;
34613             case yes.BOTTOM:
34614                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34615                 break;
34616         }
34617     }
34618 };
34619
34620 /**
34621  * Orientation constant - Create a vertical SplitBar
34622  * @static
34623  * @type Number
34624  */
34625 Roo.bootstrap.SplitBar.VERTICAL = 1;
34626
34627 /**
34628  * Orientation constant - Create a horizontal SplitBar
34629  * @static
34630  * @type Number
34631  */
34632 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34633
34634 /**
34635  * Placement constant - The resizing element is to the left of the splitter element
34636  * @static
34637  * @type Number
34638  */
34639 Roo.bootstrap.SplitBar.LEFT = 1;
34640
34641 /**
34642  * Placement constant - The resizing element is to the right of the splitter element
34643  * @static
34644  * @type Number
34645  */
34646 Roo.bootstrap.SplitBar.RIGHT = 2;
34647
34648 /**
34649  * Placement constant - The resizing element is positioned above the splitter element
34650  * @static
34651  * @type Number
34652  */
34653 Roo.bootstrap.SplitBar.TOP = 3;
34654
34655 /**
34656  * Placement constant - The resizing element is positioned under splitter element
34657  * @static
34658  * @type Number
34659  */
34660 Roo.bootstrap.SplitBar.BOTTOM = 4;
34661 Roo.namespace("Roo.bootstrap.layout");/*
34662  * Based on:
34663  * Ext JS Library 1.1.1
34664  * Copyright(c) 2006-2007, Ext JS, LLC.
34665  *
34666  * Originally Released Under LGPL - original licence link has changed is not relivant.
34667  *
34668  * Fork - LGPL
34669  * <script type="text/javascript">
34670  */
34671
34672 /**
34673  * @class Roo.bootstrap.layout.Manager
34674  * @extends Roo.bootstrap.Component
34675  * Base class for layout managers.
34676  */
34677 Roo.bootstrap.layout.Manager = function(config)
34678 {
34679     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34680
34681
34682
34683
34684
34685     /** false to disable window resize monitoring @type Boolean */
34686     this.monitorWindowResize = true;
34687     this.regions = {};
34688     this.addEvents({
34689         /**
34690          * @event layout
34691          * Fires when a layout is performed.
34692          * @param {Roo.LayoutManager} this
34693          */
34694         "layout" : true,
34695         /**
34696          * @event regionresized
34697          * Fires when the user resizes a region.
34698          * @param {Roo.LayoutRegion} region The resized region
34699          * @param {Number} newSize The new size (width for east/west, height for north/south)
34700          */
34701         "regionresized" : true,
34702         /**
34703          * @event regioncollapsed
34704          * Fires when a region is collapsed.
34705          * @param {Roo.LayoutRegion} region The collapsed region
34706          */
34707         "regioncollapsed" : true,
34708         /**
34709          * @event regionexpanded
34710          * Fires when a region is expanded.
34711          * @param {Roo.LayoutRegion} region The expanded region
34712          */
34713         "regionexpanded" : true
34714     });
34715     this.updating = false;
34716
34717     if (config.el) {
34718         this.el = Roo.get(config.el);
34719         this.initEvents();
34720     }
34721
34722 };
34723
34724 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34725
34726
34727     regions : null,
34728
34729     monitorWindowResize : true,
34730
34731
34732     updating : false,
34733
34734
34735     onRender : function(ct, position)
34736     {
34737         if(!this.el){
34738             this.el = Roo.get(ct);
34739             this.initEvents();
34740         }
34741         //this.fireEvent('render',this);
34742     },
34743
34744
34745     initEvents: function()
34746     {
34747
34748
34749         // ie scrollbar fix
34750         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34751             document.body.scroll = "no";
34752         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34753             this.el.position('relative');
34754         }
34755         this.id = this.el.id;
34756         this.el.addClass("roo-layout-container");
34757         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34758         if(this.el.dom != document.body ) {
34759             this.el.on('resize', this.layout,this);
34760             this.el.on('show', this.layout,this);
34761         }
34762
34763     },
34764
34765     /**
34766      * Returns true if this layout is currently being updated
34767      * @return {Boolean}
34768      */
34769     isUpdating : function(){
34770         return this.updating;
34771     },
34772
34773     /**
34774      * Suspend the LayoutManager from doing auto-layouts while
34775      * making multiple add or remove calls
34776      */
34777     beginUpdate : function(){
34778         this.updating = true;
34779     },
34780
34781     /**
34782      * Restore auto-layouts and optionally disable the manager from performing a layout
34783      * @param {Boolean} noLayout true to disable a layout update
34784      */
34785     endUpdate : function(noLayout){
34786         this.updating = false;
34787         if(!noLayout){
34788             this.layout();
34789         }
34790     },
34791
34792     layout: function(){
34793         // abstract...
34794     },
34795
34796     onRegionResized : function(region, newSize){
34797         this.fireEvent("regionresized", region, newSize);
34798         this.layout();
34799     },
34800
34801     onRegionCollapsed : function(region){
34802         this.fireEvent("regioncollapsed", region);
34803     },
34804
34805     onRegionExpanded : function(region){
34806         this.fireEvent("regionexpanded", region);
34807     },
34808
34809     /**
34810      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34811      * performs box-model adjustments.
34812      * @return {Object} The size as an object {width: (the width), height: (the height)}
34813      */
34814     getViewSize : function()
34815     {
34816         var size;
34817         if(this.el.dom != document.body){
34818             size = this.el.getSize();
34819         }else{
34820             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34821         }
34822         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34823         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34824         return size;
34825     },
34826
34827     /**
34828      * Returns the Element this layout is bound to.
34829      * @return {Roo.Element}
34830      */
34831     getEl : function(){
34832         return this.el;
34833     },
34834
34835     /**
34836      * Returns the specified region.
34837      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34838      * @return {Roo.LayoutRegion}
34839      */
34840     getRegion : function(target){
34841         return this.regions[target.toLowerCase()];
34842     },
34843
34844     onWindowResize : function(){
34845         if(this.monitorWindowResize){
34846             this.layout();
34847         }
34848     }
34849 });
34850 /*
34851  * Based on:
34852  * Ext JS Library 1.1.1
34853  * Copyright(c) 2006-2007, Ext JS, LLC.
34854  *
34855  * Originally Released Under LGPL - original licence link has changed is not relivant.
34856  *
34857  * Fork - LGPL
34858  * <script type="text/javascript">
34859  */
34860 /**
34861  * @class Roo.bootstrap.layout.Border
34862  * @extends Roo.bootstrap.layout.Manager
34863  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34864  * please see: examples/bootstrap/nested.html<br><br>
34865  
34866 <b>The container the layout is rendered into can be either the body element or any other element.
34867 If it is not the body element, the container needs to either be an absolute positioned element,
34868 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34869 the container size if it is not the body element.</b>
34870
34871 * @constructor
34872 * Create a new Border
34873 * @param {Object} config Configuration options
34874  */
34875 Roo.bootstrap.layout.Border = function(config){
34876     config = config || {};
34877     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34878     
34879     
34880     
34881     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34882         if(config[region]){
34883             config[region].region = region;
34884             this.addRegion(config[region]);
34885         }
34886     },this);
34887     
34888 };
34889
34890 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34891
34892 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34893     /**
34894      * Creates and adds a new region if it doesn't already exist.
34895      * @param {String} target The target region key (north, south, east, west or center).
34896      * @param {Object} config The regions config object
34897      * @return {BorderLayoutRegion} The new region
34898      */
34899     addRegion : function(config)
34900     {
34901         if(!this.regions[config.region]){
34902             var r = this.factory(config);
34903             this.bindRegion(r);
34904         }
34905         return this.regions[config.region];
34906     },
34907
34908     // private (kinda)
34909     bindRegion : function(r){
34910         this.regions[r.config.region] = r;
34911         
34912         r.on("visibilitychange",    this.layout, this);
34913         r.on("paneladded",          this.layout, this);
34914         r.on("panelremoved",        this.layout, this);
34915         r.on("invalidated",         this.layout, this);
34916         r.on("resized",             this.onRegionResized, this);
34917         r.on("collapsed",           this.onRegionCollapsed, this);
34918         r.on("expanded",            this.onRegionExpanded, this);
34919     },
34920
34921     /**
34922      * Performs a layout update.
34923      */
34924     layout : function()
34925     {
34926         if(this.updating) {
34927             return;
34928         }
34929         
34930         // render all the rebions if they have not been done alreayd?
34931         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34932             if(this.regions[region] && !this.regions[region].bodyEl){
34933                 this.regions[region].onRender(this.el)
34934             }
34935         },this);
34936         
34937         var size = this.getViewSize();
34938         var w = size.width;
34939         var h = size.height;
34940         var centerW = w;
34941         var centerH = h;
34942         var centerY = 0;
34943         var centerX = 0;
34944         //var x = 0, y = 0;
34945
34946         var rs = this.regions;
34947         var north = rs["north"];
34948         var south = rs["south"]; 
34949         var west = rs["west"];
34950         var east = rs["east"];
34951         var center = rs["center"];
34952         //if(this.hideOnLayout){ // not supported anymore
34953             //c.el.setStyle("display", "none");
34954         //}
34955         if(north && north.isVisible()){
34956             var b = north.getBox();
34957             var m = north.getMargins();
34958             b.width = w - (m.left+m.right);
34959             b.x = m.left;
34960             b.y = m.top;
34961             centerY = b.height + b.y + m.bottom;
34962             centerH -= centerY;
34963             north.updateBox(this.safeBox(b));
34964         }
34965         if(south && south.isVisible()){
34966             var b = south.getBox();
34967             var m = south.getMargins();
34968             b.width = w - (m.left+m.right);
34969             b.x = m.left;
34970             var totalHeight = (b.height + m.top + m.bottom);
34971             b.y = h - totalHeight + m.top;
34972             centerH -= totalHeight;
34973             south.updateBox(this.safeBox(b));
34974         }
34975         if(west && west.isVisible()){
34976             var b = west.getBox();
34977             var m = west.getMargins();
34978             b.height = centerH - (m.top+m.bottom);
34979             b.x = m.left;
34980             b.y = centerY + m.top;
34981             var totalWidth = (b.width + m.left + m.right);
34982             centerX += totalWidth;
34983             centerW -= totalWidth;
34984             west.updateBox(this.safeBox(b));
34985         }
34986         if(east && east.isVisible()){
34987             var b = east.getBox();
34988             var m = east.getMargins();
34989             b.height = centerH - (m.top+m.bottom);
34990             var totalWidth = (b.width + m.left + m.right);
34991             b.x = w - totalWidth + m.left;
34992             b.y = centerY + m.top;
34993             centerW -= totalWidth;
34994             east.updateBox(this.safeBox(b));
34995         }
34996         if(center){
34997             var m = center.getMargins();
34998             var centerBox = {
34999                 x: centerX + m.left,
35000                 y: centerY + m.top,
35001                 width: centerW - (m.left+m.right),
35002                 height: centerH - (m.top+m.bottom)
35003             };
35004             //if(this.hideOnLayout){
35005                 //center.el.setStyle("display", "block");
35006             //}
35007             center.updateBox(this.safeBox(centerBox));
35008         }
35009         this.el.repaint();
35010         this.fireEvent("layout", this);
35011     },
35012
35013     // private
35014     safeBox : function(box){
35015         box.width = Math.max(0, box.width);
35016         box.height = Math.max(0, box.height);
35017         return box;
35018     },
35019
35020     /**
35021      * Adds a ContentPanel (or subclass) to this layout.
35022      * @param {String} target The target region key (north, south, east, west or center).
35023      * @param {Roo.ContentPanel} panel The panel to add
35024      * @return {Roo.ContentPanel} The added panel
35025      */
35026     add : function(target, panel){
35027          
35028         target = target.toLowerCase();
35029         return this.regions[target].add(panel);
35030     },
35031
35032     /**
35033      * Remove a ContentPanel (or subclass) to this layout.
35034      * @param {String} target The target region key (north, south, east, west or center).
35035      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35036      * @return {Roo.ContentPanel} The removed panel
35037      */
35038     remove : function(target, panel){
35039         target = target.toLowerCase();
35040         return this.regions[target].remove(panel);
35041     },
35042
35043     /**
35044      * Searches all regions for a panel with the specified id
35045      * @param {String} panelId
35046      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35047      */
35048     findPanel : function(panelId){
35049         var rs = this.regions;
35050         for(var target in rs){
35051             if(typeof rs[target] != "function"){
35052                 var p = rs[target].getPanel(panelId);
35053                 if(p){
35054                     return p;
35055                 }
35056             }
35057         }
35058         return null;
35059     },
35060
35061     /**
35062      * Searches all regions for a panel with the specified id and activates (shows) it.
35063      * @param {String/ContentPanel} panelId The panels id or the panel itself
35064      * @return {Roo.ContentPanel} The shown panel or null
35065      */
35066     showPanel : function(panelId) {
35067       var rs = this.regions;
35068       for(var target in rs){
35069          var r = rs[target];
35070          if(typeof r != "function"){
35071             if(r.hasPanel(panelId)){
35072                return r.showPanel(panelId);
35073             }
35074          }
35075       }
35076       return null;
35077    },
35078
35079    /**
35080      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35081      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35082      */
35083    /*
35084     restoreState : function(provider){
35085         if(!provider){
35086             provider = Roo.state.Manager;
35087         }
35088         var sm = new Roo.LayoutStateManager();
35089         sm.init(this, provider);
35090     },
35091 */
35092  
35093  
35094     /**
35095      * Adds a xtype elements to the layout.
35096      * <pre><code>
35097
35098 layout.addxtype({
35099        xtype : 'ContentPanel',
35100        region: 'west',
35101        items: [ .... ]
35102    }
35103 );
35104
35105 layout.addxtype({
35106         xtype : 'NestedLayoutPanel',
35107         region: 'west',
35108         layout: {
35109            center: { },
35110            west: { }   
35111         },
35112         items : [ ... list of content panels or nested layout panels.. ]
35113    }
35114 );
35115 </code></pre>
35116      * @param {Object} cfg Xtype definition of item to add.
35117      */
35118     addxtype : function(cfg)
35119     {
35120         // basically accepts a pannel...
35121         // can accept a layout region..!?!?
35122         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35123         
35124         
35125         // theory?  children can only be panels??
35126         
35127         //if (!cfg.xtype.match(/Panel$/)) {
35128         //    return false;
35129         //}
35130         var ret = false;
35131         
35132         if (typeof(cfg.region) == 'undefined') {
35133             Roo.log("Failed to add Panel, region was not set");
35134             Roo.log(cfg);
35135             return false;
35136         }
35137         var region = cfg.region;
35138         delete cfg.region;
35139         
35140           
35141         var xitems = [];
35142         if (cfg.items) {
35143             xitems = cfg.items;
35144             delete cfg.items;
35145         }
35146         var nb = false;
35147         
35148         switch(cfg.xtype) 
35149         {
35150             case 'Content':  // ContentPanel (el, cfg)
35151             case 'Scroll':  // ContentPanel (el, cfg)
35152             case 'View': 
35153                 cfg.autoCreate = true;
35154                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35155                 //} else {
35156                 //    var el = this.el.createChild();
35157                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35158                 //}
35159                 
35160                 this.add(region, ret);
35161                 break;
35162             
35163             /*
35164             case 'TreePanel': // our new panel!
35165                 cfg.el = this.el.createChild();
35166                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35167                 this.add(region, ret);
35168                 break;
35169             */
35170             
35171             case 'Nest': 
35172                 // create a new Layout (which is  a Border Layout...
35173                 
35174                 var clayout = cfg.layout;
35175                 clayout.el  = this.el.createChild();
35176                 clayout.items   = clayout.items  || [];
35177                 
35178                 delete cfg.layout;
35179                 
35180                 // replace this exitems with the clayout ones..
35181                 xitems = clayout.items;
35182                  
35183                 // force background off if it's in center...
35184                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35185                     cfg.background = false;
35186                 }
35187                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35188                 
35189                 
35190                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35191                 //console.log('adding nested layout panel '  + cfg.toSource());
35192                 this.add(region, ret);
35193                 nb = {}; /// find first...
35194                 break;
35195             
35196             case 'Grid':
35197                 
35198                 // needs grid and region
35199                 
35200                 //var el = this.getRegion(region).el.createChild();
35201                 /*
35202                  *var el = this.el.createChild();
35203                 // create the grid first...
35204                 cfg.grid.container = el;
35205                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35206                 */
35207                 
35208                 if (region == 'center' && this.active ) {
35209                     cfg.background = false;
35210                 }
35211                 
35212                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35213                 
35214                 this.add(region, ret);
35215                 /*
35216                 if (cfg.background) {
35217                     // render grid on panel activation (if panel background)
35218                     ret.on('activate', function(gp) {
35219                         if (!gp.grid.rendered) {
35220                     //        gp.grid.render(el);
35221                         }
35222                     });
35223                 } else {
35224                   //  cfg.grid.render(el);
35225                 }
35226                 */
35227                 break;
35228            
35229            
35230             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35231                 // it was the old xcomponent building that caused this before.
35232                 // espeically if border is the top element in the tree.
35233                 ret = this;
35234                 break; 
35235                 
35236                     
35237                 
35238                 
35239                 
35240             default:
35241                 /*
35242                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35243                     
35244                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35245                     this.add(region, ret);
35246                 } else {
35247                 */
35248                     Roo.log(cfg);
35249                     throw "Can not add '" + cfg.xtype + "' to Border";
35250                     return null;
35251              
35252                                 
35253              
35254         }
35255         this.beginUpdate();
35256         // add children..
35257         var region = '';
35258         var abn = {};
35259         Roo.each(xitems, function(i)  {
35260             region = nb && i.region ? i.region : false;
35261             
35262             var add = ret.addxtype(i);
35263            
35264             if (region) {
35265                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35266                 if (!i.background) {
35267                     abn[region] = nb[region] ;
35268                 }
35269             }
35270             
35271         });
35272         this.endUpdate();
35273
35274         // make the last non-background panel active..
35275         //if (nb) { Roo.log(abn); }
35276         if (nb) {
35277             
35278             for(var r in abn) {
35279                 region = this.getRegion(r);
35280                 if (region) {
35281                     // tried using nb[r], but it does not work..
35282                      
35283                     region.showPanel(abn[r]);
35284                    
35285                 }
35286             }
35287         }
35288         return ret;
35289         
35290     },
35291     
35292     
35293 // private
35294     factory : function(cfg)
35295     {
35296         
35297         var validRegions = Roo.bootstrap.layout.Border.regions;
35298
35299         var target = cfg.region;
35300         cfg.mgr = this;
35301         
35302         var r = Roo.bootstrap.layout;
35303         Roo.log(target);
35304         switch(target){
35305             case "north":
35306                 return new r.North(cfg);
35307             case "south":
35308                 return new r.South(cfg);
35309             case "east":
35310                 return new r.East(cfg);
35311             case "west":
35312                 return new r.West(cfg);
35313             case "center":
35314                 return new r.Center(cfg);
35315         }
35316         throw 'Layout region "'+target+'" not supported.';
35317     }
35318     
35319     
35320 });
35321  /*
35322  * Based on:
35323  * Ext JS Library 1.1.1
35324  * Copyright(c) 2006-2007, Ext JS, LLC.
35325  *
35326  * Originally Released Under LGPL - original licence link has changed is not relivant.
35327  *
35328  * Fork - LGPL
35329  * <script type="text/javascript">
35330  */
35331  
35332 /**
35333  * @class Roo.bootstrap.layout.Basic
35334  * @extends Roo.util.Observable
35335  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35336  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35337  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35338  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35339  * @cfg {string}   region  the region that it inhabits..
35340  * @cfg {bool}   skipConfig skip config?
35341  * 
35342
35343  */
35344 Roo.bootstrap.layout.Basic = function(config){
35345     
35346     this.mgr = config.mgr;
35347     
35348     this.position = config.region;
35349     
35350     var skipConfig = config.skipConfig;
35351     
35352     this.events = {
35353         /**
35354          * @scope Roo.BasicLayoutRegion
35355          */
35356         
35357         /**
35358          * @event beforeremove
35359          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35360          * @param {Roo.LayoutRegion} this
35361          * @param {Roo.ContentPanel} panel The panel
35362          * @param {Object} e The cancel event object
35363          */
35364         "beforeremove" : true,
35365         /**
35366          * @event invalidated
35367          * Fires when the layout for this region is changed.
35368          * @param {Roo.LayoutRegion} this
35369          */
35370         "invalidated" : true,
35371         /**
35372          * @event visibilitychange
35373          * Fires when this region is shown or hidden 
35374          * @param {Roo.LayoutRegion} this
35375          * @param {Boolean} visibility true or false
35376          */
35377         "visibilitychange" : true,
35378         /**
35379          * @event paneladded
35380          * Fires when a panel is added. 
35381          * @param {Roo.LayoutRegion} this
35382          * @param {Roo.ContentPanel} panel The panel
35383          */
35384         "paneladded" : true,
35385         /**
35386          * @event panelremoved
35387          * Fires when a panel is removed. 
35388          * @param {Roo.LayoutRegion} this
35389          * @param {Roo.ContentPanel} panel The panel
35390          */
35391         "panelremoved" : true,
35392         /**
35393          * @event beforecollapse
35394          * Fires when this region before collapse.
35395          * @param {Roo.LayoutRegion} this
35396          */
35397         "beforecollapse" : true,
35398         /**
35399          * @event collapsed
35400          * Fires when this region is collapsed.
35401          * @param {Roo.LayoutRegion} this
35402          */
35403         "collapsed" : true,
35404         /**
35405          * @event expanded
35406          * Fires when this region is expanded.
35407          * @param {Roo.LayoutRegion} this
35408          */
35409         "expanded" : true,
35410         /**
35411          * @event slideshow
35412          * Fires when this region is slid into view.
35413          * @param {Roo.LayoutRegion} this
35414          */
35415         "slideshow" : true,
35416         /**
35417          * @event slidehide
35418          * Fires when this region slides out of view. 
35419          * @param {Roo.LayoutRegion} this
35420          */
35421         "slidehide" : true,
35422         /**
35423          * @event panelactivated
35424          * Fires when a panel is activated. 
35425          * @param {Roo.LayoutRegion} this
35426          * @param {Roo.ContentPanel} panel The activated panel
35427          */
35428         "panelactivated" : true,
35429         /**
35430          * @event resized
35431          * Fires when the user resizes this region. 
35432          * @param {Roo.LayoutRegion} this
35433          * @param {Number} newSize The new size (width for east/west, height for north/south)
35434          */
35435         "resized" : true
35436     };
35437     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35438     this.panels = new Roo.util.MixedCollection();
35439     this.panels.getKey = this.getPanelId.createDelegate(this);
35440     this.box = null;
35441     this.activePanel = null;
35442     // ensure listeners are added...
35443     
35444     if (config.listeners || config.events) {
35445         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35446             listeners : config.listeners || {},
35447             events : config.events || {}
35448         });
35449     }
35450     
35451     if(skipConfig !== true){
35452         this.applyConfig(config);
35453     }
35454 };
35455
35456 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35457 {
35458     getPanelId : function(p){
35459         return p.getId();
35460     },
35461     
35462     applyConfig : function(config){
35463         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35464         this.config = config;
35465         
35466     },
35467     
35468     /**
35469      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35470      * the width, for horizontal (north, south) the height.
35471      * @param {Number} newSize The new width or height
35472      */
35473     resizeTo : function(newSize){
35474         var el = this.el ? this.el :
35475                  (this.activePanel ? this.activePanel.getEl() : null);
35476         if(el){
35477             switch(this.position){
35478                 case "east":
35479                 case "west":
35480                     el.setWidth(newSize);
35481                     this.fireEvent("resized", this, newSize);
35482                 break;
35483                 case "north":
35484                 case "south":
35485                     el.setHeight(newSize);
35486                     this.fireEvent("resized", this, newSize);
35487                 break;                
35488             }
35489         }
35490     },
35491     
35492     getBox : function(){
35493         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35494     },
35495     
35496     getMargins : function(){
35497         return this.margins;
35498     },
35499     
35500     updateBox : function(box){
35501         this.box = box;
35502         var el = this.activePanel.getEl();
35503         el.dom.style.left = box.x + "px";
35504         el.dom.style.top = box.y + "px";
35505         this.activePanel.setSize(box.width, box.height);
35506     },
35507     
35508     /**
35509      * Returns the container element for this region.
35510      * @return {Roo.Element}
35511      */
35512     getEl : function(){
35513         return this.activePanel;
35514     },
35515     
35516     /**
35517      * Returns true if this region is currently visible.
35518      * @return {Boolean}
35519      */
35520     isVisible : function(){
35521         return this.activePanel ? true : false;
35522     },
35523     
35524     setActivePanel : function(panel){
35525         panel = this.getPanel(panel);
35526         if(this.activePanel && this.activePanel != panel){
35527             this.activePanel.setActiveState(false);
35528             this.activePanel.getEl().setLeftTop(-10000,-10000);
35529         }
35530         this.activePanel = panel;
35531         panel.setActiveState(true);
35532         if(this.box){
35533             panel.setSize(this.box.width, this.box.height);
35534         }
35535         this.fireEvent("panelactivated", this, panel);
35536         this.fireEvent("invalidated");
35537     },
35538     
35539     /**
35540      * Show the specified panel.
35541      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35542      * @return {Roo.ContentPanel} The shown panel or null
35543      */
35544     showPanel : function(panel){
35545         panel = this.getPanel(panel);
35546         if(panel){
35547             this.setActivePanel(panel);
35548         }
35549         return panel;
35550     },
35551     
35552     /**
35553      * Get the active panel for this region.
35554      * @return {Roo.ContentPanel} The active panel or null
35555      */
35556     getActivePanel : function(){
35557         return this.activePanel;
35558     },
35559     
35560     /**
35561      * Add the passed ContentPanel(s)
35562      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35563      * @return {Roo.ContentPanel} The panel added (if only one was added)
35564      */
35565     add : function(panel){
35566         if(arguments.length > 1){
35567             for(var i = 0, len = arguments.length; i < len; i++) {
35568                 this.add(arguments[i]);
35569             }
35570             return null;
35571         }
35572         if(this.hasPanel(panel)){
35573             this.showPanel(panel);
35574             return panel;
35575         }
35576         var el = panel.getEl();
35577         if(el.dom.parentNode != this.mgr.el.dom){
35578             this.mgr.el.dom.appendChild(el.dom);
35579         }
35580         if(panel.setRegion){
35581             panel.setRegion(this);
35582         }
35583         this.panels.add(panel);
35584         el.setStyle("position", "absolute");
35585         if(!panel.background){
35586             this.setActivePanel(panel);
35587             if(this.config.initialSize && this.panels.getCount()==1){
35588                 this.resizeTo(this.config.initialSize);
35589             }
35590         }
35591         this.fireEvent("paneladded", this, panel);
35592         return panel;
35593     },
35594     
35595     /**
35596      * Returns true if the panel is in this region.
35597      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35598      * @return {Boolean}
35599      */
35600     hasPanel : function(panel){
35601         if(typeof panel == "object"){ // must be panel obj
35602             panel = panel.getId();
35603         }
35604         return this.getPanel(panel) ? true : false;
35605     },
35606     
35607     /**
35608      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35609      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35610      * @param {Boolean} preservePanel Overrides the config preservePanel option
35611      * @return {Roo.ContentPanel} The panel that was removed
35612      */
35613     remove : function(panel, preservePanel){
35614         panel = this.getPanel(panel);
35615         if(!panel){
35616             return null;
35617         }
35618         var e = {};
35619         this.fireEvent("beforeremove", this, panel, e);
35620         if(e.cancel === true){
35621             return null;
35622         }
35623         var panelId = panel.getId();
35624         this.panels.removeKey(panelId);
35625         return panel;
35626     },
35627     
35628     /**
35629      * Returns the panel specified or null if it's not in this region.
35630      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35631      * @return {Roo.ContentPanel}
35632      */
35633     getPanel : function(id){
35634         if(typeof id == "object"){ // must be panel obj
35635             return id;
35636         }
35637         return this.panels.get(id);
35638     },
35639     
35640     /**
35641      * Returns this regions position (north/south/east/west/center).
35642      * @return {String} 
35643      */
35644     getPosition: function(){
35645         return this.position;    
35646     }
35647 });/*
35648  * Based on:
35649  * Ext JS Library 1.1.1
35650  * Copyright(c) 2006-2007, Ext JS, LLC.
35651  *
35652  * Originally Released Under LGPL - original licence link has changed is not relivant.
35653  *
35654  * Fork - LGPL
35655  * <script type="text/javascript">
35656  */
35657  
35658 /**
35659  * @class Roo.bootstrap.layout.Region
35660  * @extends Roo.bootstrap.layout.Basic
35661  * This class represents a region in a layout manager.
35662  
35663  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35664  * @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})
35665  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35666  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35667  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35668  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35669  * @cfg {String}    title           The title for the region (overrides panel titles)
35670  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35671  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35672  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35673  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35674  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35675  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35676  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35677  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35678  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35679  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35680
35681  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35682  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35683  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35684  * @cfg {Number}    width           For East/West panels
35685  * @cfg {Number}    height          For North/South panels
35686  * @cfg {Boolean}   split           To show the splitter
35687  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35688  * 
35689  * @cfg {string}   cls             Extra CSS classes to add to region
35690  * 
35691  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35692  * @cfg {string}   region  the region that it inhabits..
35693  *
35694
35695  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35696  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35697
35698  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35699  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35700  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35701  */
35702 Roo.bootstrap.layout.Region = function(config)
35703 {
35704     this.applyConfig(config);
35705
35706     var mgr = config.mgr;
35707     var pos = config.region;
35708     config.skipConfig = true;
35709     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35710     
35711     if (mgr.el) {
35712         this.onRender(mgr.el);   
35713     }
35714      
35715     this.visible = true;
35716     this.collapsed = false;
35717     this.unrendered_panels = [];
35718 };
35719
35720 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35721
35722     position: '', // set by wrapper (eg. north/south etc..)
35723     unrendered_panels : null,  // unrendered panels.
35724     createBody : function(){
35725         /** This region's body element 
35726         * @type Roo.Element */
35727         this.bodyEl = this.el.createChild({
35728                 tag: "div",
35729                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35730         });
35731     },
35732
35733     onRender: function(ctr, pos)
35734     {
35735         var dh = Roo.DomHelper;
35736         /** This region's container element 
35737         * @type Roo.Element */
35738         this.el = dh.append(ctr.dom, {
35739                 tag: "div",
35740                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35741             }, true);
35742         /** This region's title element 
35743         * @type Roo.Element */
35744     
35745         this.titleEl = dh.append(this.el.dom,
35746             {
35747                     tag: "div",
35748                     unselectable: "on",
35749                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35750                     children:[
35751                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35752                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35753                     ]}, true);
35754         
35755         this.titleEl.enableDisplayMode();
35756         /** This region's title text element 
35757         * @type HTMLElement */
35758         this.titleTextEl = this.titleEl.dom.firstChild;
35759         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35760         /*
35761         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35762         this.closeBtn.enableDisplayMode();
35763         this.closeBtn.on("click", this.closeClicked, this);
35764         this.closeBtn.hide();
35765     */
35766         this.createBody(this.config);
35767         if(this.config.hideWhenEmpty){
35768             this.hide();
35769             this.on("paneladded", this.validateVisibility, this);
35770             this.on("panelremoved", this.validateVisibility, this);
35771         }
35772         if(this.autoScroll){
35773             this.bodyEl.setStyle("overflow", "auto");
35774         }else{
35775             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35776         }
35777         //if(c.titlebar !== false){
35778             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35779                 this.titleEl.hide();
35780             }else{
35781                 this.titleEl.show();
35782                 if(this.config.title){
35783                     this.titleTextEl.innerHTML = this.config.title;
35784                 }
35785             }
35786         //}
35787         if(this.config.collapsed){
35788             this.collapse(true);
35789         }
35790         if(this.config.hidden){
35791             this.hide();
35792         }
35793         
35794         if (this.unrendered_panels && this.unrendered_panels.length) {
35795             for (var i =0;i< this.unrendered_panels.length; i++) {
35796                 this.add(this.unrendered_panels[i]);
35797             }
35798             this.unrendered_panels = null;
35799             
35800         }
35801         
35802     },
35803     
35804     applyConfig : function(c)
35805     {
35806         /*
35807          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35808             var dh = Roo.DomHelper;
35809             if(c.titlebar !== false){
35810                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35811                 this.collapseBtn.on("click", this.collapse, this);
35812                 this.collapseBtn.enableDisplayMode();
35813                 /*
35814                 if(c.showPin === true || this.showPin){
35815                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35816                     this.stickBtn.enableDisplayMode();
35817                     this.stickBtn.on("click", this.expand, this);
35818                     this.stickBtn.hide();
35819                 }
35820                 
35821             }
35822             */
35823             /** This region's collapsed element
35824             * @type Roo.Element */
35825             /*
35826              *
35827             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35828                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35829             ]}, true);
35830             
35831             if(c.floatable !== false){
35832                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35833                this.collapsedEl.on("click", this.collapseClick, this);
35834             }
35835
35836             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35837                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35838                    id: "message", unselectable: "on", style:{"float":"left"}});
35839                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35840              }
35841             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35842             this.expandBtn.on("click", this.expand, this);
35843             
35844         }
35845         
35846         if(this.collapseBtn){
35847             this.collapseBtn.setVisible(c.collapsible == true);
35848         }
35849         
35850         this.cmargins = c.cmargins || this.cmargins ||
35851                          (this.position == "west" || this.position == "east" ?
35852                              {top: 0, left: 2, right:2, bottom: 0} :
35853                              {top: 2, left: 0, right:0, bottom: 2});
35854         */
35855         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35856         
35857         
35858         this.bottomTabs = c.tabPosition != "top";
35859         
35860         this.autoScroll = c.autoScroll || false;
35861         
35862         
35863        
35864         
35865         this.duration = c.duration || .30;
35866         this.slideDuration = c.slideDuration || .45;
35867         this.config = c;
35868        
35869     },
35870     /**
35871      * Returns true if this region is currently visible.
35872      * @return {Boolean}
35873      */
35874     isVisible : function(){
35875         return this.visible;
35876     },
35877
35878     /**
35879      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35880      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35881      */
35882     //setCollapsedTitle : function(title){
35883     //    title = title || "&#160;";
35884      //   if(this.collapsedTitleTextEl){
35885       //      this.collapsedTitleTextEl.innerHTML = title;
35886        // }
35887     //},
35888
35889     getBox : function(){
35890         var b;
35891       //  if(!this.collapsed){
35892             b = this.el.getBox(false, true);
35893        // }else{
35894           //  b = this.collapsedEl.getBox(false, true);
35895         //}
35896         return b;
35897     },
35898
35899     getMargins : function(){
35900         return this.margins;
35901         //return this.collapsed ? this.cmargins : this.margins;
35902     },
35903 /*
35904     highlight : function(){
35905         this.el.addClass("x-layout-panel-dragover");
35906     },
35907
35908     unhighlight : function(){
35909         this.el.removeClass("x-layout-panel-dragover");
35910     },
35911 */
35912     updateBox : function(box)
35913     {
35914         if (!this.bodyEl) {
35915             return; // not rendered yet..
35916         }
35917         
35918         this.box = box;
35919         if(!this.collapsed){
35920             this.el.dom.style.left = box.x + "px";
35921             this.el.dom.style.top = box.y + "px";
35922             this.updateBody(box.width, box.height);
35923         }else{
35924             this.collapsedEl.dom.style.left = box.x + "px";
35925             this.collapsedEl.dom.style.top = box.y + "px";
35926             this.collapsedEl.setSize(box.width, box.height);
35927         }
35928         if(this.tabs){
35929             this.tabs.autoSizeTabs();
35930         }
35931     },
35932
35933     updateBody : function(w, h)
35934     {
35935         if(w !== null){
35936             this.el.setWidth(w);
35937             w -= this.el.getBorderWidth("rl");
35938             if(this.config.adjustments){
35939                 w += this.config.adjustments[0];
35940             }
35941         }
35942         if(h !== null && h > 0){
35943             this.el.setHeight(h);
35944             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35945             h -= this.el.getBorderWidth("tb");
35946             if(this.config.adjustments){
35947                 h += this.config.adjustments[1];
35948             }
35949             this.bodyEl.setHeight(h);
35950             if(this.tabs){
35951                 h = this.tabs.syncHeight(h);
35952             }
35953         }
35954         if(this.panelSize){
35955             w = w !== null ? w : this.panelSize.width;
35956             h = h !== null ? h : this.panelSize.height;
35957         }
35958         if(this.activePanel){
35959             var el = this.activePanel.getEl();
35960             w = w !== null ? w : el.getWidth();
35961             h = h !== null ? h : el.getHeight();
35962             this.panelSize = {width: w, height: h};
35963             this.activePanel.setSize(w, h);
35964         }
35965         if(Roo.isIE && this.tabs){
35966             this.tabs.el.repaint();
35967         }
35968     },
35969
35970     /**
35971      * Returns the container element for this region.
35972      * @return {Roo.Element}
35973      */
35974     getEl : function(){
35975         return this.el;
35976     },
35977
35978     /**
35979      * Hides this region.
35980      */
35981     hide : function(){
35982         //if(!this.collapsed){
35983             this.el.dom.style.left = "-2000px";
35984             this.el.hide();
35985         //}else{
35986          //   this.collapsedEl.dom.style.left = "-2000px";
35987          //   this.collapsedEl.hide();
35988        // }
35989         this.visible = false;
35990         this.fireEvent("visibilitychange", this, false);
35991     },
35992
35993     /**
35994      * Shows this region if it was previously hidden.
35995      */
35996     show : function(){
35997         //if(!this.collapsed){
35998             this.el.show();
35999         //}else{
36000         //    this.collapsedEl.show();
36001        // }
36002         this.visible = true;
36003         this.fireEvent("visibilitychange", this, true);
36004     },
36005 /*
36006     closeClicked : function(){
36007         if(this.activePanel){
36008             this.remove(this.activePanel);
36009         }
36010     },
36011
36012     collapseClick : function(e){
36013         if(this.isSlid){
36014            e.stopPropagation();
36015            this.slideIn();
36016         }else{
36017            e.stopPropagation();
36018            this.slideOut();
36019         }
36020     },
36021 */
36022     /**
36023      * Collapses this region.
36024      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36025      */
36026     /*
36027     collapse : function(skipAnim, skipCheck = false){
36028         if(this.collapsed) {
36029             return;
36030         }
36031         
36032         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36033             
36034             this.collapsed = true;
36035             if(this.split){
36036                 this.split.el.hide();
36037             }
36038             if(this.config.animate && skipAnim !== true){
36039                 this.fireEvent("invalidated", this);
36040                 this.animateCollapse();
36041             }else{
36042                 this.el.setLocation(-20000,-20000);
36043                 this.el.hide();
36044                 this.collapsedEl.show();
36045                 this.fireEvent("collapsed", this);
36046                 this.fireEvent("invalidated", this);
36047             }
36048         }
36049         
36050     },
36051 */
36052     animateCollapse : function(){
36053         // overridden
36054     },
36055
36056     /**
36057      * Expands this region if it was previously collapsed.
36058      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36059      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36060      */
36061     /*
36062     expand : function(e, skipAnim){
36063         if(e) {
36064             e.stopPropagation();
36065         }
36066         if(!this.collapsed || this.el.hasActiveFx()) {
36067             return;
36068         }
36069         if(this.isSlid){
36070             this.afterSlideIn();
36071             skipAnim = true;
36072         }
36073         this.collapsed = false;
36074         if(this.config.animate && skipAnim !== true){
36075             this.animateExpand();
36076         }else{
36077             this.el.show();
36078             if(this.split){
36079                 this.split.el.show();
36080             }
36081             this.collapsedEl.setLocation(-2000,-2000);
36082             this.collapsedEl.hide();
36083             this.fireEvent("invalidated", this);
36084             this.fireEvent("expanded", this);
36085         }
36086     },
36087 */
36088     animateExpand : function(){
36089         // overridden
36090     },
36091
36092     initTabs : function()
36093     {
36094         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36095         
36096         var ts = new Roo.bootstrap.panel.Tabs({
36097                 el: this.bodyEl.dom,
36098                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36099                 disableTooltips: this.config.disableTabTips,
36100                 toolbar : this.config.toolbar
36101             });
36102         
36103         if(this.config.hideTabs){
36104             ts.stripWrap.setDisplayed(false);
36105         }
36106         this.tabs = ts;
36107         ts.resizeTabs = this.config.resizeTabs === true;
36108         ts.minTabWidth = this.config.minTabWidth || 40;
36109         ts.maxTabWidth = this.config.maxTabWidth || 250;
36110         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36111         ts.monitorResize = false;
36112         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36113         ts.bodyEl.addClass('roo-layout-tabs-body');
36114         this.panels.each(this.initPanelAsTab, this);
36115     },
36116
36117     initPanelAsTab : function(panel){
36118         var ti = this.tabs.addTab(
36119             panel.getEl().id,
36120             panel.getTitle(),
36121             null,
36122             this.config.closeOnTab && panel.isClosable(),
36123             panel.tpl
36124         );
36125         if(panel.tabTip !== undefined){
36126             ti.setTooltip(panel.tabTip);
36127         }
36128         ti.on("activate", function(){
36129               this.setActivePanel(panel);
36130         }, this);
36131         
36132         if(this.config.closeOnTab){
36133             ti.on("beforeclose", function(t, e){
36134                 e.cancel = true;
36135                 this.remove(panel);
36136             }, this);
36137         }
36138         
36139         panel.tabItem = ti;
36140         
36141         return ti;
36142     },
36143
36144     updatePanelTitle : function(panel, title)
36145     {
36146         if(this.activePanel == panel){
36147             this.updateTitle(title);
36148         }
36149         if(this.tabs){
36150             var ti = this.tabs.getTab(panel.getEl().id);
36151             ti.setText(title);
36152             if(panel.tabTip !== undefined){
36153                 ti.setTooltip(panel.tabTip);
36154             }
36155         }
36156     },
36157
36158     updateTitle : function(title){
36159         if(this.titleTextEl && !this.config.title){
36160             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36161         }
36162     },
36163
36164     setActivePanel : function(panel)
36165     {
36166         panel = this.getPanel(panel);
36167         if(this.activePanel && this.activePanel != panel){
36168             if(this.activePanel.setActiveState(false) === false){
36169                 return;
36170             }
36171         }
36172         this.activePanel = panel;
36173         panel.setActiveState(true);
36174         if(this.panelSize){
36175             panel.setSize(this.panelSize.width, this.panelSize.height);
36176         }
36177         if(this.closeBtn){
36178             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36179         }
36180         this.updateTitle(panel.getTitle());
36181         if(this.tabs){
36182             this.fireEvent("invalidated", this);
36183         }
36184         this.fireEvent("panelactivated", this, panel);
36185     },
36186
36187     /**
36188      * Shows the specified panel.
36189      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36190      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36191      */
36192     showPanel : function(panel)
36193     {
36194         panel = this.getPanel(panel);
36195         if(panel){
36196             if(this.tabs){
36197                 var tab = this.tabs.getTab(panel.getEl().id);
36198                 if(tab.isHidden()){
36199                     this.tabs.unhideTab(tab.id);
36200                 }
36201                 tab.activate();
36202             }else{
36203                 this.setActivePanel(panel);
36204             }
36205         }
36206         return panel;
36207     },
36208
36209     /**
36210      * Get the active panel for this region.
36211      * @return {Roo.ContentPanel} The active panel or null
36212      */
36213     getActivePanel : function(){
36214         return this.activePanel;
36215     },
36216
36217     validateVisibility : function(){
36218         if(this.panels.getCount() < 1){
36219             this.updateTitle("&#160;");
36220             this.closeBtn.hide();
36221             this.hide();
36222         }else{
36223             if(!this.isVisible()){
36224                 this.show();
36225             }
36226         }
36227     },
36228
36229     /**
36230      * Adds the passed ContentPanel(s) to this region.
36231      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36232      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36233      */
36234     add : function(panel)
36235     {
36236         if(arguments.length > 1){
36237             for(var i = 0, len = arguments.length; i < len; i++) {
36238                 this.add(arguments[i]);
36239             }
36240             return null;
36241         }
36242         
36243         // if we have not been rendered yet, then we can not really do much of this..
36244         if (!this.bodyEl) {
36245             this.unrendered_panels.push(panel);
36246             return panel;
36247         }
36248         
36249         
36250         
36251         
36252         if(this.hasPanel(panel)){
36253             this.showPanel(panel);
36254             return panel;
36255         }
36256         panel.setRegion(this);
36257         this.panels.add(panel);
36258        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36259             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36260             // and hide them... ???
36261             this.bodyEl.dom.appendChild(panel.getEl().dom);
36262             if(panel.background !== true){
36263                 this.setActivePanel(panel);
36264             }
36265             this.fireEvent("paneladded", this, panel);
36266             return panel;
36267         }
36268         */
36269         if(!this.tabs){
36270             this.initTabs();
36271         }else{
36272             this.initPanelAsTab(panel);
36273         }
36274         
36275         
36276         if(panel.background !== true){
36277             this.tabs.activate(panel.getEl().id);
36278         }
36279         this.fireEvent("paneladded", this, panel);
36280         return panel;
36281     },
36282
36283     /**
36284      * Hides the tab for the specified panel.
36285      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36286      */
36287     hidePanel : function(panel){
36288         if(this.tabs && (panel = this.getPanel(panel))){
36289             this.tabs.hideTab(panel.getEl().id);
36290         }
36291     },
36292
36293     /**
36294      * Unhides the tab for a previously hidden panel.
36295      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36296      */
36297     unhidePanel : function(panel){
36298         if(this.tabs && (panel = this.getPanel(panel))){
36299             this.tabs.unhideTab(panel.getEl().id);
36300         }
36301     },
36302
36303     clearPanels : function(){
36304         while(this.panels.getCount() > 0){
36305              this.remove(this.panels.first());
36306         }
36307     },
36308
36309     /**
36310      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36311      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36312      * @param {Boolean} preservePanel Overrides the config preservePanel option
36313      * @return {Roo.ContentPanel} The panel that was removed
36314      */
36315     remove : function(panel, preservePanel)
36316     {
36317         panel = this.getPanel(panel);
36318         if(!panel){
36319             return null;
36320         }
36321         var e = {};
36322         this.fireEvent("beforeremove", this, panel, e);
36323         if(e.cancel === true){
36324             return null;
36325         }
36326         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36327         var panelId = panel.getId();
36328         this.panels.removeKey(panelId);
36329         if(preservePanel){
36330             document.body.appendChild(panel.getEl().dom);
36331         }
36332         if(this.tabs){
36333             this.tabs.removeTab(panel.getEl().id);
36334         }else if (!preservePanel){
36335             this.bodyEl.dom.removeChild(panel.getEl().dom);
36336         }
36337         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36338             var p = this.panels.first();
36339             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36340             tempEl.appendChild(p.getEl().dom);
36341             this.bodyEl.update("");
36342             this.bodyEl.dom.appendChild(p.getEl().dom);
36343             tempEl = null;
36344             this.updateTitle(p.getTitle());
36345             this.tabs = null;
36346             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36347             this.setActivePanel(p);
36348         }
36349         panel.setRegion(null);
36350         if(this.activePanel == panel){
36351             this.activePanel = null;
36352         }
36353         if(this.config.autoDestroy !== false && preservePanel !== true){
36354             try{panel.destroy();}catch(e){}
36355         }
36356         this.fireEvent("panelremoved", this, panel);
36357         return panel;
36358     },
36359
36360     /**
36361      * Returns the TabPanel component used by this region
36362      * @return {Roo.TabPanel}
36363      */
36364     getTabs : function(){
36365         return this.tabs;
36366     },
36367
36368     createTool : function(parentEl, className){
36369         var btn = Roo.DomHelper.append(parentEl, {
36370             tag: "div",
36371             cls: "x-layout-tools-button",
36372             children: [ {
36373                 tag: "div",
36374                 cls: "roo-layout-tools-button-inner " + className,
36375                 html: "&#160;"
36376             }]
36377         }, true);
36378         btn.addClassOnOver("roo-layout-tools-button-over");
36379         return btn;
36380     }
36381 });/*
36382  * Based on:
36383  * Ext JS Library 1.1.1
36384  * Copyright(c) 2006-2007, Ext JS, LLC.
36385  *
36386  * Originally Released Under LGPL - original licence link has changed is not relivant.
36387  *
36388  * Fork - LGPL
36389  * <script type="text/javascript">
36390  */
36391  
36392
36393
36394 /**
36395  * @class Roo.SplitLayoutRegion
36396  * @extends Roo.LayoutRegion
36397  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36398  */
36399 Roo.bootstrap.layout.Split = function(config){
36400     this.cursor = config.cursor;
36401     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36402 };
36403
36404 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36405 {
36406     splitTip : "Drag to resize.",
36407     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36408     useSplitTips : false,
36409
36410     applyConfig : function(config){
36411         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36412     },
36413     
36414     onRender : function(ctr,pos) {
36415         
36416         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36417         if(!this.config.split){
36418             return;
36419         }
36420         if(!this.split){
36421             
36422             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36423                             tag: "div",
36424                             id: this.el.id + "-split",
36425                             cls: "roo-layout-split roo-layout-split-"+this.position,
36426                             html: "&#160;"
36427             });
36428             /** The SplitBar for this region 
36429             * @type Roo.SplitBar */
36430             // does not exist yet...
36431             Roo.log([this.position, this.orientation]);
36432             
36433             this.split = new Roo.bootstrap.SplitBar({
36434                 dragElement : splitEl,
36435                 resizingElement: this.el,
36436                 orientation : this.orientation
36437             });
36438             
36439             this.split.on("moved", this.onSplitMove, this);
36440             this.split.useShim = this.config.useShim === true;
36441             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36442             if(this.useSplitTips){
36443                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36444             }
36445             //if(config.collapsible){
36446             //    this.split.el.on("dblclick", this.collapse,  this);
36447             //}
36448         }
36449         if(typeof this.config.minSize != "undefined"){
36450             this.split.minSize = this.config.minSize;
36451         }
36452         if(typeof this.config.maxSize != "undefined"){
36453             this.split.maxSize = this.config.maxSize;
36454         }
36455         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36456             this.hideSplitter();
36457         }
36458         
36459     },
36460
36461     getHMaxSize : function(){
36462          var cmax = this.config.maxSize || 10000;
36463          var center = this.mgr.getRegion("center");
36464          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36465     },
36466
36467     getVMaxSize : function(){
36468          var cmax = this.config.maxSize || 10000;
36469          var center = this.mgr.getRegion("center");
36470          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36471     },
36472
36473     onSplitMove : function(split, newSize){
36474         this.fireEvent("resized", this, newSize);
36475     },
36476     
36477     /** 
36478      * Returns the {@link Roo.SplitBar} for this region.
36479      * @return {Roo.SplitBar}
36480      */
36481     getSplitBar : function(){
36482         return this.split;
36483     },
36484     
36485     hide : function(){
36486         this.hideSplitter();
36487         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36488     },
36489
36490     hideSplitter : function(){
36491         if(this.split){
36492             this.split.el.setLocation(-2000,-2000);
36493             this.split.el.hide();
36494         }
36495     },
36496
36497     show : function(){
36498         if(this.split){
36499             this.split.el.show();
36500         }
36501         Roo.bootstrap.layout.Split.superclass.show.call(this);
36502     },
36503     
36504     beforeSlide: function(){
36505         if(Roo.isGecko){// firefox overflow auto bug workaround
36506             this.bodyEl.clip();
36507             if(this.tabs) {
36508                 this.tabs.bodyEl.clip();
36509             }
36510             if(this.activePanel){
36511                 this.activePanel.getEl().clip();
36512                 
36513                 if(this.activePanel.beforeSlide){
36514                     this.activePanel.beforeSlide();
36515                 }
36516             }
36517         }
36518     },
36519     
36520     afterSlide : function(){
36521         if(Roo.isGecko){// firefox overflow auto bug workaround
36522             this.bodyEl.unclip();
36523             if(this.tabs) {
36524                 this.tabs.bodyEl.unclip();
36525             }
36526             if(this.activePanel){
36527                 this.activePanel.getEl().unclip();
36528                 if(this.activePanel.afterSlide){
36529                     this.activePanel.afterSlide();
36530                 }
36531             }
36532         }
36533     },
36534
36535     initAutoHide : function(){
36536         if(this.autoHide !== false){
36537             if(!this.autoHideHd){
36538                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36539                 this.autoHideHd = {
36540                     "mouseout": function(e){
36541                         if(!e.within(this.el, true)){
36542                             st.delay(500);
36543                         }
36544                     },
36545                     "mouseover" : function(e){
36546                         st.cancel();
36547                     },
36548                     scope : this
36549                 };
36550             }
36551             this.el.on(this.autoHideHd);
36552         }
36553     },
36554
36555     clearAutoHide : function(){
36556         if(this.autoHide !== false){
36557             this.el.un("mouseout", this.autoHideHd.mouseout);
36558             this.el.un("mouseover", this.autoHideHd.mouseover);
36559         }
36560     },
36561
36562     clearMonitor : function(){
36563         Roo.get(document).un("click", this.slideInIf, this);
36564     },
36565
36566     // these names are backwards but not changed for compat
36567     slideOut : function(){
36568         if(this.isSlid || this.el.hasActiveFx()){
36569             return;
36570         }
36571         this.isSlid = true;
36572         if(this.collapseBtn){
36573             this.collapseBtn.hide();
36574         }
36575         this.closeBtnState = this.closeBtn.getStyle('display');
36576         this.closeBtn.hide();
36577         if(this.stickBtn){
36578             this.stickBtn.show();
36579         }
36580         this.el.show();
36581         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36582         this.beforeSlide();
36583         this.el.setStyle("z-index", 10001);
36584         this.el.slideIn(this.getSlideAnchor(), {
36585             callback: function(){
36586                 this.afterSlide();
36587                 this.initAutoHide();
36588                 Roo.get(document).on("click", this.slideInIf, this);
36589                 this.fireEvent("slideshow", this);
36590             },
36591             scope: this,
36592             block: true
36593         });
36594     },
36595
36596     afterSlideIn : function(){
36597         this.clearAutoHide();
36598         this.isSlid = false;
36599         this.clearMonitor();
36600         this.el.setStyle("z-index", "");
36601         if(this.collapseBtn){
36602             this.collapseBtn.show();
36603         }
36604         this.closeBtn.setStyle('display', this.closeBtnState);
36605         if(this.stickBtn){
36606             this.stickBtn.hide();
36607         }
36608         this.fireEvent("slidehide", this);
36609     },
36610
36611     slideIn : function(cb){
36612         if(!this.isSlid || this.el.hasActiveFx()){
36613             Roo.callback(cb);
36614             return;
36615         }
36616         this.isSlid = false;
36617         this.beforeSlide();
36618         this.el.slideOut(this.getSlideAnchor(), {
36619             callback: function(){
36620                 this.el.setLeftTop(-10000, -10000);
36621                 this.afterSlide();
36622                 this.afterSlideIn();
36623                 Roo.callback(cb);
36624             },
36625             scope: this,
36626             block: true
36627         });
36628     },
36629     
36630     slideInIf : function(e){
36631         if(!e.within(this.el)){
36632             this.slideIn();
36633         }
36634     },
36635
36636     animateCollapse : function(){
36637         this.beforeSlide();
36638         this.el.setStyle("z-index", 20000);
36639         var anchor = this.getSlideAnchor();
36640         this.el.slideOut(anchor, {
36641             callback : function(){
36642                 this.el.setStyle("z-index", "");
36643                 this.collapsedEl.slideIn(anchor, {duration:.3});
36644                 this.afterSlide();
36645                 this.el.setLocation(-10000,-10000);
36646                 this.el.hide();
36647                 this.fireEvent("collapsed", this);
36648             },
36649             scope: this,
36650             block: true
36651         });
36652     },
36653
36654     animateExpand : function(){
36655         this.beforeSlide();
36656         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36657         this.el.setStyle("z-index", 20000);
36658         this.collapsedEl.hide({
36659             duration:.1
36660         });
36661         this.el.slideIn(this.getSlideAnchor(), {
36662             callback : function(){
36663                 this.el.setStyle("z-index", "");
36664                 this.afterSlide();
36665                 if(this.split){
36666                     this.split.el.show();
36667                 }
36668                 this.fireEvent("invalidated", this);
36669                 this.fireEvent("expanded", this);
36670             },
36671             scope: this,
36672             block: true
36673         });
36674     },
36675
36676     anchors : {
36677         "west" : "left",
36678         "east" : "right",
36679         "north" : "top",
36680         "south" : "bottom"
36681     },
36682
36683     sanchors : {
36684         "west" : "l",
36685         "east" : "r",
36686         "north" : "t",
36687         "south" : "b"
36688     },
36689
36690     canchors : {
36691         "west" : "tl-tr",
36692         "east" : "tr-tl",
36693         "north" : "tl-bl",
36694         "south" : "bl-tl"
36695     },
36696
36697     getAnchor : function(){
36698         return this.anchors[this.position];
36699     },
36700
36701     getCollapseAnchor : function(){
36702         return this.canchors[this.position];
36703     },
36704
36705     getSlideAnchor : function(){
36706         return this.sanchors[this.position];
36707     },
36708
36709     getAlignAdj : function(){
36710         var cm = this.cmargins;
36711         switch(this.position){
36712             case "west":
36713                 return [0, 0];
36714             break;
36715             case "east":
36716                 return [0, 0];
36717             break;
36718             case "north":
36719                 return [0, 0];
36720             break;
36721             case "south":
36722                 return [0, 0];
36723             break;
36724         }
36725     },
36726
36727     getExpandAdj : function(){
36728         var c = this.collapsedEl, cm = this.cmargins;
36729         switch(this.position){
36730             case "west":
36731                 return [-(cm.right+c.getWidth()+cm.left), 0];
36732             break;
36733             case "east":
36734                 return [cm.right+c.getWidth()+cm.left, 0];
36735             break;
36736             case "north":
36737                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36738             break;
36739             case "south":
36740                 return [0, cm.top+cm.bottom+c.getHeight()];
36741             break;
36742         }
36743     }
36744 });/*
36745  * Based on:
36746  * Ext JS Library 1.1.1
36747  * Copyright(c) 2006-2007, Ext JS, LLC.
36748  *
36749  * Originally Released Under LGPL - original licence link has changed is not relivant.
36750  *
36751  * Fork - LGPL
36752  * <script type="text/javascript">
36753  */
36754 /*
36755  * These classes are private internal classes
36756  */
36757 Roo.bootstrap.layout.Center = function(config){
36758     config.region = "center";
36759     Roo.bootstrap.layout.Region.call(this, config);
36760     this.visible = true;
36761     this.minWidth = config.minWidth || 20;
36762     this.minHeight = config.minHeight || 20;
36763 };
36764
36765 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36766     hide : function(){
36767         // center panel can't be hidden
36768     },
36769     
36770     show : function(){
36771         // center panel can't be hidden
36772     },
36773     
36774     getMinWidth: function(){
36775         return this.minWidth;
36776     },
36777     
36778     getMinHeight: function(){
36779         return this.minHeight;
36780     }
36781 });
36782
36783
36784
36785
36786  
36787
36788
36789
36790
36791
36792 Roo.bootstrap.layout.North = function(config)
36793 {
36794     config.region = 'north';
36795     config.cursor = 'n-resize';
36796     
36797     Roo.bootstrap.layout.Split.call(this, config);
36798     
36799     
36800     if(this.split){
36801         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36802         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36803         this.split.el.addClass("roo-layout-split-v");
36804     }
36805     var size = config.initialSize || config.height;
36806     if(typeof size != "undefined"){
36807         this.el.setHeight(size);
36808     }
36809 };
36810 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36811 {
36812     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36813     
36814     
36815     
36816     getBox : function(){
36817         if(this.collapsed){
36818             return this.collapsedEl.getBox();
36819         }
36820         var box = this.el.getBox();
36821         if(this.split){
36822             box.height += this.split.el.getHeight();
36823         }
36824         return box;
36825     },
36826     
36827     updateBox : function(box){
36828         if(this.split && !this.collapsed){
36829             box.height -= this.split.el.getHeight();
36830             this.split.el.setLeft(box.x);
36831             this.split.el.setTop(box.y+box.height);
36832             this.split.el.setWidth(box.width);
36833         }
36834         if(this.collapsed){
36835             this.updateBody(box.width, null);
36836         }
36837         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36838     }
36839 });
36840
36841
36842
36843
36844
36845 Roo.bootstrap.layout.South = function(config){
36846     config.region = 'south';
36847     config.cursor = 's-resize';
36848     Roo.bootstrap.layout.Split.call(this, config);
36849     if(this.split){
36850         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36851         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36852         this.split.el.addClass("roo-layout-split-v");
36853     }
36854     var size = config.initialSize || config.height;
36855     if(typeof size != "undefined"){
36856         this.el.setHeight(size);
36857     }
36858 };
36859
36860 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36861     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36862     getBox : function(){
36863         if(this.collapsed){
36864             return this.collapsedEl.getBox();
36865         }
36866         var box = this.el.getBox();
36867         if(this.split){
36868             var sh = this.split.el.getHeight();
36869             box.height += sh;
36870             box.y -= sh;
36871         }
36872         return box;
36873     },
36874     
36875     updateBox : function(box){
36876         if(this.split && !this.collapsed){
36877             var sh = this.split.el.getHeight();
36878             box.height -= sh;
36879             box.y += sh;
36880             this.split.el.setLeft(box.x);
36881             this.split.el.setTop(box.y-sh);
36882             this.split.el.setWidth(box.width);
36883         }
36884         if(this.collapsed){
36885             this.updateBody(box.width, null);
36886         }
36887         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36888     }
36889 });
36890
36891 Roo.bootstrap.layout.East = function(config){
36892     config.region = "east";
36893     config.cursor = "e-resize";
36894     Roo.bootstrap.layout.Split.call(this, config);
36895     if(this.split){
36896         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36897         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36898         this.split.el.addClass("roo-layout-split-h");
36899     }
36900     var size = config.initialSize || config.width;
36901     if(typeof size != "undefined"){
36902         this.el.setWidth(size);
36903     }
36904 };
36905 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36906     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36907     getBox : function(){
36908         if(this.collapsed){
36909             return this.collapsedEl.getBox();
36910         }
36911         var box = this.el.getBox();
36912         if(this.split){
36913             var sw = this.split.el.getWidth();
36914             box.width += sw;
36915             box.x -= sw;
36916         }
36917         return box;
36918     },
36919
36920     updateBox : function(box){
36921         if(this.split && !this.collapsed){
36922             var sw = this.split.el.getWidth();
36923             box.width -= sw;
36924             this.split.el.setLeft(box.x);
36925             this.split.el.setTop(box.y);
36926             this.split.el.setHeight(box.height);
36927             box.x += sw;
36928         }
36929         if(this.collapsed){
36930             this.updateBody(null, box.height);
36931         }
36932         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36933     }
36934 });
36935
36936 Roo.bootstrap.layout.West = function(config){
36937     config.region = "west";
36938     config.cursor = "w-resize";
36939     
36940     Roo.bootstrap.layout.Split.call(this, config);
36941     if(this.split){
36942         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36943         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36944         this.split.el.addClass("roo-layout-split-h");
36945     }
36946     
36947 };
36948 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36949     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36950     
36951     onRender: function(ctr, pos)
36952     {
36953         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36954         var size = this.config.initialSize || this.config.width;
36955         if(typeof size != "undefined"){
36956             this.el.setWidth(size);
36957         }
36958     },
36959     
36960     getBox : function(){
36961         if(this.collapsed){
36962             return this.collapsedEl.getBox();
36963         }
36964         var box = this.el.getBox();
36965         if(this.split){
36966             box.width += this.split.el.getWidth();
36967         }
36968         return box;
36969     },
36970     
36971     updateBox : function(box){
36972         if(this.split && !this.collapsed){
36973             var sw = this.split.el.getWidth();
36974             box.width -= sw;
36975             this.split.el.setLeft(box.x+box.width);
36976             this.split.el.setTop(box.y);
36977             this.split.el.setHeight(box.height);
36978         }
36979         if(this.collapsed){
36980             this.updateBody(null, box.height);
36981         }
36982         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36983     }
36984 });
36985 Roo.namespace("Roo.bootstrap.panel");/*
36986  * Based on:
36987  * Ext JS Library 1.1.1
36988  * Copyright(c) 2006-2007, Ext JS, LLC.
36989  *
36990  * Originally Released Under LGPL - original licence link has changed is not relivant.
36991  *
36992  * Fork - LGPL
36993  * <script type="text/javascript">
36994  */
36995 /**
36996  * @class Roo.ContentPanel
36997  * @extends Roo.util.Observable
36998  * A basic ContentPanel element.
36999  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37000  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37001  * @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
37002  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37003  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37004  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37005  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37006  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37007  * @cfg {String} title          The title for this panel
37008  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37009  * @cfg {String} url            Calls {@link #setUrl} with this value
37010  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37011  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37012  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37013  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37014  * @cfg {Boolean} badges render the badges
37015
37016  * @constructor
37017  * Create a new ContentPanel.
37018  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37019  * @param {String/Object} config A string to set only the title or a config object
37020  * @param {String} content (optional) Set the HTML content for this panel
37021  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37022  */
37023 Roo.bootstrap.panel.Content = function( config){
37024     
37025     this.tpl = config.tpl || false;
37026     
37027     var el = config.el;
37028     var content = config.content;
37029
37030     if(config.autoCreate){ // xtype is available if this is called from factory
37031         el = Roo.id();
37032     }
37033     this.el = Roo.get(el);
37034     if(!this.el && config && config.autoCreate){
37035         if(typeof config.autoCreate == "object"){
37036             if(!config.autoCreate.id){
37037                 config.autoCreate.id = config.id||el;
37038             }
37039             this.el = Roo.DomHelper.append(document.body,
37040                         config.autoCreate, true);
37041         }else{
37042             var elcfg =  {   tag: "div",
37043                             cls: "roo-layout-inactive-content",
37044                             id: config.id||el
37045                             };
37046             if (config.html) {
37047                 elcfg.html = config.html;
37048                 
37049             }
37050                         
37051             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37052         }
37053     } 
37054     this.closable = false;
37055     this.loaded = false;
37056     this.active = false;
37057    
37058       
37059     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37060         
37061         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37062         
37063         this.wrapEl = this.el; //this.el.wrap();
37064         var ti = [];
37065         if (config.toolbar.items) {
37066             ti = config.toolbar.items ;
37067             delete config.toolbar.items ;
37068         }
37069         
37070         var nitems = [];
37071         this.toolbar.render(this.wrapEl, 'before');
37072         for(var i =0;i < ti.length;i++) {
37073           //  Roo.log(['add child', items[i]]);
37074             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37075         }
37076         this.toolbar.items = nitems;
37077         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37078         delete config.toolbar;
37079         
37080     }
37081     /*
37082     // xtype created footer. - not sure if will work as we normally have to render first..
37083     if (this.footer && !this.footer.el && this.footer.xtype) {
37084         if (!this.wrapEl) {
37085             this.wrapEl = this.el.wrap();
37086         }
37087     
37088         this.footer.container = this.wrapEl.createChild();
37089          
37090         this.footer = Roo.factory(this.footer, Roo);
37091         
37092     }
37093     */
37094     
37095      if(typeof config == "string"){
37096         this.title = config;
37097     }else{
37098         Roo.apply(this, config);
37099     }
37100     
37101     if(this.resizeEl){
37102         this.resizeEl = Roo.get(this.resizeEl, true);
37103     }else{
37104         this.resizeEl = this.el;
37105     }
37106     // handle view.xtype
37107     
37108  
37109     
37110     
37111     this.addEvents({
37112         /**
37113          * @event activate
37114          * Fires when this panel is activated. 
37115          * @param {Roo.ContentPanel} this
37116          */
37117         "activate" : true,
37118         /**
37119          * @event deactivate
37120          * Fires when this panel is activated. 
37121          * @param {Roo.ContentPanel} this
37122          */
37123         "deactivate" : true,
37124
37125         /**
37126          * @event resize
37127          * Fires when this panel is resized if fitToFrame is true.
37128          * @param {Roo.ContentPanel} this
37129          * @param {Number} width The width after any component adjustments
37130          * @param {Number} height The height after any component adjustments
37131          */
37132         "resize" : true,
37133         
37134          /**
37135          * @event render
37136          * Fires when this tab is created
37137          * @param {Roo.ContentPanel} this
37138          */
37139         "render" : true
37140         
37141         
37142         
37143     });
37144     
37145
37146     
37147     
37148     if(this.autoScroll){
37149         this.resizeEl.setStyle("overflow", "auto");
37150     } else {
37151         // fix randome scrolling
37152         //this.el.on('scroll', function() {
37153         //    Roo.log('fix random scolling');
37154         //    this.scrollTo('top',0); 
37155         //});
37156     }
37157     content = content || this.content;
37158     if(content){
37159         this.setContent(content);
37160     }
37161     if(config && config.url){
37162         this.setUrl(this.url, this.params, this.loadOnce);
37163     }
37164     
37165     
37166     
37167     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37168     
37169     if (this.view && typeof(this.view.xtype) != 'undefined') {
37170         this.view.el = this.el.appendChild(document.createElement("div"));
37171         this.view = Roo.factory(this.view); 
37172         this.view.render  &&  this.view.render(false, '');  
37173     }
37174     
37175     
37176     this.fireEvent('render', this);
37177 };
37178
37179 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37180     
37181     tabTip : '',
37182     
37183     setRegion : function(region){
37184         this.region = region;
37185         this.setActiveClass(region && !this.background);
37186     },
37187     
37188     
37189     setActiveClass: function(state)
37190     {
37191         if(state){
37192            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37193            this.el.setStyle('position','relative');
37194         }else{
37195            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37196            this.el.setStyle('position', 'absolute');
37197         } 
37198     },
37199     
37200     /**
37201      * Returns the toolbar for this Panel if one was configured. 
37202      * @return {Roo.Toolbar} 
37203      */
37204     getToolbar : function(){
37205         return this.toolbar;
37206     },
37207     
37208     setActiveState : function(active)
37209     {
37210         this.active = active;
37211         this.setActiveClass(active);
37212         if(!active){
37213             if(this.fireEvent("deactivate", this) === false){
37214                 return false;
37215             }
37216             return true;
37217         }
37218         this.fireEvent("activate", this);
37219         return true;
37220     },
37221     /**
37222      * Updates this panel's element
37223      * @param {String} content The new content
37224      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37225     */
37226     setContent : function(content, loadScripts){
37227         this.el.update(content, loadScripts);
37228     },
37229
37230     ignoreResize : function(w, h){
37231         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37232             return true;
37233         }else{
37234             this.lastSize = {width: w, height: h};
37235             return false;
37236         }
37237     },
37238     /**
37239      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37240      * @return {Roo.UpdateManager} The UpdateManager
37241      */
37242     getUpdateManager : function(){
37243         return this.el.getUpdateManager();
37244     },
37245      /**
37246      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37247      * @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:
37248 <pre><code>
37249 panel.load({
37250     url: "your-url.php",
37251     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37252     callback: yourFunction,
37253     scope: yourObject, //(optional scope)
37254     discardUrl: false,
37255     nocache: false,
37256     text: "Loading...",
37257     timeout: 30,
37258     scripts: false
37259 });
37260 </code></pre>
37261      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37262      * 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.
37263      * @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}
37264      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37265      * @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.
37266      * @return {Roo.ContentPanel} this
37267      */
37268     load : function(){
37269         var um = this.el.getUpdateManager();
37270         um.update.apply(um, arguments);
37271         return this;
37272     },
37273
37274
37275     /**
37276      * 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.
37277      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37278      * @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)
37279      * @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)
37280      * @return {Roo.UpdateManager} The UpdateManager
37281      */
37282     setUrl : function(url, params, loadOnce){
37283         if(this.refreshDelegate){
37284             this.removeListener("activate", this.refreshDelegate);
37285         }
37286         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37287         this.on("activate", this.refreshDelegate);
37288         return this.el.getUpdateManager();
37289     },
37290     
37291     _handleRefresh : function(url, params, loadOnce){
37292         if(!loadOnce || !this.loaded){
37293             var updater = this.el.getUpdateManager();
37294             updater.update(url, params, this._setLoaded.createDelegate(this));
37295         }
37296     },
37297     
37298     _setLoaded : function(){
37299         this.loaded = true;
37300     }, 
37301     
37302     /**
37303      * Returns this panel's id
37304      * @return {String} 
37305      */
37306     getId : function(){
37307         return this.el.id;
37308     },
37309     
37310     /** 
37311      * Returns this panel's element - used by regiosn to add.
37312      * @return {Roo.Element} 
37313      */
37314     getEl : function(){
37315         return this.wrapEl || this.el;
37316     },
37317     
37318    
37319     
37320     adjustForComponents : function(width, height)
37321     {
37322         //Roo.log('adjustForComponents ');
37323         if(this.resizeEl != this.el){
37324             width -= this.el.getFrameWidth('lr');
37325             height -= this.el.getFrameWidth('tb');
37326         }
37327         if(this.toolbar){
37328             var te = this.toolbar.getEl();
37329             te.setWidth(width);
37330             height -= te.getHeight();
37331         }
37332         if(this.footer){
37333             var te = this.footer.getEl();
37334             te.setWidth(width);
37335             height -= te.getHeight();
37336         }
37337         
37338         
37339         if(this.adjustments){
37340             width += this.adjustments[0];
37341             height += this.adjustments[1];
37342         }
37343         return {"width": width, "height": height};
37344     },
37345     
37346     setSize : function(width, height){
37347         if(this.fitToFrame && !this.ignoreResize(width, height)){
37348             if(this.fitContainer && this.resizeEl != this.el){
37349                 this.el.setSize(width, height);
37350             }
37351             var size = this.adjustForComponents(width, height);
37352             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37353             this.fireEvent('resize', this, size.width, size.height);
37354         }
37355     },
37356     
37357     /**
37358      * Returns this panel's title
37359      * @return {String} 
37360      */
37361     getTitle : function(){
37362         
37363         if (typeof(this.title) != 'object') {
37364             return this.title;
37365         }
37366         
37367         var t = '';
37368         for (var k in this.title) {
37369             if (!this.title.hasOwnProperty(k)) {
37370                 continue;
37371             }
37372             
37373             if (k.indexOf('-') >= 0) {
37374                 var s = k.split('-');
37375                 for (var i = 0; i<s.length; i++) {
37376                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37377                 }
37378             } else {
37379                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37380             }
37381         }
37382         return t;
37383     },
37384     
37385     /**
37386      * Set this panel's title
37387      * @param {String} title
37388      */
37389     setTitle : function(title){
37390         this.title = title;
37391         if(this.region){
37392             this.region.updatePanelTitle(this, title);
37393         }
37394     },
37395     
37396     /**
37397      * Returns true is this panel was configured to be closable
37398      * @return {Boolean} 
37399      */
37400     isClosable : function(){
37401         return this.closable;
37402     },
37403     
37404     beforeSlide : function(){
37405         this.el.clip();
37406         this.resizeEl.clip();
37407     },
37408     
37409     afterSlide : function(){
37410         this.el.unclip();
37411         this.resizeEl.unclip();
37412     },
37413     
37414     /**
37415      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37416      *   Will fail silently if the {@link #setUrl} method has not been called.
37417      *   This does not activate the panel, just updates its content.
37418      */
37419     refresh : function(){
37420         if(this.refreshDelegate){
37421            this.loaded = false;
37422            this.refreshDelegate();
37423         }
37424     },
37425     
37426     /**
37427      * Destroys this panel
37428      */
37429     destroy : function(){
37430         this.el.removeAllListeners();
37431         var tempEl = document.createElement("span");
37432         tempEl.appendChild(this.el.dom);
37433         tempEl.innerHTML = "";
37434         this.el.remove();
37435         this.el = null;
37436     },
37437     
37438     /**
37439      * form - if the content panel contains a form - this is a reference to it.
37440      * @type {Roo.form.Form}
37441      */
37442     form : false,
37443     /**
37444      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37445      *    This contains a reference to it.
37446      * @type {Roo.View}
37447      */
37448     view : false,
37449     
37450       /**
37451      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37452      * <pre><code>
37453
37454 layout.addxtype({
37455        xtype : 'Form',
37456        items: [ .... ]
37457    }
37458 );
37459
37460 </code></pre>
37461      * @param {Object} cfg Xtype definition of item to add.
37462      */
37463     
37464     
37465     getChildContainer: function () {
37466         return this.getEl();
37467     }
37468     
37469     
37470     /*
37471         var  ret = new Roo.factory(cfg);
37472         return ret;
37473         
37474         
37475         // add form..
37476         if (cfg.xtype.match(/^Form$/)) {
37477             
37478             var el;
37479             //if (this.footer) {
37480             //    el = this.footer.container.insertSibling(false, 'before');
37481             //} else {
37482                 el = this.el.createChild();
37483             //}
37484
37485             this.form = new  Roo.form.Form(cfg);
37486             
37487             
37488             if ( this.form.allItems.length) {
37489                 this.form.render(el.dom);
37490             }
37491             return this.form;
37492         }
37493         // should only have one of theses..
37494         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37495             // views.. should not be just added - used named prop 'view''
37496             
37497             cfg.el = this.el.appendChild(document.createElement("div"));
37498             // factory?
37499             
37500             var ret = new Roo.factory(cfg);
37501              
37502              ret.render && ret.render(false, ''); // render blank..
37503             this.view = ret;
37504             return ret;
37505         }
37506         return false;
37507     }
37508     \*/
37509 });
37510  
37511 /**
37512  * @class Roo.bootstrap.panel.Grid
37513  * @extends Roo.bootstrap.panel.Content
37514  * @constructor
37515  * Create a new GridPanel.
37516  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37517  * @param {Object} config A the config object
37518   
37519  */
37520
37521
37522
37523 Roo.bootstrap.panel.Grid = function(config)
37524 {
37525     
37526       
37527     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37528         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37529
37530     config.el = this.wrapper;
37531     //this.el = this.wrapper;
37532     
37533       if (config.container) {
37534         // ctor'ed from a Border/panel.grid
37535         
37536         
37537         this.wrapper.setStyle("overflow", "hidden");
37538         this.wrapper.addClass('roo-grid-container');
37539
37540     }
37541     
37542     
37543     if(config.toolbar){
37544         var tool_el = this.wrapper.createChild();    
37545         this.toolbar = Roo.factory(config.toolbar);
37546         var ti = [];
37547         if (config.toolbar.items) {
37548             ti = config.toolbar.items ;
37549             delete config.toolbar.items ;
37550         }
37551         
37552         var nitems = [];
37553         this.toolbar.render(tool_el);
37554         for(var i =0;i < ti.length;i++) {
37555           //  Roo.log(['add child', items[i]]);
37556             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37557         }
37558         this.toolbar.items = nitems;
37559         
37560         delete config.toolbar;
37561     }
37562     
37563     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37564     config.grid.scrollBody = true;;
37565     config.grid.monitorWindowResize = false; // turn off autosizing
37566     config.grid.autoHeight = false;
37567     config.grid.autoWidth = false;
37568     
37569     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37570     
37571     if (config.background) {
37572         // render grid on panel activation (if panel background)
37573         this.on('activate', function(gp) {
37574             if (!gp.grid.rendered) {
37575                 gp.grid.render(this.wrapper);
37576                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37577             }
37578         });
37579             
37580     } else {
37581         this.grid.render(this.wrapper);
37582         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37583
37584     }
37585     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37586     // ??? needed ??? config.el = this.wrapper;
37587     
37588     
37589     
37590   
37591     // xtype created footer. - not sure if will work as we normally have to render first..
37592     if (this.footer && !this.footer.el && this.footer.xtype) {
37593         
37594         var ctr = this.grid.getView().getFooterPanel(true);
37595         this.footer.dataSource = this.grid.dataSource;
37596         this.footer = Roo.factory(this.footer, Roo);
37597         this.footer.render(ctr);
37598         
37599     }
37600     
37601     
37602     
37603     
37604      
37605 };
37606
37607 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37608     getId : function(){
37609         return this.grid.id;
37610     },
37611     
37612     /**
37613      * Returns the grid for this panel
37614      * @return {Roo.bootstrap.Table} 
37615      */
37616     getGrid : function(){
37617         return this.grid;    
37618     },
37619     
37620     setSize : function(width, height){
37621         if(!this.ignoreResize(width, height)){
37622             var grid = this.grid;
37623             var size = this.adjustForComponents(width, height);
37624             var gridel = grid.getGridEl();
37625             gridel.setSize(size.width, size.height);
37626             /*
37627             var thd = grid.getGridEl().select('thead',true).first();
37628             var tbd = grid.getGridEl().select('tbody', true).first();
37629             if (tbd) {
37630                 tbd.setSize(width, height - thd.getHeight());
37631             }
37632             */
37633             grid.autoSize();
37634         }
37635     },
37636      
37637     
37638     
37639     beforeSlide : function(){
37640         this.grid.getView().scroller.clip();
37641     },
37642     
37643     afterSlide : function(){
37644         this.grid.getView().scroller.unclip();
37645     },
37646     
37647     destroy : function(){
37648         this.grid.destroy();
37649         delete this.grid;
37650         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37651     }
37652 });
37653
37654 /**
37655  * @class Roo.bootstrap.panel.Nest
37656  * @extends Roo.bootstrap.panel.Content
37657  * @constructor
37658  * Create a new Panel, that can contain a layout.Border.
37659  * 
37660  * 
37661  * @param {Roo.BorderLayout} layout The layout for this panel
37662  * @param {String/Object} config A string to set only the title or a config object
37663  */
37664 Roo.bootstrap.panel.Nest = function(config)
37665 {
37666     // construct with only one argument..
37667     /* FIXME - implement nicer consturctors
37668     if (layout.layout) {
37669         config = layout;
37670         layout = config.layout;
37671         delete config.layout;
37672     }
37673     if (layout.xtype && !layout.getEl) {
37674         // then layout needs constructing..
37675         layout = Roo.factory(layout, Roo);
37676     }
37677     */
37678     
37679     config.el =  config.layout.getEl();
37680     
37681     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37682     
37683     config.layout.monitorWindowResize = false; // turn off autosizing
37684     this.layout = config.layout;
37685     this.layout.getEl().addClass("roo-layout-nested-layout");
37686     
37687     
37688     
37689     
37690 };
37691
37692 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37693
37694     setSize : function(width, height){
37695         if(!this.ignoreResize(width, height)){
37696             var size = this.adjustForComponents(width, height);
37697             var el = this.layout.getEl();
37698             if (size.height < 1) {
37699                 el.setWidth(size.width);   
37700             } else {
37701                 el.setSize(size.width, size.height);
37702             }
37703             var touch = el.dom.offsetWidth;
37704             this.layout.layout();
37705             // ie requires a double layout on the first pass
37706             if(Roo.isIE && !this.initialized){
37707                 this.initialized = true;
37708                 this.layout.layout();
37709             }
37710         }
37711     },
37712     
37713     // activate all subpanels if not currently active..
37714     
37715     setActiveState : function(active){
37716         this.active = active;
37717         this.setActiveClass(active);
37718         
37719         if(!active){
37720             this.fireEvent("deactivate", this);
37721             return;
37722         }
37723         
37724         this.fireEvent("activate", this);
37725         // not sure if this should happen before or after..
37726         if (!this.layout) {
37727             return; // should not happen..
37728         }
37729         var reg = false;
37730         for (var r in this.layout.regions) {
37731             reg = this.layout.getRegion(r);
37732             if (reg.getActivePanel()) {
37733                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37734                 reg.setActivePanel(reg.getActivePanel());
37735                 continue;
37736             }
37737             if (!reg.panels.length) {
37738                 continue;
37739             }
37740             reg.showPanel(reg.getPanel(0));
37741         }
37742         
37743         
37744         
37745         
37746     },
37747     
37748     /**
37749      * Returns the nested BorderLayout for this panel
37750      * @return {Roo.BorderLayout} 
37751      */
37752     getLayout : function(){
37753         return this.layout;
37754     },
37755     
37756      /**
37757      * Adds a xtype elements to the layout of the nested panel
37758      * <pre><code>
37759
37760 panel.addxtype({
37761        xtype : 'ContentPanel',
37762        region: 'west',
37763        items: [ .... ]
37764    }
37765 );
37766
37767 panel.addxtype({
37768         xtype : 'NestedLayoutPanel',
37769         region: 'west',
37770         layout: {
37771            center: { },
37772            west: { }   
37773         },
37774         items : [ ... list of content panels or nested layout panels.. ]
37775    }
37776 );
37777 </code></pre>
37778      * @param {Object} cfg Xtype definition of item to add.
37779      */
37780     addxtype : function(cfg) {
37781         return this.layout.addxtype(cfg);
37782     
37783     }
37784 });        /*
37785  * Based on:
37786  * Ext JS Library 1.1.1
37787  * Copyright(c) 2006-2007, Ext JS, LLC.
37788  *
37789  * Originally Released Under LGPL - original licence link has changed is not relivant.
37790  *
37791  * Fork - LGPL
37792  * <script type="text/javascript">
37793  */
37794 /**
37795  * @class Roo.TabPanel
37796  * @extends Roo.util.Observable
37797  * A lightweight tab container.
37798  * <br><br>
37799  * Usage:
37800  * <pre><code>
37801 // basic tabs 1, built from existing content
37802 var tabs = new Roo.TabPanel("tabs1");
37803 tabs.addTab("script", "View Script");
37804 tabs.addTab("markup", "View Markup");
37805 tabs.activate("script");
37806
37807 // more advanced tabs, built from javascript
37808 var jtabs = new Roo.TabPanel("jtabs");
37809 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37810
37811 // set up the UpdateManager
37812 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37813 var updater = tab2.getUpdateManager();
37814 updater.setDefaultUrl("ajax1.htm");
37815 tab2.on('activate', updater.refresh, updater, true);
37816
37817 // Use setUrl for Ajax loading
37818 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37819 tab3.setUrl("ajax2.htm", null, true);
37820
37821 // Disabled tab
37822 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37823 tab4.disable();
37824
37825 jtabs.activate("jtabs-1");
37826  * </code></pre>
37827  * @constructor
37828  * Create a new TabPanel.
37829  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37830  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37831  */
37832 Roo.bootstrap.panel.Tabs = function(config){
37833     /**
37834     * The container element for this TabPanel.
37835     * @type Roo.Element
37836     */
37837     this.el = Roo.get(config.el);
37838     delete config.el;
37839     if(config){
37840         if(typeof config == "boolean"){
37841             this.tabPosition = config ? "bottom" : "top";
37842         }else{
37843             Roo.apply(this, config);
37844         }
37845     }
37846     
37847     if(this.tabPosition == "bottom"){
37848         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37849         this.el.addClass("roo-tabs-bottom");
37850     }
37851     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37852     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37853     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37854     if(Roo.isIE){
37855         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37856     }
37857     if(this.tabPosition != "bottom"){
37858         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37859          * @type Roo.Element
37860          */
37861         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37862         this.el.addClass("roo-tabs-top");
37863     }
37864     this.items = [];
37865
37866     this.bodyEl.setStyle("position", "relative");
37867
37868     this.active = null;
37869     this.activateDelegate = this.activate.createDelegate(this);
37870
37871     this.addEvents({
37872         /**
37873          * @event tabchange
37874          * Fires when the active tab changes
37875          * @param {Roo.TabPanel} this
37876          * @param {Roo.TabPanelItem} activePanel The new active tab
37877          */
37878         "tabchange": true,
37879         /**
37880          * @event beforetabchange
37881          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37882          * @param {Roo.TabPanel} this
37883          * @param {Object} e Set cancel to true on this object to cancel the tab change
37884          * @param {Roo.TabPanelItem} tab The tab being changed to
37885          */
37886         "beforetabchange" : true
37887     });
37888
37889     Roo.EventManager.onWindowResize(this.onResize, this);
37890     this.cpad = this.el.getPadding("lr");
37891     this.hiddenCount = 0;
37892
37893
37894     // toolbar on the tabbar support...
37895     if (this.toolbar) {
37896         alert("no toolbar support yet");
37897         this.toolbar  = false;
37898         /*
37899         var tcfg = this.toolbar;
37900         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37901         this.toolbar = new Roo.Toolbar(tcfg);
37902         if (Roo.isSafari) {
37903             var tbl = tcfg.container.child('table', true);
37904             tbl.setAttribute('width', '100%');
37905         }
37906         */
37907         
37908     }
37909    
37910
37911
37912     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37913 };
37914
37915 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37916     /*
37917      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37918      */
37919     tabPosition : "top",
37920     /*
37921      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37922      */
37923     currentTabWidth : 0,
37924     /*
37925      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37926      */
37927     minTabWidth : 40,
37928     /*
37929      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37930      */
37931     maxTabWidth : 250,
37932     /*
37933      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37934      */
37935     preferredTabWidth : 175,
37936     /*
37937      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37938      */
37939     resizeTabs : false,
37940     /*
37941      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37942      */
37943     monitorResize : true,
37944     /*
37945      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37946      */
37947     toolbar : false,
37948
37949     /**
37950      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37951      * @param {String} id The id of the div to use <b>or create</b>
37952      * @param {String} text The text for the tab
37953      * @param {String} content (optional) Content to put in the TabPanelItem body
37954      * @param {Boolean} closable (optional) True to create a close icon on the tab
37955      * @return {Roo.TabPanelItem} The created TabPanelItem
37956      */
37957     addTab : function(id, text, content, closable, tpl)
37958     {
37959         var item = new Roo.bootstrap.panel.TabItem({
37960             panel: this,
37961             id : id,
37962             text : text,
37963             closable : closable,
37964             tpl : tpl
37965         });
37966         this.addTabItem(item);
37967         if(content){
37968             item.setContent(content);
37969         }
37970         return item;
37971     },
37972
37973     /**
37974      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37975      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37976      * @return {Roo.TabPanelItem}
37977      */
37978     getTab : function(id){
37979         return this.items[id];
37980     },
37981
37982     /**
37983      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37984      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37985      */
37986     hideTab : function(id){
37987         var t = this.items[id];
37988         if(!t.isHidden()){
37989            t.setHidden(true);
37990            this.hiddenCount++;
37991            this.autoSizeTabs();
37992         }
37993     },
37994
37995     /**
37996      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37997      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37998      */
37999     unhideTab : function(id){
38000         var t = this.items[id];
38001         if(t.isHidden()){
38002            t.setHidden(false);
38003            this.hiddenCount--;
38004            this.autoSizeTabs();
38005         }
38006     },
38007
38008     /**
38009      * Adds an existing {@link Roo.TabPanelItem}.
38010      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38011      */
38012     addTabItem : function(item){
38013         this.items[item.id] = item;
38014         this.items.push(item);
38015       //  if(this.resizeTabs){
38016     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38017   //         this.autoSizeTabs();
38018 //        }else{
38019 //            item.autoSize();
38020        // }
38021     },
38022
38023     /**
38024      * Removes a {@link Roo.TabPanelItem}.
38025      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38026      */
38027     removeTab : function(id){
38028         var items = this.items;
38029         var tab = items[id];
38030         if(!tab) { return; }
38031         var index = items.indexOf(tab);
38032         if(this.active == tab && items.length > 1){
38033             var newTab = this.getNextAvailable(index);
38034             if(newTab) {
38035                 newTab.activate();
38036             }
38037         }
38038         this.stripEl.dom.removeChild(tab.pnode.dom);
38039         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38040             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38041         }
38042         items.splice(index, 1);
38043         delete this.items[tab.id];
38044         tab.fireEvent("close", tab);
38045         tab.purgeListeners();
38046         this.autoSizeTabs();
38047     },
38048
38049     getNextAvailable : function(start){
38050         var items = this.items;
38051         var index = start;
38052         // look for a next tab that will slide over to
38053         // replace the one being removed
38054         while(index < items.length){
38055             var item = items[++index];
38056             if(item && !item.isHidden()){
38057                 return item;
38058             }
38059         }
38060         // if one isn't found select the previous tab (on the left)
38061         index = start;
38062         while(index >= 0){
38063             var item = items[--index];
38064             if(item && !item.isHidden()){
38065                 return item;
38066             }
38067         }
38068         return null;
38069     },
38070
38071     /**
38072      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38073      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38074      */
38075     disableTab : function(id){
38076         var tab = this.items[id];
38077         if(tab && this.active != tab){
38078             tab.disable();
38079         }
38080     },
38081
38082     /**
38083      * Enables a {@link Roo.TabPanelItem} that is disabled.
38084      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38085      */
38086     enableTab : function(id){
38087         var tab = this.items[id];
38088         tab.enable();
38089     },
38090
38091     /**
38092      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38093      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38094      * @return {Roo.TabPanelItem} The TabPanelItem.
38095      */
38096     activate : function(id){
38097         var tab = this.items[id];
38098         if(!tab){
38099             return null;
38100         }
38101         if(tab == this.active || tab.disabled){
38102             return tab;
38103         }
38104         var e = {};
38105         this.fireEvent("beforetabchange", this, e, tab);
38106         if(e.cancel !== true && !tab.disabled){
38107             if(this.active){
38108                 this.active.hide();
38109             }
38110             this.active = this.items[id];
38111             this.active.show();
38112             this.fireEvent("tabchange", this, this.active);
38113         }
38114         return tab;
38115     },
38116
38117     /**
38118      * Gets the active {@link Roo.TabPanelItem}.
38119      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38120      */
38121     getActiveTab : function(){
38122         return this.active;
38123     },
38124
38125     /**
38126      * Updates the tab body element to fit the height of the container element
38127      * for overflow scrolling
38128      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38129      */
38130     syncHeight : function(targetHeight){
38131         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38132         var bm = this.bodyEl.getMargins();
38133         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38134         this.bodyEl.setHeight(newHeight);
38135         return newHeight;
38136     },
38137
38138     onResize : function(){
38139         if(this.monitorResize){
38140             this.autoSizeTabs();
38141         }
38142     },
38143
38144     /**
38145      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38146      */
38147     beginUpdate : function(){
38148         this.updating = true;
38149     },
38150
38151     /**
38152      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38153      */
38154     endUpdate : function(){
38155         this.updating = false;
38156         this.autoSizeTabs();
38157     },
38158
38159     /**
38160      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38161      */
38162     autoSizeTabs : function(){
38163         var count = this.items.length;
38164         var vcount = count - this.hiddenCount;
38165         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38166             return;
38167         }
38168         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38169         var availWidth = Math.floor(w / vcount);
38170         var b = this.stripBody;
38171         if(b.getWidth() > w){
38172             var tabs = this.items;
38173             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38174             if(availWidth < this.minTabWidth){
38175                 /*if(!this.sleft){    // incomplete scrolling code
38176                     this.createScrollButtons();
38177                 }
38178                 this.showScroll();
38179                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38180             }
38181         }else{
38182             if(this.currentTabWidth < this.preferredTabWidth){
38183                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38184             }
38185         }
38186     },
38187
38188     /**
38189      * Returns the number of tabs in this TabPanel.
38190      * @return {Number}
38191      */
38192      getCount : function(){
38193          return this.items.length;
38194      },
38195
38196     /**
38197      * Resizes all the tabs to the passed width
38198      * @param {Number} The new width
38199      */
38200     setTabWidth : function(width){
38201         this.currentTabWidth = width;
38202         for(var i = 0, len = this.items.length; i < len; i++) {
38203                 if(!this.items[i].isHidden()) {
38204                 this.items[i].setWidth(width);
38205             }
38206         }
38207     },
38208
38209     /**
38210      * Destroys this TabPanel
38211      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38212      */
38213     destroy : function(removeEl){
38214         Roo.EventManager.removeResizeListener(this.onResize, this);
38215         for(var i = 0, len = this.items.length; i < len; i++){
38216             this.items[i].purgeListeners();
38217         }
38218         if(removeEl === true){
38219             this.el.update("");
38220             this.el.remove();
38221         }
38222     },
38223     
38224     createStrip : function(container)
38225     {
38226         var strip = document.createElement("nav");
38227         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38228         container.appendChild(strip);
38229         return strip;
38230     },
38231     
38232     createStripList : function(strip)
38233     {
38234         // div wrapper for retard IE
38235         // returns the "tr" element.
38236         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38237         //'<div class="x-tabs-strip-wrap">'+
38238           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38239           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38240         return strip.firstChild; //.firstChild.firstChild.firstChild;
38241     },
38242     createBody : function(container)
38243     {
38244         var body = document.createElement("div");
38245         Roo.id(body, "tab-body");
38246         //Roo.fly(body).addClass("x-tabs-body");
38247         Roo.fly(body).addClass("tab-content");
38248         container.appendChild(body);
38249         return body;
38250     },
38251     createItemBody :function(bodyEl, id){
38252         var body = Roo.getDom(id);
38253         if(!body){
38254             body = document.createElement("div");
38255             body.id = id;
38256         }
38257         //Roo.fly(body).addClass("x-tabs-item-body");
38258         Roo.fly(body).addClass("tab-pane");
38259          bodyEl.insertBefore(body, bodyEl.firstChild);
38260         return body;
38261     },
38262     /** @private */
38263     createStripElements :  function(stripEl, text, closable, tpl)
38264     {
38265         var td = document.createElement("li"); // was td..
38266         
38267         
38268         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38269         
38270         
38271         stripEl.appendChild(td);
38272         /*if(closable){
38273             td.className = "x-tabs-closable";
38274             if(!this.closeTpl){
38275                 this.closeTpl = new Roo.Template(
38276                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38277                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38278                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38279                 );
38280             }
38281             var el = this.closeTpl.overwrite(td, {"text": text});
38282             var close = el.getElementsByTagName("div")[0];
38283             var inner = el.getElementsByTagName("em")[0];
38284             return {"el": el, "close": close, "inner": inner};
38285         } else {
38286         */
38287         // not sure what this is..
38288 //            if(!this.tabTpl){
38289                 //this.tabTpl = new Roo.Template(
38290                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38291                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38292                 //);
38293 //                this.tabTpl = new Roo.Template(
38294 //                   '<a href="#">' +
38295 //                   '<span unselectable="on"' +
38296 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38297 //                            ' >{text}</span></a>'
38298 //                );
38299 //                
38300 //            }
38301
38302
38303             var template = tpl || this.tabTpl || false;
38304             
38305             if(!template){
38306                 
38307                 template = new Roo.Template(
38308                    '<a href="#">' +
38309                    '<span unselectable="on"' +
38310                             (this.disableTooltips ? '' : ' title="{text}"') +
38311                             ' >{text}</span></a>'
38312                 );
38313             }
38314             
38315             switch (typeof(template)) {
38316                 case 'object' :
38317                     break;
38318                 case 'string' :
38319                     template = new Roo.Template(template);
38320                     break;
38321                 default :
38322                     break;
38323             }
38324             
38325             var el = template.overwrite(td, {"text": text});
38326             
38327             var inner = el.getElementsByTagName("span")[0];
38328             
38329             return {"el": el, "inner": inner};
38330             
38331     }
38332         
38333     
38334 });
38335
38336 /**
38337  * @class Roo.TabPanelItem
38338  * @extends Roo.util.Observable
38339  * Represents an individual item (tab plus body) in a TabPanel.
38340  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38341  * @param {String} id The id of this TabPanelItem
38342  * @param {String} text The text for the tab of this TabPanelItem
38343  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38344  */
38345 Roo.bootstrap.panel.TabItem = function(config){
38346     /**
38347      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38348      * @type Roo.TabPanel
38349      */
38350     this.tabPanel = config.panel;
38351     /**
38352      * The id for this TabPanelItem
38353      * @type String
38354      */
38355     this.id = config.id;
38356     /** @private */
38357     this.disabled = false;
38358     /** @private */
38359     this.text = config.text;
38360     /** @private */
38361     this.loaded = false;
38362     this.closable = config.closable;
38363
38364     /**
38365      * The body element for this TabPanelItem.
38366      * @type Roo.Element
38367      */
38368     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38369     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38370     this.bodyEl.setStyle("display", "block");
38371     this.bodyEl.setStyle("zoom", "1");
38372     //this.hideAction();
38373
38374     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38375     /** @private */
38376     this.el = Roo.get(els.el);
38377     this.inner = Roo.get(els.inner, true);
38378     this.textEl = Roo.get(this.el.dom.firstChild, true);
38379     this.pnode = Roo.get(els.el.parentNode, true);
38380 //    this.el.on("mousedown", this.onTabMouseDown, this);
38381     this.el.on("click", this.onTabClick, this);
38382     /** @private */
38383     if(config.closable){
38384         var c = Roo.get(els.close, true);
38385         c.dom.title = this.closeText;
38386         c.addClassOnOver("close-over");
38387         c.on("click", this.closeClick, this);
38388      }
38389
38390     this.addEvents({
38391          /**
38392          * @event activate
38393          * Fires when this tab becomes the active tab.
38394          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38395          * @param {Roo.TabPanelItem} this
38396          */
38397         "activate": true,
38398         /**
38399          * @event beforeclose
38400          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38401          * @param {Roo.TabPanelItem} this
38402          * @param {Object} e Set cancel to true on this object to cancel the close.
38403          */
38404         "beforeclose": true,
38405         /**
38406          * @event close
38407          * Fires when this tab is closed.
38408          * @param {Roo.TabPanelItem} this
38409          */
38410          "close": true,
38411         /**
38412          * @event deactivate
38413          * Fires when this tab is no longer the active tab.
38414          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38415          * @param {Roo.TabPanelItem} this
38416          */
38417          "deactivate" : true
38418     });
38419     this.hidden = false;
38420
38421     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38422 };
38423
38424 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38425            {
38426     purgeListeners : function(){
38427        Roo.util.Observable.prototype.purgeListeners.call(this);
38428        this.el.removeAllListeners();
38429     },
38430     /**
38431      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38432      */
38433     show : function(){
38434         this.pnode.addClass("active");
38435         this.showAction();
38436         if(Roo.isOpera){
38437             this.tabPanel.stripWrap.repaint();
38438         }
38439         this.fireEvent("activate", this.tabPanel, this);
38440     },
38441
38442     /**
38443      * Returns true if this tab is the active tab.
38444      * @return {Boolean}
38445      */
38446     isActive : function(){
38447         return this.tabPanel.getActiveTab() == this;
38448     },
38449
38450     /**
38451      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38452      */
38453     hide : function(){
38454         this.pnode.removeClass("active");
38455         this.hideAction();
38456         this.fireEvent("deactivate", this.tabPanel, this);
38457     },
38458
38459     hideAction : function(){
38460         this.bodyEl.hide();
38461         this.bodyEl.setStyle("position", "absolute");
38462         this.bodyEl.setLeft("-20000px");
38463         this.bodyEl.setTop("-20000px");
38464     },
38465
38466     showAction : function(){
38467         this.bodyEl.setStyle("position", "relative");
38468         this.bodyEl.setTop("");
38469         this.bodyEl.setLeft("");
38470         this.bodyEl.show();
38471     },
38472
38473     /**
38474      * Set the tooltip for the tab.
38475      * @param {String} tooltip The tab's tooltip
38476      */
38477     setTooltip : function(text){
38478         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38479             this.textEl.dom.qtip = text;
38480             this.textEl.dom.removeAttribute('title');
38481         }else{
38482             this.textEl.dom.title = text;
38483         }
38484     },
38485
38486     onTabClick : function(e){
38487         e.preventDefault();
38488         this.tabPanel.activate(this.id);
38489     },
38490
38491     onTabMouseDown : function(e){
38492         e.preventDefault();
38493         this.tabPanel.activate(this.id);
38494     },
38495 /*
38496     getWidth : function(){
38497         return this.inner.getWidth();
38498     },
38499
38500     setWidth : function(width){
38501         var iwidth = width - this.pnode.getPadding("lr");
38502         this.inner.setWidth(iwidth);
38503         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38504         this.pnode.setWidth(width);
38505     },
38506 */
38507     /**
38508      * Show or hide the tab
38509      * @param {Boolean} hidden True to hide or false to show.
38510      */
38511     setHidden : function(hidden){
38512         this.hidden = hidden;
38513         this.pnode.setStyle("display", hidden ? "none" : "");
38514     },
38515
38516     /**
38517      * Returns true if this tab is "hidden"
38518      * @return {Boolean}
38519      */
38520     isHidden : function(){
38521         return this.hidden;
38522     },
38523
38524     /**
38525      * Returns the text for this tab
38526      * @return {String}
38527      */
38528     getText : function(){
38529         return this.text;
38530     },
38531     /*
38532     autoSize : function(){
38533         //this.el.beginMeasure();
38534         this.textEl.setWidth(1);
38535         /*
38536          *  #2804 [new] Tabs in Roojs
38537          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38538          */
38539         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38540         //this.el.endMeasure();
38541     //},
38542
38543     /**
38544      * Sets the text for the tab (Note: this also sets the tooltip text)
38545      * @param {String} text The tab's text and tooltip
38546      */
38547     setText : function(text){
38548         this.text = text;
38549         this.textEl.update(text);
38550         this.setTooltip(text);
38551         //if(!this.tabPanel.resizeTabs){
38552         //    this.autoSize();
38553         //}
38554     },
38555     /**
38556      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38557      */
38558     activate : function(){
38559         this.tabPanel.activate(this.id);
38560     },
38561
38562     /**
38563      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38564      */
38565     disable : function(){
38566         if(this.tabPanel.active != this){
38567             this.disabled = true;
38568             this.pnode.addClass("disabled");
38569         }
38570     },
38571
38572     /**
38573      * Enables this TabPanelItem if it was previously disabled.
38574      */
38575     enable : function(){
38576         this.disabled = false;
38577         this.pnode.removeClass("disabled");
38578     },
38579
38580     /**
38581      * Sets the content for this TabPanelItem.
38582      * @param {String} content The content
38583      * @param {Boolean} loadScripts true to look for and load scripts
38584      */
38585     setContent : function(content, loadScripts){
38586         this.bodyEl.update(content, loadScripts);
38587     },
38588
38589     /**
38590      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38591      * @return {Roo.UpdateManager} The UpdateManager
38592      */
38593     getUpdateManager : function(){
38594         return this.bodyEl.getUpdateManager();
38595     },
38596
38597     /**
38598      * Set a URL to be used to load the content for this TabPanelItem.
38599      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38600      * @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)
38601      * @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)
38602      * @return {Roo.UpdateManager} The UpdateManager
38603      */
38604     setUrl : function(url, params, loadOnce){
38605         if(this.refreshDelegate){
38606             this.un('activate', this.refreshDelegate);
38607         }
38608         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38609         this.on("activate", this.refreshDelegate);
38610         return this.bodyEl.getUpdateManager();
38611     },
38612
38613     /** @private */
38614     _handleRefresh : function(url, params, loadOnce){
38615         if(!loadOnce || !this.loaded){
38616             var updater = this.bodyEl.getUpdateManager();
38617             updater.update(url, params, this._setLoaded.createDelegate(this));
38618         }
38619     },
38620
38621     /**
38622      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38623      *   Will fail silently if the setUrl method has not been called.
38624      *   This does not activate the panel, just updates its content.
38625      */
38626     refresh : function(){
38627         if(this.refreshDelegate){
38628            this.loaded = false;
38629            this.refreshDelegate();
38630         }
38631     },
38632
38633     /** @private */
38634     _setLoaded : function(){
38635         this.loaded = true;
38636     },
38637
38638     /** @private */
38639     closeClick : function(e){
38640         var o = {};
38641         e.stopEvent();
38642         this.fireEvent("beforeclose", this, o);
38643         if(o.cancel !== true){
38644             this.tabPanel.removeTab(this.id);
38645         }
38646     },
38647     /**
38648      * The text displayed in the tooltip for the close icon.
38649      * @type String
38650      */
38651     closeText : "Close this tab"
38652 });
38653 /**
38654 *    This script refer to:
38655 *    Title: International Telephone Input
38656 *    Author: Jack O'Connor
38657 *    Code version:  v12.1.12
38658 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38659 **/
38660
38661 Roo.bootstrap.PhoneInputData = function() {
38662     var d = [
38663       [
38664         "Afghanistan (‫افغانستان‬‎)",
38665         "af",
38666         "93"
38667       ],
38668       [
38669         "Albania (Shqipëri)",
38670         "al",
38671         "355"
38672       ],
38673       [
38674         "Algeria (‫الجزائر‬‎)",
38675         "dz",
38676         "213"
38677       ],
38678       [
38679         "American Samoa",
38680         "as",
38681         "1684"
38682       ],
38683       [
38684         "Andorra",
38685         "ad",
38686         "376"
38687       ],
38688       [
38689         "Angola",
38690         "ao",
38691         "244"
38692       ],
38693       [
38694         "Anguilla",
38695         "ai",
38696         "1264"
38697       ],
38698       [
38699         "Antigua and Barbuda",
38700         "ag",
38701         "1268"
38702       ],
38703       [
38704         "Argentina",
38705         "ar",
38706         "54"
38707       ],
38708       [
38709         "Armenia (Հայաստան)",
38710         "am",
38711         "374"
38712       ],
38713       [
38714         "Aruba",
38715         "aw",
38716         "297"
38717       ],
38718       [
38719         "Australia",
38720         "au",
38721         "61",
38722         0
38723       ],
38724       [
38725         "Austria (Österreich)",
38726         "at",
38727         "43"
38728       ],
38729       [
38730         "Azerbaijan (Azərbaycan)",
38731         "az",
38732         "994"
38733       ],
38734       [
38735         "Bahamas",
38736         "bs",
38737         "1242"
38738       ],
38739       [
38740         "Bahrain (‫البحرين‬‎)",
38741         "bh",
38742         "973"
38743       ],
38744       [
38745         "Bangladesh (বাংলাদেশ)",
38746         "bd",
38747         "880"
38748       ],
38749       [
38750         "Barbados",
38751         "bb",
38752         "1246"
38753       ],
38754       [
38755         "Belarus (Беларусь)",
38756         "by",
38757         "375"
38758       ],
38759       [
38760         "Belgium (België)",
38761         "be",
38762         "32"
38763       ],
38764       [
38765         "Belize",
38766         "bz",
38767         "501"
38768       ],
38769       [
38770         "Benin (Bénin)",
38771         "bj",
38772         "229"
38773       ],
38774       [
38775         "Bermuda",
38776         "bm",
38777         "1441"
38778       ],
38779       [
38780         "Bhutan (འབྲུག)",
38781         "bt",
38782         "975"
38783       ],
38784       [
38785         "Bolivia",
38786         "bo",
38787         "591"
38788       ],
38789       [
38790         "Bosnia and Herzegovina (Босна и Херцеговина)",
38791         "ba",
38792         "387"
38793       ],
38794       [
38795         "Botswana",
38796         "bw",
38797         "267"
38798       ],
38799       [
38800         "Brazil (Brasil)",
38801         "br",
38802         "55"
38803       ],
38804       [
38805         "British Indian Ocean Territory",
38806         "io",
38807         "246"
38808       ],
38809       [
38810         "British Virgin Islands",
38811         "vg",
38812         "1284"
38813       ],
38814       [
38815         "Brunei",
38816         "bn",
38817         "673"
38818       ],
38819       [
38820         "Bulgaria (България)",
38821         "bg",
38822         "359"
38823       ],
38824       [
38825         "Burkina Faso",
38826         "bf",
38827         "226"
38828       ],
38829       [
38830         "Burundi (Uburundi)",
38831         "bi",
38832         "257"
38833       ],
38834       [
38835         "Cambodia (កម្ពុជា)",
38836         "kh",
38837         "855"
38838       ],
38839       [
38840         "Cameroon (Cameroun)",
38841         "cm",
38842         "237"
38843       ],
38844       [
38845         "Canada",
38846         "ca",
38847         "1",
38848         1,
38849         ["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"]
38850       ],
38851       [
38852         "Cape Verde (Kabu Verdi)",
38853         "cv",
38854         "238"
38855       ],
38856       [
38857         "Caribbean Netherlands",
38858         "bq",
38859         "599",
38860         1
38861       ],
38862       [
38863         "Cayman Islands",
38864         "ky",
38865         "1345"
38866       ],
38867       [
38868         "Central African Republic (République centrafricaine)",
38869         "cf",
38870         "236"
38871       ],
38872       [
38873         "Chad (Tchad)",
38874         "td",
38875         "235"
38876       ],
38877       [
38878         "Chile",
38879         "cl",
38880         "56"
38881       ],
38882       [
38883         "China (中国)",
38884         "cn",
38885         "86"
38886       ],
38887       [
38888         "Christmas Island",
38889         "cx",
38890         "61",
38891         2
38892       ],
38893       [
38894         "Cocos (Keeling) Islands",
38895         "cc",
38896         "61",
38897         1
38898       ],
38899       [
38900         "Colombia",
38901         "co",
38902         "57"
38903       ],
38904       [
38905         "Comoros (‫جزر القمر‬‎)",
38906         "km",
38907         "269"
38908       ],
38909       [
38910         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38911         "cd",
38912         "243"
38913       ],
38914       [
38915         "Congo (Republic) (Congo-Brazzaville)",
38916         "cg",
38917         "242"
38918       ],
38919       [
38920         "Cook Islands",
38921         "ck",
38922         "682"
38923       ],
38924       [
38925         "Costa Rica",
38926         "cr",
38927         "506"
38928       ],
38929       [
38930         "Côte d’Ivoire",
38931         "ci",
38932         "225"
38933       ],
38934       [
38935         "Croatia (Hrvatska)",
38936         "hr",
38937         "385"
38938       ],
38939       [
38940         "Cuba",
38941         "cu",
38942         "53"
38943       ],
38944       [
38945         "Curaçao",
38946         "cw",
38947         "599",
38948         0
38949       ],
38950       [
38951         "Cyprus (Κύπρος)",
38952         "cy",
38953         "357"
38954       ],
38955       [
38956         "Czech Republic (Česká republika)",
38957         "cz",
38958         "420"
38959       ],
38960       [
38961         "Denmark (Danmark)",
38962         "dk",
38963         "45"
38964       ],
38965       [
38966         "Djibouti",
38967         "dj",
38968         "253"
38969       ],
38970       [
38971         "Dominica",
38972         "dm",
38973         "1767"
38974       ],
38975       [
38976         "Dominican Republic (República Dominicana)",
38977         "do",
38978         "1",
38979         2,
38980         ["809", "829", "849"]
38981       ],
38982       [
38983         "Ecuador",
38984         "ec",
38985         "593"
38986       ],
38987       [
38988         "Egypt (‫مصر‬‎)",
38989         "eg",
38990         "20"
38991       ],
38992       [
38993         "El Salvador",
38994         "sv",
38995         "503"
38996       ],
38997       [
38998         "Equatorial Guinea (Guinea Ecuatorial)",
38999         "gq",
39000         "240"
39001       ],
39002       [
39003         "Eritrea",
39004         "er",
39005         "291"
39006       ],
39007       [
39008         "Estonia (Eesti)",
39009         "ee",
39010         "372"
39011       ],
39012       [
39013         "Ethiopia",
39014         "et",
39015         "251"
39016       ],
39017       [
39018         "Falkland Islands (Islas Malvinas)",
39019         "fk",
39020         "500"
39021       ],
39022       [
39023         "Faroe Islands (Føroyar)",
39024         "fo",
39025         "298"
39026       ],
39027       [
39028         "Fiji",
39029         "fj",
39030         "679"
39031       ],
39032       [
39033         "Finland (Suomi)",
39034         "fi",
39035         "358",
39036         0
39037       ],
39038       [
39039         "France",
39040         "fr",
39041         "33"
39042       ],
39043       [
39044         "French Guiana (Guyane française)",
39045         "gf",
39046         "594"
39047       ],
39048       [
39049         "French Polynesia (Polynésie française)",
39050         "pf",
39051         "689"
39052       ],
39053       [
39054         "Gabon",
39055         "ga",
39056         "241"
39057       ],
39058       [
39059         "Gambia",
39060         "gm",
39061         "220"
39062       ],
39063       [
39064         "Georgia (საქართველო)",
39065         "ge",
39066         "995"
39067       ],
39068       [
39069         "Germany (Deutschland)",
39070         "de",
39071         "49"
39072       ],
39073       [
39074         "Ghana (Gaana)",
39075         "gh",
39076         "233"
39077       ],
39078       [
39079         "Gibraltar",
39080         "gi",
39081         "350"
39082       ],
39083       [
39084         "Greece (Ελλάδα)",
39085         "gr",
39086         "30"
39087       ],
39088       [
39089         "Greenland (Kalaallit Nunaat)",
39090         "gl",
39091         "299"
39092       ],
39093       [
39094         "Grenada",
39095         "gd",
39096         "1473"
39097       ],
39098       [
39099         "Guadeloupe",
39100         "gp",
39101         "590",
39102         0
39103       ],
39104       [
39105         "Guam",
39106         "gu",
39107         "1671"
39108       ],
39109       [
39110         "Guatemala",
39111         "gt",
39112         "502"
39113       ],
39114       [
39115         "Guernsey",
39116         "gg",
39117         "44",
39118         1
39119       ],
39120       [
39121         "Guinea (Guinée)",
39122         "gn",
39123         "224"
39124       ],
39125       [
39126         "Guinea-Bissau (Guiné Bissau)",
39127         "gw",
39128         "245"
39129       ],
39130       [
39131         "Guyana",
39132         "gy",
39133         "592"
39134       ],
39135       [
39136         "Haiti",
39137         "ht",
39138         "509"
39139       ],
39140       [
39141         "Honduras",
39142         "hn",
39143         "504"
39144       ],
39145       [
39146         "Hong Kong (香港)",
39147         "hk",
39148         "852"
39149       ],
39150       [
39151         "Hungary (Magyarország)",
39152         "hu",
39153         "36"
39154       ],
39155       [
39156         "Iceland (Ísland)",
39157         "is",
39158         "354"
39159       ],
39160       [
39161         "India (भारत)",
39162         "in",
39163         "91"
39164       ],
39165       [
39166         "Indonesia",
39167         "id",
39168         "62"
39169       ],
39170       [
39171         "Iran (‫ایران‬‎)",
39172         "ir",
39173         "98"
39174       ],
39175       [
39176         "Iraq (‫العراق‬‎)",
39177         "iq",
39178         "964"
39179       ],
39180       [
39181         "Ireland",
39182         "ie",
39183         "353"
39184       ],
39185       [
39186         "Isle of Man",
39187         "im",
39188         "44",
39189         2
39190       ],
39191       [
39192         "Israel (‫ישראל‬‎)",
39193         "il",
39194         "972"
39195       ],
39196       [
39197         "Italy (Italia)",
39198         "it",
39199         "39",
39200         0
39201       ],
39202       [
39203         "Jamaica",
39204         "jm",
39205         "1876"
39206       ],
39207       [
39208         "Japan (日本)",
39209         "jp",
39210         "81"
39211       ],
39212       [
39213         "Jersey",
39214         "je",
39215         "44",
39216         3
39217       ],
39218       [
39219         "Jordan (‫الأردن‬‎)",
39220         "jo",
39221         "962"
39222       ],
39223       [
39224         "Kazakhstan (Казахстан)",
39225         "kz",
39226         "7",
39227         1
39228       ],
39229       [
39230         "Kenya",
39231         "ke",
39232         "254"
39233       ],
39234       [
39235         "Kiribati",
39236         "ki",
39237         "686"
39238       ],
39239       [
39240         "Kosovo",
39241         "xk",
39242         "383"
39243       ],
39244       [
39245         "Kuwait (‫الكويت‬‎)",
39246         "kw",
39247         "965"
39248       ],
39249       [
39250         "Kyrgyzstan (Кыргызстан)",
39251         "kg",
39252         "996"
39253       ],
39254       [
39255         "Laos (ລາວ)",
39256         "la",
39257         "856"
39258       ],
39259       [
39260         "Latvia (Latvija)",
39261         "lv",
39262         "371"
39263       ],
39264       [
39265         "Lebanon (‫لبنان‬‎)",
39266         "lb",
39267         "961"
39268       ],
39269       [
39270         "Lesotho",
39271         "ls",
39272         "266"
39273       ],
39274       [
39275         "Liberia",
39276         "lr",
39277         "231"
39278       ],
39279       [
39280         "Libya (‫ليبيا‬‎)",
39281         "ly",
39282         "218"
39283       ],
39284       [
39285         "Liechtenstein",
39286         "li",
39287         "423"
39288       ],
39289       [
39290         "Lithuania (Lietuva)",
39291         "lt",
39292         "370"
39293       ],
39294       [
39295         "Luxembourg",
39296         "lu",
39297         "352"
39298       ],
39299       [
39300         "Macau (澳門)",
39301         "mo",
39302         "853"
39303       ],
39304       [
39305         "Macedonia (FYROM) (Македонија)",
39306         "mk",
39307         "389"
39308       ],
39309       [
39310         "Madagascar (Madagasikara)",
39311         "mg",
39312         "261"
39313       ],
39314       [
39315         "Malawi",
39316         "mw",
39317         "265"
39318       ],
39319       [
39320         "Malaysia",
39321         "my",
39322         "60"
39323       ],
39324       [
39325         "Maldives",
39326         "mv",
39327         "960"
39328       ],
39329       [
39330         "Mali",
39331         "ml",
39332         "223"
39333       ],
39334       [
39335         "Malta",
39336         "mt",
39337         "356"
39338       ],
39339       [
39340         "Marshall Islands",
39341         "mh",
39342         "692"
39343       ],
39344       [
39345         "Martinique",
39346         "mq",
39347         "596"
39348       ],
39349       [
39350         "Mauritania (‫موريتانيا‬‎)",
39351         "mr",
39352         "222"
39353       ],
39354       [
39355         "Mauritius (Moris)",
39356         "mu",
39357         "230"
39358       ],
39359       [
39360         "Mayotte",
39361         "yt",
39362         "262",
39363         1
39364       ],
39365       [
39366         "Mexico (México)",
39367         "mx",
39368         "52"
39369       ],
39370       [
39371         "Micronesia",
39372         "fm",
39373         "691"
39374       ],
39375       [
39376         "Moldova (Republica Moldova)",
39377         "md",
39378         "373"
39379       ],
39380       [
39381         "Monaco",
39382         "mc",
39383         "377"
39384       ],
39385       [
39386         "Mongolia (Монгол)",
39387         "mn",
39388         "976"
39389       ],
39390       [
39391         "Montenegro (Crna Gora)",
39392         "me",
39393         "382"
39394       ],
39395       [
39396         "Montserrat",
39397         "ms",
39398         "1664"
39399       ],
39400       [
39401         "Morocco (‫المغرب‬‎)",
39402         "ma",
39403         "212",
39404         0
39405       ],
39406       [
39407         "Mozambique (Moçambique)",
39408         "mz",
39409         "258"
39410       ],
39411       [
39412         "Myanmar (Burma) (မြန်မာ)",
39413         "mm",
39414         "95"
39415       ],
39416       [
39417         "Namibia (Namibië)",
39418         "na",
39419         "264"
39420       ],
39421       [
39422         "Nauru",
39423         "nr",
39424         "674"
39425       ],
39426       [
39427         "Nepal (नेपाल)",
39428         "np",
39429         "977"
39430       ],
39431       [
39432         "Netherlands (Nederland)",
39433         "nl",
39434         "31"
39435       ],
39436       [
39437         "New Caledonia (Nouvelle-Calédonie)",
39438         "nc",
39439         "687"
39440       ],
39441       [
39442         "New Zealand",
39443         "nz",
39444         "64"
39445       ],
39446       [
39447         "Nicaragua",
39448         "ni",
39449         "505"
39450       ],
39451       [
39452         "Niger (Nijar)",
39453         "ne",
39454         "227"
39455       ],
39456       [
39457         "Nigeria",
39458         "ng",
39459         "234"
39460       ],
39461       [
39462         "Niue",
39463         "nu",
39464         "683"
39465       ],
39466       [
39467         "Norfolk Island",
39468         "nf",
39469         "672"
39470       ],
39471       [
39472         "North Korea (조선 민주주의 인민 공화국)",
39473         "kp",
39474         "850"
39475       ],
39476       [
39477         "Northern Mariana Islands",
39478         "mp",
39479         "1670"
39480       ],
39481       [
39482         "Norway (Norge)",
39483         "no",
39484         "47",
39485         0
39486       ],
39487       [
39488         "Oman (‫عُمان‬‎)",
39489         "om",
39490         "968"
39491       ],
39492       [
39493         "Pakistan (‫پاکستان‬‎)",
39494         "pk",
39495         "92"
39496       ],
39497       [
39498         "Palau",
39499         "pw",
39500         "680"
39501       ],
39502       [
39503         "Palestine (‫فلسطين‬‎)",
39504         "ps",
39505         "970"
39506       ],
39507       [
39508         "Panama (Panamá)",
39509         "pa",
39510         "507"
39511       ],
39512       [
39513         "Papua New Guinea",
39514         "pg",
39515         "675"
39516       ],
39517       [
39518         "Paraguay",
39519         "py",
39520         "595"
39521       ],
39522       [
39523         "Peru (Perú)",
39524         "pe",
39525         "51"
39526       ],
39527       [
39528         "Philippines",
39529         "ph",
39530         "63"
39531       ],
39532       [
39533         "Poland (Polska)",
39534         "pl",
39535         "48"
39536       ],
39537       [
39538         "Portugal",
39539         "pt",
39540         "351"
39541       ],
39542       [
39543         "Puerto Rico",
39544         "pr",
39545         "1",
39546         3,
39547         ["787", "939"]
39548       ],
39549       [
39550         "Qatar (‫قطر‬‎)",
39551         "qa",
39552         "974"
39553       ],
39554       [
39555         "Réunion (La Réunion)",
39556         "re",
39557         "262",
39558         0
39559       ],
39560       [
39561         "Romania (România)",
39562         "ro",
39563         "40"
39564       ],
39565       [
39566         "Russia (Россия)",
39567         "ru",
39568         "7",
39569         0
39570       ],
39571       [
39572         "Rwanda",
39573         "rw",
39574         "250"
39575       ],
39576       [
39577         "Saint Barthélemy",
39578         "bl",
39579         "590",
39580         1
39581       ],
39582       [
39583         "Saint Helena",
39584         "sh",
39585         "290"
39586       ],
39587       [
39588         "Saint Kitts and Nevis",
39589         "kn",
39590         "1869"
39591       ],
39592       [
39593         "Saint Lucia",
39594         "lc",
39595         "1758"
39596       ],
39597       [
39598         "Saint Martin (Saint-Martin (partie française))",
39599         "mf",
39600         "590",
39601         2
39602       ],
39603       [
39604         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39605         "pm",
39606         "508"
39607       ],
39608       [
39609         "Saint Vincent and the Grenadines",
39610         "vc",
39611         "1784"
39612       ],
39613       [
39614         "Samoa",
39615         "ws",
39616         "685"
39617       ],
39618       [
39619         "San Marino",
39620         "sm",
39621         "378"
39622       ],
39623       [
39624         "São Tomé and Príncipe (São Tomé e Príncipe)",
39625         "st",
39626         "239"
39627       ],
39628       [
39629         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39630         "sa",
39631         "966"
39632       ],
39633       [
39634         "Senegal (Sénégal)",
39635         "sn",
39636         "221"
39637       ],
39638       [
39639         "Serbia (Србија)",
39640         "rs",
39641         "381"
39642       ],
39643       [
39644         "Seychelles",
39645         "sc",
39646         "248"
39647       ],
39648       [
39649         "Sierra Leone",
39650         "sl",
39651         "232"
39652       ],
39653       [
39654         "Singapore",
39655         "sg",
39656         "65"
39657       ],
39658       [
39659         "Sint Maarten",
39660         "sx",
39661         "1721"
39662       ],
39663       [
39664         "Slovakia (Slovensko)",
39665         "sk",
39666         "421"
39667       ],
39668       [
39669         "Slovenia (Slovenija)",
39670         "si",
39671         "386"
39672       ],
39673       [
39674         "Solomon Islands",
39675         "sb",
39676         "677"
39677       ],
39678       [
39679         "Somalia (Soomaaliya)",
39680         "so",
39681         "252"
39682       ],
39683       [
39684         "South Africa",
39685         "za",
39686         "27"
39687       ],
39688       [
39689         "South Korea (대한민국)",
39690         "kr",
39691         "82"
39692       ],
39693       [
39694         "South Sudan (‫جنوب السودان‬‎)",
39695         "ss",
39696         "211"
39697       ],
39698       [
39699         "Spain (España)",
39700         "es",
39701         "34"
39702       ],
39703       [
39704         "Sri Lanka (ශ්‍රී ලංකාව)",
39705         "lk",
39706         "94"
39707       ],
39708       [
39709         "Sudan (‫السودان‬‎)",
39710         "sd",
39711         "249"
39712       ],
39713       [
39714         "Suriname",
39715         "sr",
39716         "597"
39717       ],
39718       [
39719         "Svalbard and Jan Mayen",
39720         "sj",
39721         "47",
39722         1
39723       ],
39724       [
39725         "Swaziland",
39726         "sz",
39727         "268"
39728       ],
39729       [
39730         "Sweden (Sverige)",
39731         "se",
39732         "46"
39733       ],
39734       [
39735         "Switzerland (Schweiz)",
39736         "ch",
39737         "41"
39738       ],
39739       [
39740         "Syria (‫سوريا‬‎)",
39741         "sy",
39742         "963"
39743       ],
39744       [
39745         "Taiwan (台灣)",
39746         "tw",
39747         "886"
39748       ],
39749       [
39750         "Tajikistan",
39751         "tj",
39752         "992"
39753       ],
39754       [
39755         "Tanzania",
39756         "tz",
39757         "255"
39758       ],
39759       [
39760         "Thailand (ไทย)",
39761         "th",
39762         "66"
39763       ],
39764       [
39765         "Timor-Leste",
39766         "tl",
39767         "670"
39768       ],
39769       [
39770         "Togo",
39771         "tg",
39772         "228"
39773       ],
39774       [
39775         "Tokelau",
39776         "tk",
39777         "690"
39778       ],
39779       [
39780         "Tonga",
39781         "to",
39782         "676"
39783       ],
39784       [
39785         "Trinidad and Tobago",
39786         "tt",
39787         "1868"
39788       ],
39789       [
39790         "Tunisia (‫تونس‬‎)",
39791         "tn",
39792         "216"
39793       ],
39794       [
39795         "Turkey (Türkiye)",
39796         "tr",
39797         "90"
39798       ],
39799       [
39800         "Turkmenistan",
39801         "tm",
39802         "993"
39803       ],
39804       [
39805         "Turks and Caicos Islands",
39806         "tc",
39807         "1649"
39808       ],
39809       [
39810         "Tuvalu",
39811         "tv",
39812         "688"
39813       ],
39814       [
39815         "U.S. Virgin Islands",
39816         "vi",
39817         "1340"
39818       ],
39819       [
39820         "Uganda",
39821         "ug",
39822         "256"
39823       ],
39824       [
39825         "Ukraine (Україна)",
39826         "ua",
39827         "380"
39828       ],
39829       [
39830         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39831         "ae",
39832         "971"
39833       ],
39834       [
39835         "United Kingdom",
39836         "gb",
39837         "44",
39838         0
39839       ],
39840       [
39841         "United States",
39842         "us",
39843         "1",
39844         0
39845       ],
39846       [
39847         "Uruguay",
39848         "uy",
39849         "598"
39850       ],
39851       [
39852         "Uzbekistan (Oʻzbekiston)",
39853         "uz",
39854         "998"
39855       ],
39856       [
39857         "Vanuatu",
39858         "vu",
39859         "678"
39860       ],
39861       [
39862         "Vatican City (Città del Vaticano)",
39863         "va",
39864         "39",
39865         1
39866       ],
39867       [
39868         "Venezuela",
39869         "ve",
39870         "58"
39871       ],
39872       [
39873         "Vietnam (Việt Nam)",
39874         "vn",
39875         "84"
39876       ],
39877       [
39878         "Wallis and Futuna (Wallis-et-Futuna)",
39879         "wf",
39880         "681"
39881       ],
39882       [
39883         "Western Sahara (‫الصحراء الغربية‬‎)",
39884         "eh",
39885         "212",
39886         1
39887       ],
39888       [
39889         "Yemen (‫اليمن‬‎)",
39890         "ye",
39891         "967"
39892       ],
39893       [
39894         "Zambia",
39895         "zm",
39896         "260"
39897       ],
39898       [
39899         "Zimbabwe",
39900         "zw",
39901         "263"
39902       ],
39903       [
39904         "Åland Islands",
39905         "ax",
39906         "358",
39907         1
39908       ]
39909   ];
39910   
39911   return d;
39912 }/**
39913 *    This script refer to:
39914 *    Title: International Telephone Input
39915 *    Author: Jack O'Connor
39916 *    Code version:  v12.1.12
39917 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39918 **/
39919
39920 /**
39921  * @class Roo.bootstrap.PhoneInput
39922  * @extends Roo.bootstrap.TriggerField
39923  * An input with International dial-code selection
39924  
39925  * @cfg {String} defaultDialCode default '+852'
39926  * @cfg {Array} preferedCountries default []
39927   
39928  * @constructor
39929  * Create a new PhoneInput.
39930  * @param {Object} config Configuration options
39931  */
39932
39933 Roo.bootstrap.PhoneInput = function(config) {
39934     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39935 };
39936
39937 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39938         
39939         listWidth: undefined,
39940         
39941         selectedClass: 'active',
39942         
39943         invalidClass : "has-warning",
39944         
39945         validClass: 'has-success',
39946         
39947         allowed: '0123456789',
39948         
39949         max_length: 15,
39950         
39951         /**
39952          * @cfg {String} defaultDialCode The default dial code when initializing the input
39953          */
39954         defaultDialCode: '+852',
39955         
39956         /**
39957          * @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
39958          */
39959         preferedCountries: false,
39960         
39961         getAutoCreate : function()
39962         {
39963             var data = Roo.bootstrap.PhoneInputData();
39964             var align = this.labelAlign || this.parentLabelAlign();
39965             var id = Roo.id();
39966             
39967             this.allCountries = [];
39968             this.dialCodeMapping = [];
39969             
39970             for (var i = 0; i < data.length; i++) {
39971               var c = data[i];
39972               this.allCountries[i] = {
39973                 name: c[0],
39974                 iso2: c[1],
39975                 dialCode: c[2],
39976                 priority: c[3] || 0,
39977                 areaCodes: c[4] || null
39978               };
39979               this.dialCodeMapping[c[2]] = {
39980                   name: c[0],
39981                   iso2: c[1],
39982                   priority: c[3] || 0,
39983                   areaCodes: c[4] || null
39984               };
39985             }
39986             
39987             var cfg = {
39988                 cls: 'form-group',
39989                 cn: []
39990             };
39991             
39992             var input =  {
39993                 tag: 'input',
39994                 id : id,
39995                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39996                 maxlength: this.max_length,
39997                 cls : 'form-control tel-input',
39998                 autocomplete: 'new-password'
39999             };
40000             
40001             var hiddenInput = {
40002                 tag: 'input',
40003                 type: 'hidden',
40004                 cls: 'hidden-tel-input'
40005             };
40006             
40007             if (this.name) {
40008                 hiddenInput.name = this.name;
40009             }
40010             
40011             if (this.disabled) {
40012                 input.disabled = true;
40013             }
40014             
40015             var flag_container = {
40016                 tag: 'div',
40017                 cls: 'flag-box',
40018                 cn: [
40019                     {
40020                         tag: 'div',
40021                         cls: 'flag'
40022                     },
40023                     {
40024                         tag: 'div',
40025                         cls: 'caret'
40026                     }
40027                 ]
40028             };
40029             
40030             var box = {
40031                 tag: 'div',
40032                 cls: this.hasFeedback ? 'has-feedback' : '',
40033                 cn: [
40034                     hiddenInput,
40035                     input,
40036                     {
40037                         tag: 'input',
40038                         cls: 'dial-code-holder',
40039                         disabled: true
40040                     }
40041                 ]
40042             };
40043             
40044             var container = {
40045                 cls: 'roo-select2-container input-group',
40046                 cn: [
40047                     flag_container,
40048                     box
40049                 ]
40050             };
40051             
40052             if (this.fieldLabel.length) {
40053                 var indicator = {
40054                     tag: 'i',
40055                     tooltip: 'This field is required'
40056                 };
40057                 
40058                 var label = {
40059                     tag: 'label',
40060                     'for':  id,
40061                     cls: 'control-label',
40062                     cn: []
40063                 };
40064                 
40065                 var label_text = {
40066                     tag: 'span',
40067                     html: this.fieldLabel
40068                 };
40069                 
40070                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40071                 label.cn = [
40072                     indicator,
40073                     label_text
40074                 ];
40075                 
40076                 if(this.indicatorpos == 'right') {
40077                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40078                     label.cn = [
40079                         label_text,
40080                         indicator
40081                     ];
40082                 }
40083                 
40084                 if(align == 'left') {
40085                     container = {
40086                         tag: 'div',
40087                         cn: [
40088                             container
40089                         ]
40090                     };
40091                     
40092                     if(this.labelWidth > 12){
40093                         label.style = "width: " + this.labelWidth + 'px';
40094                     }
40095                     if(this.labelWidth < 13 && this.labelmd == 0){
40096                         this.labelmd = this.labelWidth;
40097                     }
40098                     if(this.labellg > 0){
40099                         label.cls += ' col-lg-' + this.labellg;
40100                         input.cls += ' col-lg-' + (12 - this.labellg);
40101                     }
40102                     if(this.labelmd > 0){
40103                         label.cls += ' col-md-' + this.labelmd;
40104                         container.cls += ' col-md-' + (12 - this.labelmd);
40105                     }
40106                     if(this.labelsm > 0){
40107                         label.cls += ' col-sm-' + this.labelsm;
40108                         container.cls += ' col-sm-' + (12 - this.labelsm);
40109                     }
40110                     if(this.labelxs > 0){
40111                         label.cls += ' col-xs-' + this.labelxs;
40112                         container.cls += ' col-xs-' + (12 - this.labelxs);
40113                     }
40114                 }
40115             }
40116             
40117             cfg.cn = [
40118                 label,
40119                 container
40120             ];
40121             
40122             var settings = this;
40123             
40124             ['xs','sm','md','lg'].map(function(size){
40125                 if (settings[size]) {
40126                     cfg.cls += ' col-' + size + '-' + settings[size];
40127                 }
40128             });
40129             
40130             this.store = new Roo.data.Store({
40131                 proxy : new Roo.data.MemoryProxy({}),
40132                 reader : new Roo.data.JsonReader({
40133                     fields : [
40134                         {
40135                             'name' : 'name',
40136                             'type' : 'string'
40137                         },
40138                         {
40139                             'name' : 'iso2',
40140                             'type' : 'string'
40141                         },
40142                         {
40143                             'name' : 'dialCode',
40144                             'type' : 'string'
40145                         },
40146                         {
40147                             'name' : 'priority',
40148                             'type' : 'string'
40149                         },
40150                         {
40151                             'name' : 'areaCodes',
40152                             'type' : 'string'
40153                         }
40154                     ]
40155                 })
40156             });
40157             
40158             if(!this.preferedCountries) {
40159                 this.preferedCountries = [
40160                     'hk',
40161                     'gb',
40162                     'us'
40163                 ];
40164             }
40165             
40166             var p = this.preferedCountries.reverse();
40167             
40168             if(p) {
40169                 for (var i = 0; i < p.length; i++) {
40170                     for (var j = 0; j < this.allCountries.length; j++) {
40171                         if(this.allCountries[j].iso2 == p[i]) {
40172                             var t = this.allCountries[j];
40173                             this.allCountries.splice(j,1);
40174                             this.allCountries.unshift(t);
40175                         }
40176                     } 
40177                 }
40178             }
40179             
40180             this.store.proxy.data = {
40181                 success: true,
40182                 data: this.allCountries
40183             };
40184             
40185             return cfg;
40186         },
40187         
40188         initEvents : function()
40189         {
40190             this.createList();
40191             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40192             
40193             this.indicator = this.indicatorEl();
40194             this.flag = this.flagEl();
40195             this.dialCodeHolder = this.dialCodeHolderEl();
40196             
40197             this.trigger = this.el.select('div.flag-box',true).first();
40198             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40199             
40200             var _this = this;
40201             
40202             (function(){
40203                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40204                 _this.list.setWidth(lw);
40205             }).defer(100);
40206             
40207             this.list.on('mouseover', this.onViewOver, this);
40208             this.list.on('mousemove', this.onViewMove, this);
40209             this.inputEl().on("keyup", this.onKeyUp, this);
40210             this.inputEl().on("keypress", this.onKeyPress, this);
40211             
40212             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40213
40214             this.view = new Roo.View(this.list, this.tpl, {
40215                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40216             });
40217             
40218             this.view.on('click', this.onViewClick, this);
40219             this.setValue(this.defaultDialCode);
40220         },
40221         
40222         onTriggerClick : function(e)
40223         {
40224             Roo.log('trigger click');
40225             if(this.disabled){
40226                 return;
40227             }
40228             
40229             if(this.isExpanded()){
40230                 this.collapse();
40231                 this.hasFocus = false;
40232             }else {
40233                 this.store.load({});
40234                 this.hasFocus = true;
40235                 this.expand();
40236             }
40237         },
40238         
40239         isExpanded : function()
40240         {
40241             return this.list.isVisible();
40242         },
40243         
40244         collapse : function()
40245         {
40246             if(!this.isExpanded()){
40247                 return;
40248             }
40249             this.list.hide();
40250             Roo.get(document).un('mousedown', this.collapseIf, this);
40251             Roo.get(document).un('mousewheel', this.collapseIf, this);
40252             this.fireEvent('collapse', this);
40253             this.validate();
40254         },
40255         
40256         expand : function()
40257         {
40258             Roo.log('expand');
40259
40260             if(this.isExpanded() || !this.hasFocus){
40261                 return;
40262             }
40263             
40264             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40265             this.list.setWidth(lw);
40266             
40267             this.list.show();
40268             this.restrictHeight();
40269             
40270             Roo.get(document).on('mousedown', this.collapseIf, this);
40271             Roo.get(document).on('mousewheel', this.collapseIf, this);
40272             
40273             this.fireEvent('expand', this);
40274         },
40275         
40276         restrictHeight : function()
40277         {
40278             this.list.alignTo(this.inputEl(), this.listAlign);
40279             this.list.alignTo(this.inputEl(), this.listAlign);
40280         },
40281         
40282         onViewOver : function(e, t)
40283         {
40284             if(this.inKeyMode){
40285                 return;
40286             }
40287             var item = this.view.findItemFromChild(t);
40288             
40289             if(item){
40290                 var index = this.view.indexOf(item);
40291                 this.select(index, false);
40292             }
40293         },
40294
40295         // private
40296         onViewClick : function(view, doFocus, el, e)
40297         {
40298             var index = this.view.getSelectedIndexes()[0];
40299             
40300             var r = this.store.getAt(index);
40301             
40302             if(r){
40303                 this.onSelect(r, index);
40304             }
40305             if(doFocus !== false && !this.blockFocus){
40306                 this.inputEl().focus();
40307             }
40308         },
40309         
40310         onViewMove : function(e, t)
40311         {
40312             this.inKeyMode = false;
40313         },
40314         
40315         select : function(index, scrollIntoView)
40316         {
40317             this.selectedIndex = index;
40318             this.view.select(index);
40319             if(scrollIntoView !== false){
40320                 var el = this.view.getNode(index);
40321                 if(el){
40322                     this.list.scrollChildIntoView(el, false);
40323                 }
40324             }
40325         },
40326         
40327         createList : function()
40328         {
40329             this.list = Roo.get(document.body).createChild({
40330                 tag: 'ul',
40331                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40332                 style: 'display:none'
40333             });
40334             
40335             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40336         },
40337         
40338         collapseIf : function(e)
40339         {
40340             var in_combo  = e.within(this.el);
40341             var in_list =  e.within(this.list);
40342             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40343             
40344             if (in_combo || in_list || is_list) {
40345                 return;
40346             }
40347             this.collapse();
40348         },
40349         
40350         onSelect : function(record, index)
40351         {
40352             if(this.fireEvent('beforeselect', this, record, index) !== false){
40353                 
40354                 this.setFlagClass(record.data.iso2);
40355                 this.setDialCode(record.data.dialCode);
40356                 this.hasFocus = false;
40357                 this.collapse();
40358                 this.fireEvent('select', this, record, index);
40359             }
40360         },
40361         
40362         flagEl : function()
40363         {
40364             var flag = this.el.select('div.flag',true).first();
40365             if(!flag){
40366                 return false;
40367             }
40368             return flag;
40369         },
40370         
40371         dialCodeHolderEl : function()
40372         {
40373             var d = this.el.select('input.dial-code-holder',true).first();
40374             if(!d){
40375                 return false;
40376             }
40377             return d;
40378         },
40379         
40380         setDialCode : function(v)
40381         {
40382             this.dialCodeHolder.dom.value = '+'+v;
40383         },
40384         
40385         setFlagClass : function(n)
40386         {
40387             this.flag.dom.className = 'flag '+n;
40388         },
40389         
40390         getValue : function()
40391         {
40392             var v = this.inputEl().getValue();
40393             if(this.dialCodeHolder) {
40394                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40395             }
40396             return v;
40397         },
40398         
40399         setValue : function(v)
40400         {
40401             var d = this.getDialCode(v);
40402             
40403             //invalid dial code
40404             if(v.length == 0 || !d || d.length == 0) {
40405                 if(this.rendered){
40406                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40407                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40408                 }
40409                 return;
40410             }
40411             
40412             //valid dial code
40413             this.setFlagClass(this.dialCodeMapping[d].iso2);
40414             this.setDialCode(d);
40415             this.inputEl().dom.value = v.replace('+'+d,'');
40416             this.hiddenEl().dom.value = this.getValue();
40417             
40418             this.validate();
40419         },
40420         
40421         getDialCode : function(v)
40422         {
40423             v = v ||  '';
40424             
40425             if (v.length == 0) {
40426                 return this.dialCodeHolder.dom.value;
40427             }
40428             
40429             var dialCode = "";
40430             if (v.charAt(0) != "+") {
40431                 return false;
40432             }
40433             var numericChars = "";
40434             for (var i = 1; i < v.length; i++) {
40435               var c = v.charAt(i);
40436               if (!isNaN(c)) {
40437                 numericChars += c;
40438                 if (this.dialCodeMapping[numericChars]) {
40439                   dialCode = v.substr(1, i);
40440                 }
40441                 if (numericChars.length == 4) {
40442                   break;
40443                 }
40444               }
40445             }
40446             return dialCode;
40447         },
40448         
40449         reset : function()
40450         {
40451             this.setValue(this.defaultDialCode);
40452             this.validate();
40453         },
40454         
40455         hiddenEl : function()
40456         {
40457             return this.el.select('input.hidden-tel-input',true).first();
40458         },
40459         
40460         // after setting val
40461         onKeyUp : function(e){
40462             this.setValue(this.getValue());
40463         },
40464         
40465         onKeyPress : function(e){
40466             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40467                 e.stopEvent();
40468             }
40469         }
40470         
40471 });
40472 /**
40473  * @class Roo.bootstrap.MoneyField
40474  * @extends Roo.bootstrap.ComboBox
40475  * Bootstrap MoneyField class
40476  * 
40477  * @constructor
40478  * Create a new MoneyField.
40479  * @param {Object} config Configuration options
40480  */
40481
40482 Roo.bootstrap.MoneyField = function(config) {
40483     
40484     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40485     
40486 };
40487
40488 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40489     
40490     /**
40491      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40492      */
40493     allowDecimals : true,
40494     /**
40495      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40496      */
40497     decimalSeparator : ".",
40498     /**
40499      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40500      */
40501     decimalPrecision : 0,
40502     /**
40503      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40504      */
40505     allowNegative : true,
40506     /**
40507      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40508      */
40509     allowZero: true,
40510     /**
40511      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40512      */
40513     minValue : Number.NEGATIVE_INFINITY,
40514     /**
40515      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40516      */
40517     maxValue : Number.MAX_VALUE,
40518     /**
40519      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40520      */
40521     minText : "The minimum value for this field is {0}",
40522     /**
40523      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40524      */
40525     maxText : "The maximum value for this field is {0}",
40526     /**
40527      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40528      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40529      */
40530     nanText : "{0} is not a valid number",
40531     /**
40532      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40533      */
40534     castInt : true,
40535     /**
40536      * @cfg {String} defaults currency of the MoneyField
40537      * value should be in lkey
40538      */
40539     defaultCurrency : false,
40540     /**
40541      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40542      */
40543     thousandsDelimiter : false,
40544     /**
40545      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40546      */
40547     max_length: false,
40548     
40549     inputlg : 9,
40550     inputmd : 9,
40551     inputsm : 9,
40552     inputxs : 6,
40553     
40554     store : false,
40555     
40556     getAutoCreate : function()
40557     {
40558         var align = this.labelAlign || this.parentLabelAlign();
40559         
40560         var id = Roo.id();
40561
40562         var cfg = {
40563             cls: 'form-group',
40564             cn: []
40565         };
40566
40567         var input =  {
40568             tag: 'input',
40569             id : id,
40570             cls : 'form-control roo-money-amount-input',
40571             autocomplete: 'new-password'
40572         };
40573         
40574         var hiddenInput = {
40575             tag: 'input',
40576             type: 'hidden',
40577             id: Roo.id(),
40578             cls: 'hidden-number-input'
40579         };
40580         
40581         if(this.max_length) {
40582             input.maxlength = this.max_length; 
40583         }
40584         
40585         if (this.name) {
40586             hiddenInput.name = this.name;
40587         }
40588
40589         if (this.disabled) {
40590             input.disabled = true;
40591         }
40592
40593         var clg = 12 - this.inputlg;
40594         var cmd = 12 - this.inputmd;
40595         var csm = 12 - this.inputsm;
40596         var cxs = 12 - this.inputxs;
40597         
40598         var container = {
40599             tag : 'div',
40600             cls : 'row roo-money-field',
40601             cn : [
40602                 {
40603                     tag : 'div',
40604                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40605                     cn : [
40606                         {
40607                             tag : 'div',
40608                             cls: 'roo-select2-container input-group',
40609                             cn: [
40610                                 {
40611                                     tag : 'input',
40612                                     cls : 'form-control roo-money-currency-input',
40613                                     autocomplete: 'new-password',
40614                                     readOnly : 1,
40615                                     name : this.currencyName
40616                                 },
40617                                 {
40618                                     tag :'span',
40619                                     cls : 'input-group-addon',
40620                                     cn : [
40621                                         {
40622                                             tag: 'span',
40623                                             cls: 'caret'
40624                                         }
40625                                     ]
40626                                 }
40627                             ]
40628                         }
40629                     ]
40630                 },
40631                 {
40632                     tag : 'div',
40633                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40634                     cn : [
40635                         {
40636                             tag: 'div',
40637                             cls: this.hasFeedback ? 'has-feedback' : '',
40638                             cn: [
40639                                 input
40640                             ]
40641                         }
40642                     ]
40643                 }
40644             ]
40645             
40646         };
40647         
40648         if (this.fieldLabel.length) {
40649             var indicator = {
40650                 tag: 'i',
40651                 tooltip: 'This field is required'
40652             };
40653
40654             var label = {
40655                 tag: 'label',
40656                 'for':  id,
40657                 cls: 'control-label',
40658                 cn: []
40659             };
40660
40661             var label_text = {
40662                 tag: 'span',
40663                 html: this.fieldLabel
40664             };
40665
40666             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40667             label.cn = [
40668                 indicator,
40669                 label_text
40670             ];
40671
40672             if(this.indicatorpos == 'right') {
40673                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40674                 label.cn = [
40675                     label_text,
40676                     indicator
40677                 ];
40678             }
40679
40680             if(align == 'left') {
40681                 container = {
40682                     tag: 'div',
40683                     cn: [
40684                         container
40685                     ]
40686                 };
40687
40688                 if(this.labelWidth > 12){
40689                     label.style = "width: " + this.labelWidth + 'px';
40690                 }
40691                 if(this.labelWidth < 13 && this.labelmd == 0){
40692                     this.labelmd = this.labelWidth;
40693                 }
40694                 if(this.labellg > 0){
40695                     label.cls += ' col-lg-' + this.labellg;
40696                     input.cls += ' col-lg-' + (12 - this.labellg);
40697                 }
40698                 if(this.labelmd > 0){
40699                     label.cls += ' col-md-' + this.labelmd;
40700                     container.cls += ' col-md-' + (12 - this.labelmd);
40701                 }
40702                 if(this.labelsm > 0){
40703                     label.cls += ' col-sm-' + this.labelsm;
40704                     container.cls += ' col-sm-' + (12 - this.labelsm);
40705                 }
40706                 if(this.labelxs > 0){
40707                     label.cls += ' col-xs-' + this.labelxs;
40708                     container.cls += ' col-xs-' + (12 - this.labelxs);
40709                 }
40710             }
40711         }
40712
40713         cfg.cn = [
40714             label,
40715             container,
40716             hiddenInput
40717         ];
40718         
40719         var settings = this;
40720
40721         ['xs','sm','md','lg'].map(function(size){
40722             if (settings[size]) {
40723                 cfg.cls += ' col-' + size + '-' + settings[size];
40724             }
40725         });
40726         
40727         return cfg;
40728     },
40729     
40730     initEvents : function()
40731     {
40732         this.indicator = this.indicatorEl();
40733         
40734         this.initCurrencyEvent();
40735         
40736         this.initNumberEvent();
40737     },
40738     
40739     initCurrencyEvent : function()
40740     {
40741         if (!this.store) {
40742             throw "can not find store for combo";
40743         }
40744         
40745         this.store = Roo.factory(this.store, Roo.data);
40746         this.store.parent = this;
40747         
40748         this.createList();
40749         
40750         this.triggerEl = this.el.select('.input-group-addon', true).first();
40751         
40752         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40753         
40754         var _this = this;
40755         
40756         (function(){
40757             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40758             _this.list.setWidth(lw);
40759         }).defer(100);
40760         
40761         this.list.on('mouseover', this.onViewOver, this);
40762         this.list.on('mousemove', this.onViewMove, this);
40763         this.list.on('scroll', this.onViewScroll, this);
40764         
40765         if(!this.tpl){
40766             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40767         }
40768         
40769         this.view = new Roo.View(this.list, this.tpl, {
40770             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40771         });
40772         
40773         this.view.on('click', this.onViewClick, this);
40774         
40775         this.store.on('beforeload', this.onBeforeLoad, this);
40776         this.store.on('load', this.onLoad, this);
40777         this.store.on('loadexception', this.onLoadException, this);
40778         
40779         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40780             "up" : function(e){
40781                 this.inKeyMode = true;
40782                 this.selectPrev();
40783             },
40784
40785             "down" : function(e){
40786                 if(!this.isExpanded()){
40787                     this.onTriggerClick();
40788                 }else{
40789                     this.inKeyMode = true;
40790                     this.selectNext();
40791                 }
40792             },
40793
40794             "enter" : function(e){
40795                 this.collapse();
40796                 
40797                 if(this.fireEvent("specialkey", this, e)){
40798                     this.onViewClick(false);
40799                 }
40800                 
40801                 return true;
40802             },
40803
40804             "esc" : function(e){
40805                 this.collapse();
40806             },
40807
40808             "tab" : function(e){
40809                 this.collapse();
40810                 
40811                 if(this.fireEvent("specialkey", this, e)){
40812                     this.onViewClick(false);
40813                 }
40814                 
40815                 return true;
40816             },
40817
40818             scope : this,
40819
40820             doRelay : function(foo, bar, hname){
40821                 if(hname == 'down' || this.scope.isExpanded()){
40822                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40823                 }
40824                 return true;
40825             },
40826
40827             forceKeyDown: true
40828         });
40829         
40830         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40831         
40832     },
40833     
40834     initNumberEvent : function(e)
40835     {
40836         this.inputEl().on("keydown" , this.fireKey,  this);
40837         this.inputEl().on("focus", this.onFocus,  this);
40838         this.inputEl().on("blur", this.onBlur,  this);
40839         
40840         this.inputEl().relayEvent('keyup', this);
40841         
40842         if(this.indicator){
40843             this.indicator.addClass('invisible');
40844         }
40845  
40846         this.originalValue = this.getValue();
40847         
40848         if(this.validationEvent == 'keyup'){
40849             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40850             this.inputEl().on('keyup', this.filterValidation, this);
40851         }
40852         else if(this.validationEvent !== false){
40853             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40854         }
40855         
40856         if(this.selectOnFocus){
40857             this.on("focus", this.preFocus, this);
40858             
40859         }
40860         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40861             this.inputEl().on("keypress", this.filterKeys, this);
40862         } else {
40863             this.inputEl().relayEvent('keypress', this);
40864         }
40865         
40866         var allowed = "0123456789";
40867         
40868         if(this.allowDecimals){
40869             allowed += this.decimalSeparator;
40870         }
40871         
40872         if(this.allowNegative){
40873             allowed += "-";
40874         }
40875         
40876         if(this.thousandsDelimiter) {
40877             allowed += ",";
40878         }
40879         
40880         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40881         
40882         var keyPress = function(e){
40883             
40884             var k = e.getKey();
40885             
40886             var c = e.getCharCode();
40887             
40888             if(
40889                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40890                     allowed.indexOf(String.fromCharCode(c)) === -1
40891             ){
40892                 e.stopEvent();
40893                 return;
40894             }
40895             
40896             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40897                 return;
40898             }
40899             
40900             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40901                 e.stopEvent();
40902             }
40903         };
40904         
40905         this.inputEl().on("keypress", keyPress, this);
40906         
40907     },
40908     
40909     onTriggerClick : function(e)
40910     {   
40911         if(this.disabled){
40912             return;
40913         }
40914         
40915         this.page = 0;
40916         this.loadNext = false;
40917         
40918         if(this.isExpanded()){
40919             this.collapse();
40920             return;
40921         }
40922         
40923         this.hasFocus = true;
40924         
40925         if(this.triggerAction == 'all') {
40926             this.doQuery(this.allQuery, true);
40927             return;
40928         }
40929         
40930         this.doQuery(this.getRawValue());
40931     },
40932     
40933     getCurrency : function()
40934     {   
40935         var v = this.currencyEl().getValue();
40936         
40937         return v;
40938     },
40939     
40940     restrictHeight : function()
40941     {
40942         this.list.alignTo(this.currencyEl(), this.listAlign);
40943         this.list.alignTo(this.currencyEl(), this.listAlign);
40944     },
40945     
40946     onViewClick : function(view, doFocus, el, e)
40947     {
40948         var index = this.view.getSelectedIndexes()[0];
40949         
40950         var r = this.store.getAt(index);
40951         
40952         if(r){
40953             this.onSelect(r, index);
40954         }
40955     },
40956     
40957     onSelect : function(record, index){
40958         
40959         if(this.fireEvent('beforeselect', this, record, index) !== false){
40960         
40961             this.setFromCurrencyData(index > -1 ? record.data : false);
40962             
40963             this.collapse();
40964             
40965             this.fireEvent('select', this, record, index);
40966         }
40967     },
40968     
40969     setFromCurrencyData : function(o)
40970     {
40971         var currency = '';
40972         
40973         this.lastCurrency = o;
40974         
40975         if (this.currencyField) {
40976             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40977         } else {
40978             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40979         }
40980         
40981         this.lastSelectionText = currency;
40982         
40983         //setting default currency
40984         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40985             this.setCurrency(this.defaultCurrency);
40986             return;
40987         }
40988         
40989         this.setCurrency(currency);
40990     },
40991     
40992     setFromData : function(o)
40993     {
40994         var c = {};
40995         
40996         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40997         
40998         this.setFromCurrencyData(c);
40999         
41000         var value = '';
41001         
41002         if (this.name) {
41003             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41004         } else {
41005             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41006         }
41007         
41008         this.setValue(value);
41009         
41010     },
41011     
41012     setCurrency : function(v)
41013     {   
41014         this.currencyValue = v;
41015         
41016         if(this.rendered){
41017             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41018             this.validate();
41019         }
41020     },
41021     
41022     setValue : function(v)
41023     {
41024         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41025         
41026         this.value = v;
41027         
41028         if(this.rendered){
41029             
41030             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41031             
41032             this.inputEl().dom.value = (v == '') ? '' :
41033                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41034             
41035             if(!this.allowZero && v === '0') {
41036                 this.hiddenEl().dom.value = '';
41037                 this.inputEl().dom.value = '';
41038             }
41039             
41040             this.validate();
41041         }
41042     },
41043     
41044     getRawValue : function()
41045     {
41046         var v = this.inputEl().getValue();
41047         
41048         return v;
41049     },
41050     
41051     getValue : function()
41052     {
41053         return this.fixPrecision(this.parseValue(this.getRawValue()));
41054     },
41055     
41056     parseValue : function(value)
41057     {
41058         if(this.thousandsDelimiter) {
41059             value += "";
41060             r = new RegExp(",", "g");
41061             value = value.replace(r, "");
41062         }
41063         
41064         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41065         return isNaN(value) ? '' : value;
41066         
41067     },
41068     
41069     fixPrecision : function(value)
41070     {
41071         if(this.thousandsDelimiter) {
41072             value += "";
41073             r = new RegExp(",", "g");
41074             value = value.replace(r, "");
41075         }
41076         
41077         var nan = isNaN(value);
41078         
41079         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41080             return nan ? '' : value;
41081         }
41082         return parseFloat(value).toFixed(this.decimalPrecision);
41083     },
41084     
41085     decimalPrecisionFcn : function(v)
41086     {
41087         return Math.floor(v);
41088     },
41089     
41090     validateValue : function(value)
41091     {
41092         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41093             return false;
41094         }
41095         
41096         var num = this.parseValue(value);
41097         
41098         if(isNaN(num)){
41099             this.markInvalid(String.format(this.nanText, value));
41100             return false;
41101         }
41102         
41103         if(num < this.minValue){
41104             this.markInvalid(String.format(this.minText, this.minValue));
41105             return false;
41106         }
41107         
41108         if(num > this.maxValue){
41109             this.markInvalid(String.format(this.maxText, this.maxValue));
41110             return false;
41111         }
41112         
41113         return true;
41114     },
41115     
41116     validate : function()
41117     {
41118         if(this.disabled || this.allowBlank){
41119             this.markValid();
41120             return true;
41121         }
41122         
41123         var currency = this.getCurrency();
41124         
41125         if(this.validateValue(this.getRawValue()) && currency.length){
41126             this.markValid();
41127             return true;
41128         }
41129         
41130         this.markInvalid();
41131         return false;
41132     },
41133     
41134     getName: function()
41135     {
41136         return this.name;
41137     },
41138     
41139     beforeBlur : function()
41140     {
41141         if(!this.castInt){
41142             return;
41143         }
41144         
41145         var v = this.parseValue(this.getRawValue());
41146         
41147         if(v || v == 0){
41148             this.setValue(v);
41149         }
41150     },
41151     
41152     onBlur : function()
41153     {
41154         this.beforeBlur();
41155         
41156         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41157             //this.el.removeClass(this.focusClass);
41158         }
41159         
41160         this.hasFocus = false;
41161         
41162         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41163             this.validate();
41164         }
41165         
41166         var v = this.getValue();
41167         
41168         if(String(v) !== String(this.startValue)){
41169             this.fireEvent('change', this, v, this.startValue);
41170         }
41171         
41172         this.fireEvent("blur", this);
41173     },
41174     
41175     inputEl : function()
41176     {
41177         return this.el.select('.roo-money-amount-input', true).first();
41178     },
41179     
41180     currencyEl : function()
41181     {
41182         return this.el.select('.roo-money-currency-input', true).first();
41183     },
41184     
41185     hiddenEl : function()
41186     {
41187         return this.el.select('input.hidden-number-input',true).first();
41188     }
41189     
41190 });