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.addClass('show d-block');
2925         Roo.get(document.body).addClass('modal-open');
2926  
2927         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2928             var _this = this;
2929             (function(){
2930                 this.el.addClass('in');
2931             }).defer(50, this);
2932         }else{
2933             this.el.addClass('in');
2934         }
2935
2936         // not sure how we can show data in here..
2937         //if (this.tmpl) {
2938         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2939         //}
2940
2941         Roo.get(document.body).addClass("x-body-masked");
2942         
2943         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2944         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2945         this.maskEl.addClass('show d-block');
2946         
2947         this.resize();
2948         
2949         this.fireEvent('show', this);
2950
2951         // set zindex here - otherwise it appears to be ignored...
2952         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2953
2954         (function () {
2955             this.items.forEach( function(e) {
2956                 e.layout ? e.layout() : false;
2957
2958             });
2959         }).defer(100,this);
2960
2961     },
2962     hide : function()
2963     {
2964         if(this.fireEvent("beforehide", this) !== false){
2965             this.maskEl.removeClass('show d-block');
2966             Roo.get(document.body).removeClass("x-body-masked");
2967             this.el.removeClass('in');
2968             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2969
2970             if(this.animate){ // why
2971                 this.el.addClass('hideing');
2972                 (function(){
2973                     if (!this.el.hasClass('hideing')) {
2974                         return; // it's been shown again...
2975                     }
2976                     this.el.removeClass('show d-block');
2977
2978                     Roo.get(document.body).removeClass('modal-open');
2979                     this.el.removeClass('hideing');
2980                 }).defer(150,this);
2981                 
2982             }else{
2983                 this.el.removeClass('show d-block');
2984                 Roo.get(document.body).removeClass('modal-open');
2985
2986             }
2987             this.fireEvent('hide', this);
2988         }
2989     },
2990     isVisible : function()
2991     {
2992         
2993         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2994         
2995     },
2996
2997     addButton : function(str, cb)
2998     {
2999
3000
3001         var b = Roo.apply({}, { html : str } );
3002         b.xns = b.xns || Roo.bootstrap;
3003         b.xtype = b.xtype || 'Button';
3004         if (typeof(b.listeners) == 'undefined') {
3005             b.listeners = { click : cb.createDelegate(this)  };
3006         }
3007
3008         var btn = Roo.factory(b);
3009
3010         btn.render(this.el.select('.modal-footer div').first());
3011
3012         return btn;
3013
3014     },
3015
3016     setDefaultButton : function(btn)
3017     {
3018         //this.el.select('.modal-footer').()
3019     },
3020     diff : false,
3021
3022     resizeTo: function(w,h)
3023     {
3024         // skip.. ?? why??
3025
3026         this.dialogEl.setWidth(w);
3027         if (this.diff === false) {
3028             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3029         }
3030
3031         this.bodyEl.setHeight(h - this.diff);
3032
3033         this.fireEvent('resize', this);
3034
3035     },
3036     setContentSize  : function(w, h)
3037     {
3038
3039     },
3040     onButtonClick: function(btn,e)
3041     {
3042         //Roo.log([a,b,c]);
3043         this.fireEvent('btnclick', btn.name, e);
3044     },
3045      /**
3046      * Set the title of the Dialog
3047      * @param {String} str new Title
3048      */
3049     setTitle: function(str) {
3050         this.titleEl.dom.innerHTML = str;
3051     },
3052     /**
3053      * Set the body of the Dialog
3054      * @param {String} str new Title
3055      */
3056     setBody: function(str) {
3057         this.bodyEl.dom.innerHTML = str;
3058     },
3059     /**
3060      * Set the body of the Dialog using the template
3061      * @param {Obj} data - apply this data to the template and replace the body contents.
3062      */
3063     applyBody: function(obj)
3064     {
3065         if (!this.tmpl) {
3066             Roo.log("Error - using apply Body without a template");
3067             //code
3068         }
3069         this.tmpl.overwrite(this.bodyEl, obj);
3070     },
3071     
3072     getChildHeight : function(child_nodes)
3073     {
3074         if(
3075             !child_nodes ||
3076             child_nodes.length == 0
3077         ) {
3078             return;
3079         }
3080         
3081         var child_height = 0;
3082         
3083         for(var i = 0; i < child_nodes.length; i++) {
3084             
3085             /*
3086             * for modal with tabs...
3087             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3088                 
3089                 var layout_childs = child_nodes[i].childNodes;
3090                 
3091                 for(var j = 0; j < layout_childs.length; j++) {
3092                     
3093                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3094                         
3095                         var layout_body_childs = layout_childs[j].childNodes;
3096                         
3097                         for(var k = 0; k < layout_body_childs.length; k++) {
3098                             
3099                             if(layout_body_childs[k].classList.contains('navbar')) {
3100                                 child_height += layout_body_childs[k].offsetHeight;
3101                                 continue;
3102                             }
3103                             
3104                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3105                                 
3106                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3107                                 
3108                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3109                                     
3110                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3111                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3112                                         continue;
3113                                     }
3114                                     
3115                                 }
3116                                 
3117                             }
3118                             
3119                         }
3120                     }
3121                 }
3122                 continue;
3123             }
3124             */
3125             
3126             child_height += child_nodes[i].offsetHeight;
3127             // Roo.log(child_nodes[i].offsetHeight);
3128         }
3129         
3130         return child_height;
3131     }
3132
3133 });
3134
3135
3136 Roo.apply(Roo.bootstrap.Modal,  {
3137     /**
3138          * Button config that displays a single OK button
3139          * @type Object
3140          */
3141         OK :  [{
3142             name : 'ok',
3143             weight : 'primary',
3144             html : 'OK'
3145         }],
3146         /**
3147          * Button config that displays Yes and No buttons
3148          * @type Object
3149          */
3150         YESNO : [
3151             {
3152                 name  : 'no',
3153                 html : 'No'
3154             },
3155             {
3156                 name  :'yes',
3157                 weight : 'primary',
3158                 html : 'Yes'
3159             }
3160         ],
3161
3162         /**
3163          * Button config that displays OK and Cancel buttons
3164          * @type Object
3165          */
3166         OKCANCEL : [
3167             {
3168                name : 'cancel',
3169                 html : 'Cancel'
3170             },
3171             {
3172                 name : 'ok',
3173                 weight : 'primary',
3174                 html : 'OK'
3175             }
3176         ],
3177         /**
3178          * Button config that displays Yes, No and Cancel buttons
3179          * @type Object
3180          */
3181         YESNOCANCEL : [
3182             {
3183                 name : 'yes',
3184                 weight : 'primary',
3185                 html : 'Yes'
3186             },
3187             {
3188                 name : 'no',
3189                 html : 'No'
3190             },
3191             {
3192                 name : 'cancel',
3193                 html : 'Cancel'
3194             }
3195         ],
3196         
3197         zIndex : 10001
3198 });
3199 /*
3200  * - LGPL
3201  *
3202  * messagebox - can be used as a replace
3203  * 
3204  */
3205 /**
3206  * @class Roo.MessageBox
3207  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3208  * Example usage:
3209  *<pre><code>
3210 // Basic alert:
3211 Roo.Msg.alert('Status', 'Changes saved successfully.');
3212
3213 // Prompt for user data:
3214 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3215     if (btn == 'ok'){
3216         // process text value...
3217     }
3218 });
3219
3220 // Show a dialog using config options:
3221 Roo.Msg.show({
3222    title:'Save Changes?',
3223    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3224    buttons: Roo.Msg.YESNOCANCEL,
3225    fn: processResult,
3226    animEl: 'elId'
3227 });
3228 </code></pre>
3229  * @singleton
3230  */
3231 Roo.bootstrap.MessageBox = function(){
3232     var dlg, opt, mask, waitTimer;
3233     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3234     var buttons, activeTextEl, bwidth;
3235
3236     
3237     // private
3238     var handleButton = function(button){
3239         dlg.hide();
3240         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3241     };
3242
3243     // private
3244     var handleHide = function(){
3245         if(opt && opt.cls){
3246             dlg.el.removeClass(opt.cls);
3247         }
3248         //if(waitTimer){
3249         //    Roo.TaskMgr.stop(waitTimer);
3250         //    waitTimer = null;
3251         //}
3252     };
3253
3254     // private
3255     var updateButtons = function(b){
3256         var width = 0;
3257         if(!b){
3258             buttons["ok"].hide();
3259             buttons["cancel"].hide();
3260             buttons["yes"].hide();
3261             buttons["no"].hide();
3262             //dlg.footer.dom.style.display = 'none';
3263             return width;
3264         }
3265         dlg.footerEl.dom.style.display = '';
3266         for(var k in buttons){
3267             if(typeof buttons[k] != "function"){
3268                 if(b[k]){
3269                     buttons[k].show();
3270                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3271                     width += buttons[k].el.getWidth()+15;
3272                 }else{
3273                     buttons[k].hide();
3274                 }
3275             }
3276         }
3277         return width;
3278     };
3279
3280     // private
3281     var handleEsc = function(d, k, e){
3282         if(opt && opt.closable !== false){
3283             dlg.hide();
3284         }
3285         if(e){
3286             e.stopEvent();
3287         }
3288     };
3289
3290     return {
3291         /**
3292          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3293          * @return {Roo.BasicDialog} The BasicDialog element
3294          */
3295         getDialog : function(){
3296            if(!dlg){
3297                 dlg = new Roo.bootstrap.Modal( {
3298                     //draggable: true,
3299                     //resizable:false,
3300                     //constraintoviewport:false,
3301                     //fixedcenter:true,
3302                     //collapsible : false,
3303                     //shim:true,
3304                     //modal: true,
3305                 //    width: 'auto',
3306                   //  height:100,
3307                     //buttonAlign:"center",
3308                     closeClick : function(){
3309                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3310                             handleButton("no");
3311                         }else{
3312                             handleButton("cancel");
3313                         }
3314                     }
3315                 });
3316                 dlg.render();
3317                 dlg.on("hide", handleHide);
3318                 mask = dlg.mask;
3319                 //dlg.addKeyListener(27, handleEsc);
3320                 buttons = {};
3321                 this.buttons = buttons;
3322                 var bt = this.buttonText;
3323                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3324                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3325                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3326                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3327                 //Roo.log(buttons);
3328                 bodyEl = dlg.bodyEl.createChild({
3329
3330                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3331                         '<textarea class="roo-mb-textarea"></textarea>' +
3332                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3333                 });
3334                 msgEl = bodyEl.dom.firstChild;
3335                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3336                 textboxEl.enableDisplayMode();
3337                 textboxEl.addKeyListener([10,13], function(){
3338                     if(dlg.isVisible() && opt && opt.buttons){
3339                         if(opt.buttons.ok){
3340                             handleButton("ok");
3341                         }else if(opt.buttons.yes){
3342                             handleButton("yes");
3343                         }
3344                     }
3345                 });
3346                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3347                 textareaEl.enableDisplayMode();
3348                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3349                 progressEl.enableDisplayMode();
3350                 
3351                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3352                 var pf = progressEl.dom.firstChild;
3353                 if (pf) {
3354                     pp = Roo.get(pf.firstChild);
3355                     pp.setHeight(pf.offsetHeight);
3356                 }
3357                 
3358             }
3359             return dlg;
3360         },
3361
3362         /**
3363          * Updates the message box body text
3364          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3365          * the XHTML-compliant non-breaking space character '&amp;#160;')
3366          * @return {Roo.MessageBox} This message box
3367          */
3368         updateText : function(text)
3369         {
3370             if(!dlg.isVisible() && !opt.width){
3371                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3372                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3373             }
3374             msgEl.innerHTML = text || '&#160;';
3375       
3376             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3377             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3378             var w = Math.max(
3379                     Math.min(opt.width || cw , this.maxWidth), 
3380                     Math.max(opt.minWidth || this.minWidth, bwidth)
3381             );
3382             if(opt.prompt){
3383                 activeTextEl.setWidth(w);
3384             }
3385             if(dlg.isVisible()){
3386                 dlg.fixedcenter = false;
3387             }
3388             // to big, make it scroll. = But as usual stupid IE does not support
3389             // !important..
3390             
3391             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3392                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3393                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3394             } else {
3395                 bodyEl.dom.style.height = '';
3396                 bodyEl.dom.style.overflowY = '';
3397             }
3398             if (cw > w) {
3399                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3400             } else {
3401                 bodyEl.dom.style.overflowX = '';
3402             }
3403             
3404             dlg.setContentSize(w, bodyEl.getHeight());
3405             if(dlg.isVisible()){
3406                 dlg.fixedcenter = true;
3407             }
3408             return this;
3409         },
3410
3411         /**
3412          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3413          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3414          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3415          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         updateProgress : function(value, text){
3419             if(text){
3420                 this.updateText(text);
3421             }
3422             
3423             if (pp) { // weird bug on my firefox - for some reason this is not defined
3424                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3425                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3426             }
3427             return this;
3428         },        
3429
3430         /**
3431          * Returns true if the message box is currently displayed
3432          * @return {Boolean} True if the message box is visible, else false
3433          */
3434         isVisible : function(){
3435             return dlg && dlg.isVisible();  
3436         },
3437
3438         /**
3439          * Hides the message box if it is displayed
3440          */
3441         hide : function(){
3442             if(this.isVisible()){
3443                 dlg.hide();
3444             }  
3445         },
3446
3447         /**
3448          * Displays a new message box, or reinitializes an existing message box, based on the config options
3449          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3450          * The following config object properties are supported:
3451          * <pre>
3452 Property    Type             Description
3453 ----------  ---------------  ------------------------------------------------------------------------------------
3454 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3455                                    closes (defaults to undefined)
3456 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3457                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3458 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3459                                    progress and wait dialogs will ignore this property and always hide the
3460                                    close button as they can only be closed programmatically.
3461 cls               String           A custom CSS class to apply to the message box element
3462 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3463                                    displayed (defaults to 75)
3464 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3465                                    function will be btn (the name of the button that was clicked, if applicable,
3466                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3467                                    Progress and wait dialogs will ignore this option since they do not respond to
3468                                    user actions and can only be closed programmatically, so any required function
3469                                    should be called by the same code after it closes the dialog.
3470 icon              String           A CSS class that provides a background image to be used as an icon for
3471                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3472 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3473 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3474 modal             Boolean          False to allow user interaction with the page while the message box is
3475                                    displayed (defaults to true)
3476 msg               String           A string that will replace the existing message box body text (defaults
3477                                    to the XHTML-compliant non-breaking space character '&#160;')
3478 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3479 progress          Boolean          True to display a progress bar (defaults to false)
3480 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3481 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3482 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3483 title             String           The title text
3484 value             String           The string value to set into the active textbox element if displayed
3485 wait              Boolean          True to display a progress bar (defaults to false)
3486 width             Number           The width of the dialog in pixels
3487 </pre>
3488          *
3489          * Example usage:
3490          * <pre><code>
3491 Roo.Msg.show({
3492    title: 'Address',
3493    msg: 'Please enter your address:',
3494    width: 300,
3495    buttons: Roo.MessageBox.OKCANCEL,
3496    multiline: true,
3497    fn: saveAddress,
3498    animEl: 'addAddressBtn'
3499 });
3500 </code></pre>
3501          * @param {Object} config Configuration options
3502          * @return {Roo.MessageBox} This message box
3503          */
3504         show : function(options)
3505         {
3506             
3507             // this causes nightmares if you show one dialog after another
3508             // especially on callbacks..
3509              
3510             if(this.isVisible()){
3511                 
3512                 this.hide();
3513                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3514                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3515                 Roo.log("New Dialog Message:" +  options.msg )
3516                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3517                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3518                 
3519             }
3520             var d = this.getDialog();
3521             opt = options;
3522             d.setTitle(opt.title || "&#160;");
3523             d.closeEl.setDisplayed(opt.closable !== false);
3524             activeTextEl = textboxEl;
3525             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3526             if(opt.prompt){
3527                 if(opt.multiline){
3528                     textboxEl.hide();
3529                     textareaEl.show();
3530                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3531                         opt.multiline : this.defaultTextHeight);
3532                     activeTextEl = textareaEl;
3533                 }else{
3534                     textboxEl.show();
3535                     textareaEl.hide();
3536                 }
3537             }else{
3538                 textboxEl.hide();
3539                 textareaEl.hide();
3540             }
3541             progressEl.setDisplayed(opt.progress === true);
3542             this.updateProgress(0);
3543             activeTextEl.dom.value = opt.value || "";
3544             if(opt.prompt){
3545                 dlg.setDefaultButton(activeTextEl);
3546             }else{
3547                 var bs = opt.buttons;
3548                 var db = null;
3549                 if(bs && bs.ok){
3550                     db = buttons["ok"];
3551                 }else if(bs && bs.yes){
3552                     db = buttons["yes"];
3553                 }
3554                 dlg.setDefaultButton(db);
3555             }
3556             bwidth = updateButtons(opt.buttons);
3557             this.updateText(opt.msg);
3558             if(opt.cls){
3559                 d.el.addClass(opt.cls);
3560             }
3561             d.proxyDrag = opt.proxyDrag === true;
3562             d.modal = opt.modal !== false;
3563             d.mask = opt.modal !== false ? mask : false;
3564             if(!d.isVisible()){
3565                 // force it to the end of the z-index stack so it gets a cursor in FF
3566                 document.body.appendChild(dlg.el.dom);
3567                 d.animateTarget = null;
3568                 d.show(options.animEl);
3569             }
3570             return this;
3571         },
3572
3573         /**
3574          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3575          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3576          * and closing the message box when the process is complete.
3577          * @param {String} title The title bar text
3578          * @param {String} msg The message box body text
3579          * @return {Roo.MessageBox} This message box
3580          */
3581         progress : function(title, msg){
3582             this.show({
3583                 title : title,
3584                 msg : msg,
3585                 buttons: false,
3586                 progress:true,
3587                 closable:false,
3588                 minWidth: this.minProgressWidth,
3589                 modal : true
3590             });
3591             return this;
3592         },
3593
3594         /**
3595          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3596          * If a callback function is passed it will be called after the user clicks the button, and the
3597          * id of the button that was clicked will be passed as the only parameter to the callback
3598          * (could also be the top-right close button).
3599          * @param {String} title The title bar text
3600          * @param {String} msg The message box body text
3601          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3602          * @param {Object} scope (optional) The scope of the callback function
3603          * @return {Roo.MessageBox} This message box
3604          */
3605         alert : function(title, msg, fn, scope)
3606         {
3607             this.show({
3608                 title : title,
3609                 msg : msg,
3610                 buttons: this.OK,
3611                 fn: fn,
3612                 closable : false,
3613                 scope : scope,
3614                 modal : true
3615             });
3616             return this;
3617         },
3618
3619         /**
3620          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3621          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3622          * You are responsible for closing the message box when the process is complete.
3623          * @param {String} msg The message box body text
3624          * @param {String} title (optional) The title bar text
3625          * @return {Roo.MessageBox} This message box
3626          */
3627         wait : function(msg, title){
3628             this.show({
3629                 title : title,
3630                 msg : msg,
3631                 buttons: false,
3632                 closable:false,
3633                 progress:true,
3634                 modal:true,
3635                 width:300,
3636                 wait:true
3637             });
3638             waitTimer = Roo.TaskMgr.start({
3639                 run: function(i){
3640                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3641                 },
3642                 interval: 1000
3643             });
3644             return this;
3645         },
3646
3647         /**
3648          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3649          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3650          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         confirm : function(title, msg, fn, scope){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: this.YESNO,
3662                 fn: fn,
3663                 scope : scope,
3664                 modal : true
3665             });
3666             return this;
3667         },
3668
3669         /**
3670          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3671          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3672          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3673          * (could also be the top-right close button) and the text that was entered will be passed as the two
3674          * parameters to the callback.
3675          * @param {String} title The title bar text
3676          * @param {String} msg The message box body text
3677          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678          * @param {Object} scope (optional) The scope of the callback function
3679          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3680          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3681          * @return {Roo.MessageBox} This message box
3682          */
3683         prompt : function(title, msg, fn, scope, multiline){
3684             this.show({
3685                 title : title,
3686                 msg : msg,
3687                 buttons: this.OKCANCEL,
3688                 fn: fn,
3689                 minWidth:250,
3690                 scope : scope,
3691                 prompt:true,
3692                 multiline: multiline,
3693                 modal : true
3694             });
3695             return this;
3696         },
3697
3698         /**
3699          * Button config that displays a single OK button
3700          * @type Object
3701          */
3702         OK : {ok:true},
3703         /**
3704          * Button config that displays Yes and No buttons
3705          * @type Object
3706          */
3707         YESNO : {yes:true, no:true},
3708         /**
3709          * Button config that displays OK and Cancel buttons
3710          * @type Object
3711          */
3712         OKCANCEL : {ok:true, cancel:true},
3713         /**
3714          * Button config that displays Yes, No and Cancel buttons
3715          * @type Object
3716          */
3717         YESNOCANCEL : {yes:true, no:true, cancel:true},
3718
3719         /**
3720          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3721          * @type Number
3722          */
3723         defaultTextHeight : 75,
3724         /**
3725          * The maximum width in pixels of the message box (defaults to 600)
3726          * @type Number
3727          */
3728         maxWidth : 600,
3729         /**
3730          * The minimum width in pixels of the message box (defaults to 100)
3731          * @type Number
3732          */
3733         minWidth : 100,
3734         /**
3735          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3736          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3737          * @type Number
3738          */
3739         minProgressWidth : 250,
3740         /**
3741          * An object containing the default button text strings that can be overriden for localized language support.
3742          * Supported properties are: ok, cancel, yes and no.
3743          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3744          * @type Object
3745          */
3746         buttonText : {
3747             ok : "OK",
3748             cancel : "Cancel",
3749             yes : "Yes",
3750             no : "No"
3751         }
3752     };
3753 }();
3754
3755 /**
3756  * Shorthand for {@link Roo.MessageBox}
3757  */
3758 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3759 Roo.Msg = Roo.Msg || Roo.MessageBox;
3760 /*
3761  * - LGPL
3762  *
3763  * navbar
3764  * 
3765  */
3766
3767 /**
3768  * @class Roo.bootstrap.Navbar
3769  * @extends Roo.bootstrap.Component
3770  * Bootstrap Navbar class
3771
3772  * @constructor
3773  * Create a new Navbar
3774  * @param {Object} config The config object
3775  */
3776
3777
3778 Roo.bootstrap.Navbar = function(config){
3779     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3780     this.addEvents({
3781         // raw events
3782         /**
3783          * @event beforetoggle
3784          * Fire before toggle the menu
3785          * @param {Roo.EventObject} e
3786          */
3787         "beforetoggle" : true
3788     });
3789 };
3790
3791 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3792     
3793     
3794    
3795     // private
3796     navItems : false,
3797     loadMask : false,
3798     
3799     
3800     getAutoCreate : function(){
3801         
3802         
3803         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3804         
3805     },
3806     
3807     initEvents :function ()
3808     {
3809         //Roo.log(this.el.select('.navbar-toggle',true));
3810         this.el.select('.navbar-toggle',true).on('click', function() {
3811             if(this.fireEvent('beforetoggle', this) !== false){
3812                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3813             }
3814             
3815         }, this);
3816         
3817         var mark = {
3818             tag: "div",
3819             cls:"x-dlg-mask"
3820         };
3821         
3822         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3823         
3824         var size = this.el.getSize();
3825         this.maskEl.setSize(size.width, size.height);
3826         this.maskEl.enableDisplayMode("block");
3827         this.maskEl.hide();
3828         
3829         if(this.loadMask){
3830             this.maskEl.show();
3831         }
3832     },
3833     
3834     
3835     getChildContainer : function()
3836     {
3837         if (this.el.select('.collapse').getCount()) {
3838             return this.el.select('.collapse',true).first();
3839         }
3840         
3841         return this.el;
3842     },
3843     
3844     mask : function()
3845     {
3846         this.maskEl.show();
3847     },
3848     
3849     unmask : function()
3850     {
3851         this.maskEl.hide();
3852     } 
3853     
3854     
3855     
3856     
3857 });
3858
3859
3860
3861  
3862
3863  /*
3864  * - LGPL
3865  *
3866  * navbar
3867  * 
3868  */
3869
3870 /**
3871  * @class Roo.bootstrap.NavSimplebar
3872  * @extends Roo.bootstrap.Navbar
3873  * Bootstrap Sidebar class
3874  *
3875  * @cfg {Boolean} inverse is inverted color
3876  * 
3877  * @cfg {String} type (nav | pills | tabs)
3878  * @cfg {Boolean} arrangement stacked | justified
3879  * @cfg {String} align (left | right) alignment
3880  * 
3881  * @cfg {Boolean} main (true|false) main nav bar? default false
3882  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3883  * 
3884  * @cfg {String} tag (header|footer|nav|div) default is nav 
3885
3886  * 
3887  * 
3888  * 
3889  * @constructor
3890  * Create a new Sidebar
3891  * @param {Object} config The config object
3892  */
3893
3894
3895 Roo.bootstrap.NavSimplebar = function(config){
3896     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3897 };
3898
3899 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3900     
3901     inverse: false,
3902     
3903     type: false,
3904     arrangement: '',
3905     align : false,
3906     
3907     
3908     
3909     main : false,
3910     
3911     
3912     tag : false,
3913     
3914     
3915     getAutoCreate : function(){
3916         
3917         
3918         var cfg = {
3919             tag : this.tag || 'div',
3920             cls : 'navbar'
3921         };
3922           
3923         
3924         cfg.cn = [
3925             {
3926                 cls: 'nav',
3927                 tag : 'ul'
3928             }
3929         ];
3930         
3931          
3932         this.type = this.type || 'nav';
3933         if (['tabs','pills'].indexOf(this.type)!==-1) {
3934             cfg.cn[0].cls += ' nav-' + this.type
3935         
3936         
3937         } else {
3938             if (this.type!=='nav') {
3939                 Roo.log('nav type must be nav/tabs/pills')
3940             }
3941             cfg.cn[0].cls += ' navbar-nav'
3942         }
3943         
3944         
3945         
3946         
3947         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3948             cfg.cn[0].cls += ' nav-' + this.arrangement;
3949         }
3950         
3951         
3952         if (this.align === 'right') {
3953             cfg.cn[0].cls += ' navbar-right';
3954         }
3955         
3956         if (this.inverse) {
3957             cfg.cls += ' navbar-inverse';
3958             
3959         }
3960         
3961         
3962         return cfg;
3963     
3964         
3965     }
3966     
3967     
3968     
3969 });
3970
3971
3972
3973  
3974
3975  
3976        /*
3977  * - LGPL
3978  *
3979  * navbar
3980  * navbar-fixed-top
3981  * navbar-expand-md  fixed-top 
3982  */
3983
3984 /**
3985  * @class Roo.bootstrap.NavHeaderbar
3986  * @extends Roo.bootstrap.NavSimplebar
3987  * Bootstrap Sidebar class
3988  *
3989  * @cfg {String} brand what is brand
3990  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3991  * @cfg {String} brand_href href of the brand
3992  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3993  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3994  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3995  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3996  * 
3997  * @constructor
3998  * Create a new Sidebar
3999  * @param {Object} config The config object
4000  */
4001
4002
4003 Roo.bootstrap.NavHeaderbar = function(config){
4004     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4005       
4006 };
4007
4008 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4009     
4010     position: '',
4011     brand: '',
4012     brand_href: false,
4013     srButton : true,
4014     autohide : false,
4015     desktopCenter : false,
4016    
4017     
4018     getAutoCreate : function(){
4019         
4020         var   cfg = {
4021             tag: this.nav || 'nav',
4022             cls: 'navbar navbar-expand-md',
4023             role: 'navigation',
4024             cn: []
4025         };
4026         
4027         var cn = cfg.cn;
4028         if (this.desktopCenter) {
4029             cn.push({cls : 'container', cn : []});
4030             cn = cn[0].cn;
4031         }
4032         
4033         if(this.srButton){
4034             cn.push({
4035                 tag: 'div',
4036                 cls: 'navbar-header',
4037                 cn: [
4038                     {
4039                         tag: 'button',
4040                         type: 'button',
4041                         cls: 'navbar-toggle navbar-toggler',
4042                         'data-toggle': 'collapse',
4043                         cn: [
4044                             {
4045                                 tag: 'span',
4046                                 cls: 'sr-only',
4047                                 html: 'Toggle navigation'
4048                             },
4049                             {
4050                                 tag: 'span',
4051                                 cls: 'icon-bar navbar-toggler-icon'
4052                             },
4053                             {
4054                                 tag: 'span',
4055                                 cls: 'icon-bar'
4056                             },
4057                             {
4058                                 tag: 'span',
4059                                 cls: 'icon-bar'
4060                             }
4061                         ]
4062                     }
4063                 ]
4064             });
4065         }
4066         
4067         cn.push({
4068             tag: 'div',
4069             cls: 'collapse navbar-collapse',
4070             cn : []
4071         });
4072         
4073         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4074         
4075         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4076             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4077             
4078             // tag can override this..
4079             
4080             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4081         }
4082         
4083         if (this.brand !== '') {
4084             cn[0].cn.push({
4085                 tag: 'a',
4086                 href: this.brand_href ? this.brand_href : '#',
4087                 cls: 'navbar-brand',
4088                 cn: [
4089                 this.brand
4090                 ]
4091             });
4092         }
4093         
4094         if(this.main){
4095             cfg.cls += ' main-nav';
4096         }
4097         
4098         
4099         return cfg;
4100
4101         
4102     },
4103     getHeaderChildContainer : function()
4104     {
4105         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4106             return this.el.select('.navbar-header',true).first();
4107         }
4108         
4109         return this.getChildContainer();
4110     },
4111     
4112     
4113     initEvents : function()
4114     {
4115         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4116         
4117         if (this.autohide) {
4118             
4119             var prevScroll = 0;
4120             var ft = this.el;
4121             
4122             Roo.get(document).on('scroll',function(e) {
4123                 var ns = Roo.get(document).getScroll().top;
4124                 var os = prevScroll;
4125                 prevScroll = ns;
4126                 
4127                 if(ns > os){
4128                     ft.removeClass('slideDown');
4129                     ft.addClass('slideUp');
4130                     return;
4131                 }
4132                 ft.removeClass('slideUp');
4133                 ft.addClass('slideDown');
4134                  
4135               
4136           },this);
4137         }
4138     }    
4139     
4140 });
4141
4142
4143
4144  
4145
4146  /*
4147  * - LGPL
4148  *
4149  * navbar
4150  * 
4151  */
4152
4153 /**
4154  * @class Roo.bootstrap.NavSidebar
4155  * @extends Roo.bootstrap.Navbar
4156  * Bootstrap Sidebar class
4157  * 
4158  * @constructor
4159  * Create a new Sidebar
4160  * @param {Object} config The config object
4161  */
4162
4163
4164 Roo.bootstrap.NavSidebar = function(config){
4165     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4166 };
4167
4168 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4169     
4170     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4171     
4172     getAutoCreate : function(){
4173         
4174         
4175         return  {
4176             tag: 'div',
4177             cls: 'sidebar sidebar-nav'
4178         };
4179     
4180         
4181     }
4182     
4183     
4184     
4185 });
4186
4187
4188
4189  
4190
4191  /*
4192  * - LGPL
4193  *
4194  * nav group
4195  * 
4196  */
4197
4198 /**
4199  * @class Roo.bootstrap.NavGroup
4200  * @extends Roo.bootstrap.Component
4201  * Bootstrap NavGroup class
4202  * @cfg {String} align (left|right)
4203  * @cfg {Boolean} inverse
4204  * @cfg {String} type (nav|pills|tab) default nav
4205  * @cfg {String} navId - reference Id for navbar.
4206
4207  * 
4208  * @constructor
4209  * Create a new nav group
4210  * @param {Object} config The config object
4211  */
4212
4213 Roo.bootstrap.NavGroup = function(config){
4214     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4215     this.navItems = [];
4216    
4217     Roo.bootstrap.NavGroup.register(this);
4218      this.addEvents({
4219         /**
4220              * @event changed
4221              * Fires when the active item changes
4222              * @param {Roo.bootstrap.NavGroup} this
4223              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4224              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4225          */
4226         'changed': true
4227      });
4228     
4229 };
4230
4231 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4232     
4233     align: '',
4234     inverse: false,
4235     form: false,
4236     type: 'nav',
4237     navId : '',
4238     // private
4239     
4240     navItems : false, 
4241     
4242     getAutoCreate : function()
4243     {
4244         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4245         
4246         cfg = {
4247             tag : 'ul',
4248             cls: 'nav' 
4249         };
4250         
4251         if (['tabs','pills'].indexOf(this.type)!==-1) {
4252             cfg.cls += ' nav-' + this.type
4253         } else {
4254             if (this.type!=='nav') {
4255                 Roo.log('nav type must be nav/tabs/pills')
4256             }
4257             cfg.cls += ' navbar-nav'
4258         }
4259         
4260         if (this.parent() && this.parent().sidebar) {
4261             cfg = {
4262                 tag: 'ul',
4263                 cls: 'dashboard-menu sidebar-menu'
4264             };
4265             
4266             return cfg;
4267         }
4268         
4269         if (this.form === true) {
4270             cfg = {
4271                 tag: 'form',
4272                 cls: 'navbar-form'
4273             };
4274             
4275             if (this.align === 'right') {
4276                 cfg.cls += ' navbar-right ml-md-auto';
4277             } else {
4278                 cfg.cls += ' navbar-left';
4279             }
4280         }
4281         
4282         if (this.align === 'right') {
4283             cfg.cls += ' navbar-right ml-md-auto';
4284         } else {
4285             cfg.cls += ' mr-auto';
4286         }
4287         
4288         if (this.inverse) {
4289             cfg.cls += ' navbar-inverse';
4290             
4291         }
4292         
4293         
4294         return cfg;
4295     },
4296     /**
4297     * sets the active Navigation item
4298     * @param {Roo.bootstrap.NavItem} the new current navitem
4299     */
4300     setActiveItem : function(item)
4301     {
4302         var prev = false;
4303         Roo.each(this.navItems, function(v){
4304             if (v == item) {
4305                 return ;
4306             }
4307             if (v.isActive()) {
4308                 v.setActive(false, true);
4309                 prev = v;
4310                 
4311             }
4312             
4313         });
4314
4315         item.setActive(true, true);
4316         this.fireEvent('changed', this, item, prev);
4317         
4318         
4319     },
4320     /**
4321     * gets the active Navigation item
4322     * @return {Roo.bootstrap.NavItem} the current navitem
4323     */
4324     getActive : function()
4325     {
4326         
4327         var prev = false;
4328         Roo.each(this.navItems, function(v){
4329             
4330             if (v.isActive()) {
4331                 prev = v;
4332                 
4333             }
4334             
4335         });
4336         return prev;
4337     },
4338     
4339     indexOfNav : function()
4340     {
4341         
4342         var prev = false;
4343         Roo.each(this.navItems, function(v,i){
4344             
4345             if (v.isActive()) {
4346                 prev = i;
4347                 
4348             }
4349             
4350         });
4351         return prev;
4352     },
4353     /**
4354     * adds a Navigation item
4355     * @param {Roo.bootstrap.NavItem} the navitem to add
4356     */
4357     addItem : function(cfg)
4358     {
4359         var cn = new Roo.bootstrap.NavItem(cfg);
4360         this.register(cn);
4361         cn.parentId = this.id;
4362         cn.onRender(this.el, null);
4363         return cn;
4364     },
4365     /**
4366     * register a Navigation item
4367     * @param {Roo.bootstrap.NavItem} the navitem to add
4368     */
4369     register : function(item)
4370     {
4371         this.navItems.push( item);
4372         item.navId = this.navId;
4373     
4374     },
4375     
4376     /**
4377     * clear all the Navigation item
4378     */
4379    
4380     clearAll : function()
4381     {
4382         this.navItems = [];
4383         this.el.dom.innerHTML = '';
4384     },
4385     
4386     getNavItem: function(tabId)
4387     {
4388         var ret = false;
4389         Roo.each(this.navItems, function(e) {
4390             if (e.tabId == tabId) {
4391                ret =  e;
4392                return false;
4393             }
4394             return true;
4395             
4396         });
4397         return ret;
4398     },
4399     
4400     setActiveNext : function()
4401     {
4402         var i = this.indexOfNav(this.getActive());
4403         if (i > this.navItems.length) {
4404             return;
4405         }
4406         this.setActiveItem(this.navItems[i+1]);
4407     },
4408     setActivePrev : function()
4409     {
4410         var i = this.indexOfNav(this.getActive());
4411         if (i  < 1) {
4412             return;
4413         }
4414         this.setActiveItem(this.navItems[i-1]);
4415     },
4416     clearWasActive : function(except) {
4417         Roo.each(this.navItems, function(e) {
4418             if (e.tabId != except.tabId && e.was_active) {
4419                e.was_active = false;
4420                return false;
4421             }
4422             return true;
4423             
4424         });
4425     },
4426     getWasActive : function ()
4427     {
4428         var r = false;
4429         Roo.each(this.navItems, function(e) {
4430             if (e.was_active) {
4431                r = e;
4432                return false;
4433             }
4434             return true;
4435             
4436         });
4437         return r;
4438     }
4439     
4440     
4441 });
4442
4443  
4444 Roo.apply(Roo.bootstrap.NavGroup, {
4445     
4446     groups: {},
4447      /**
4448     * register a Navigation Group
4449     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4450     */
4451     register : function(navgrp)
4452     {
4453         this.groups[navgrp.navId] = navgrp;
4454         
4455     },
4456     /**
4457     * fetch a Navigation Group based on the navigation ID
4458     * @param {string} the navgroup to add
4459     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4460     */
4461     get: function(navId) {
4462         if (typeof(this.groups[navId]) == 'undefined') {
4463             return false;
4464             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4465         }
4466         return this.groups[navId] ;
4467     }
4468     
4469     
4470     
4471 });
4472
4473  /*
4474  * - LGPL
4475  *
4476  * row
4477  * 
4478  */
4479
4480 /**
4481  * @class Roo.bootstrap.NavItem
4482  * @extends Roo.bootstrap.Component
4483  * Bootstrap Navbar.NavItem class
4484  * @cfg {String} href  link to
4485  * @cfg {String} html content of button
4486  * @cfg {String} badge text inside badge
4487  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4488  * @cfg {String} glyphicon name of glyphicon
4489  * @cfg {String} icon name of font awesome icon
4490  * @cfg {Boolean} active Is item active
4491  * @cfg {Boolean} disabled Is item disabled
4492  
4493  * @cfg {Boolean} preventDefault (true | false) default false
4494  * @cfg {String} tabId the tab that this item activates.
4495  * @cfg {String} tagtype (a|span) render as a href or span?
4496  * @cfg {Boolean} animateRef (true|false) link to element default false  
4497   
4498  * @constructor
4499  * Create a new Navbar Item
4500  * @param {Object} config The config object
4501  */
4502 Roo.bootstrap.NavItem = function(config){
4503     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4504     this.addEvents({
4505         // raw events
4506         /**
4507          * @event click
4508          * The raw click event for the entire grid.
4509          * @param {Roo.EventObject} e
4510          */
4511         "click" : true,
4512          /**
4513             * @event changed
4514             * Fires when the active item active state changes
4515             * @param {Roo.bootstrap.NavItem} this
4516             * @param {boolean} state the new state
4517              
4518          */
4519         'changed': true,
4520         /**
4521             * @event scrollto
4522             * Fires when scroll to element
4523             * @param {Roo.bootstrap.NavItem} this
4524             * @param {Object} options
4525             * @param {Roo.EventObject} e
4526              
4527          */
4528         'scrollto': true
4529     });
4530    
4531 };
4532
4533 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4534     
4535     href: false,
4536     html: '',
4537     badge: '',
4538     icon: false,
4539     glyphicon: false,
4540     active: false,
4541     preventDefault : false,
4542     tabId : false,
4543     tagtype : 'a',
4544     disabled : false,
4545     animateRef : false,
4546     was_active : false,
4547     
4548     getAutoCreate : function(){
4549          
4550         var cfg = {
4551             tag: 'li',
4552             cls: 'nav-item'
4553             
4554         };
4555         
4556         if (this.active) {
4557             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4558         }
4559         if (this.disabled) {
4560             cfg.cls += ' disabled';
4561         }
4562         
4563         if (this.href || this.html || this.glyphicon || this.icon) {
4564             cfg.cn = [
4565                 {
4566                     tag: this.tagtype,
4567                     href : this.href || "#",
4568                     html: this.html || ''
4569                 }
4570             ];
4571             if (this.tagtype == 'a') {
4572                 cfg.cn[0].cls = 'nav-link';
4573             }
4574             if (this.icon) {
4575                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4576             }
4577
4578             if(this.glyphicon) {
4579                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4580             }
4581             
4582             if (this.menu) {
4583                 
4584                 cfg.cn[0].html += " <span class='caret'></span>";
4585              
4586             }
4587             
4588             if (this.badge !== '') {
4589                  
4590                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4591             }
4592         }
4593         
4594         
4595         
4596         return cfg;
4597     },
4598     initEvents: function() 
4599     {
4600         if (typeof (this.menu) != 'undefined') {
4601             this.menu.parentType = this.xtype;
4602             this.menu.triggerEl = this.el;
4603             this.menu = this.addxtype(Roo.apply({}, this.menu));
4604         }
4605         
4606         this.el.select('a',true).on('click', this.onClick, this);
4607         
4608         if(this.tagtype == 'span'){
4609             this.el.select('span',true).on('click', this.onClick, this);
4610         }
4611        
4612         // at this point parent should be available..
4613         this.parent().register(this);
4614     },
4615     
4616     onClick : function(e)
4617     {
4618         if (e.getTarget('.dropdown-menu-item')) {
4619             // did you click on a menu itemm.... - then don't trigger onclick..
4620             return;
4621         }
4622         
4623         if(
4624                 this.preventDefault || 
4625                 this.href == '#' 
4626         ){
4627             Roo.log("NavItem - prevent Default?");
4628             e.preventDefault();
4629         }
4630         
4631         if (this.disabled) {
4632             return;
4633         }
4634         
4635         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4636         if (tg && tg.transition) {
4637             Roo.log("waiting for the transitionend");
4638             return;
4639         }
4640         
4641         
4642         
4643         //Roo.log("fire event clicked");
4644         if(this.fireEvent('click', this, e) === false){
4645             return;
4646         };
4647         
4648         if(this.tagtype == 'span'){
4649             return;
4650         }
4651         
4652         //Roo.log(this.href);
4653         var ael = this.el.select('a',true).first();
4654         //Roo.log(ael);
4655         
4656         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4657             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4658             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4659                 return; // ignore... - it's a 'hash' to another page.
4660             }
4661             Roo.log("NavItem - prevent Default?");
4662             e.preventDefault();
4663             this.scrollToElement(e);
4664         }
4665         
4666         
4667         var p =  this.parent();
4668    
4669         if (['tabs','pills'].indexOf(p.type)!==-1) {
4670             if (typeof(p.setActiveItem) !== 'undefined') {
4671                 p.setActiveItem(this);
4672             }
4673         }
4674         
4675         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4676         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4677             // remove the collapsed menu expand...
4678             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4679         }
4680     },
4681     
4682     isActive: function () {
4683         return this.active
4684     },
4685     setActive : function(state, fire, is_was_active)
4686     {
4687         if (this.active && !state && this.navId) {
4688             this.was_active = true;
4689             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4690             if (nv) {
4691                 nv.clearWasActive(this);
4692             }
4693             
4694         }
4695         this.active = state;
4696         
4697         if (!state ) {
4698             this.el.removeClass('active');
4699         } else if (!this.el.hasClass('active')) {
4700             this.el.addClass('active');
4701         }
4702         if (fire) {
4703             this.fireEvent('changed', this, state);
4704         }
4705         
4706         // show a panel if it's registered and related..
4707         
4708         if (!this.navId || !this.tabId || !state || is_was_active) {
4709             return;
4710         }
4711         
4712         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4713         if (!tg) {
4714             return;
4715         }
4716         var pan = tg.getPanelByName(this.tabId);
4717         if (!pan) {
4718             return;
4719         }
4720         // if we can not flip to new panel - go back to old nav highlight..
4721         if (false == tg.showPanel(pan)) {
4722             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4723             if (nv) {
4724                 var onav = nv.getWasActive();
4725                 if (onav) {
4726                     onav.setActive(true, false, true);
4727                 }
4728             }
4729             
4730         }
4731         
4732         
4733         
4734     },
4735      // this should not be here...
4736     setDisabled : function(state)
4737     {
4738         this.disabled = state;
4739         if (!state ) {
4740             this.el.removeClass('disabled');
4741         } else if (!this.el.hasClass('disabled')) {
4742             this.el.addClass('disabled');
4743         }
4744         
4745     },
4746     
4747     /**
4748      * Fetch the element to display the tooltip on.
4749      * @return {Roo.Element} defaults to this.el
4750      */
4751     tooltipEl : function()
4752     {
4753         return this.el.select('' + this.tagtype + '', true).first();
4754     },
4755     
4756     scrollToElement : function(e)
4757     {
4758         var c = document.body;
4759         
4760         /*
4761          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4762          */
4763         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4764             c = document.documentElement;
4765         }
4766         
4767         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4768         
4769         if(!target){
4770             return;
4771         }
4772
4773         var o = target.calcOffsetsTo(c);
4774         
4775         var options = {
4776             target : target,
4777             value : o[1]
4778         };
4779         
4780         this.fireEvent('scrollto', this, options, e);
4781         
4782         Roo.get(c).scrollTo('top', options.value, true);
4783         
4784         return;
4785     }
4786 });
4787  
4788
4789  /*
4790  * - LGPL
4791  *
4792  * sidebar item
4793  *
4794  *  li
4795  *    <span> icon </span>
4796  *    <span> text </span>
4797  *    <span>badge </span>
4798  */
4799
4800 /**
4801  * @class Roo.bootstrap.NavSidebarItem
4802  * @extends Roo.bootstrap.NavItem
4803  * Bootstrap Navbar.NavSidebarItem class
4804  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4805  * {Boolean} open is the menu open
4806  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4807  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4808  * {String} buttonSize (sm|md|lg)the extra classes for the button
4809  * {Boolean} showArrow show arrow next to the text (default true)
4810  * @constructor
4811  * Create a new Navbar Button
4812  * @param {Object} config The config object
4813  */
4814 Roo.bootstrap.NavSidebarItem = function(config){
4815     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4816     this.addEvents({
4817         // raw events
4818         /**
4819          * @event click
4820          * The raw click event for the entire grid.
4821          * @param {Roo.EventObject} e
4822          */
4823         "click" : true,
4824          /**
4825             * @event changed
4826             * Fires when the active item active state changes
4827             * @param {Roo.bootstrap.NavSidebarItem} this
4828             * @param {boolean} state the new state
4829              
4830          */
4831         'changed': true
4832     });
4833    
4834 };
4835
4836 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4837     
4838     badgeWeight : 'default',
4839     
4840     open: false,
4841     
4842     buttonView : false,
4843     
4844     buttonWeight : 'default',
4845     
4846     buttonSize : 'md',
4847     
4848     showArrow : true,
4849     
4850     getAutoCreate : function(){
4851         
4852         
4853         var a = {
4854                 tag: 'a',
4855                 href : this.href || '#',
4856                 cls: '',
4857                 html : '',
4858                 cn : []
4859         };
4860         
4861         if(this.buttonView){
4862             a = {
4863                 tag: 'button',
4864                 href : this.href || '#',
4865                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4866                 html : this.html,
4867                 cn : []
4868             };
4869         }
4870         
4871         var cfg = {
4872             tag: 'li',
4873             cls: '',
4874             cn: [ a ]
4875         };
4876         
4877         if (this.active) {
4878             cfg.cls += ' active';
4879         }
4880         
4881         if (this.disabled) {
4882             cfg.cls += ' disabled';
4883         }
4884         if (this.open) {
4885             cfg.cls += ' open x-open';
4886         }
4887         // left icon..
4888         if (this.glyphicon || this.icon) {
4889             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4890             a.cn.push({ tag : 'i', cls : c }) ;
4891         }
4892         
4893         if(!this.buttonView){
4894             var span = {
4895                 tag: 'span',
4896                 html : this.html || ''
4897             };
4898
4899             a.cn.push(span);
4900             
4901         }
4902         
4903         if (this.badge !== '') {
4904             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4905         }
4906         
4907         if (this.menu) {
4908             
4909             if(this.showArrow){
4910                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4911             }
4912             
4913             a.cls += ' dropdown-toggle treeview' ;
4914         }
4915         
4916         return cfg;
4917     },
4918     
4919     initEvents : function()
4920     { 
4921         if (typeof (this.menu) != 'undefined') {
4922             this.menu.parentType = this.xtype;
4923             this.menu.triggerEl = this.el;
4924             this.menu = this.addxtype(Roo.apply({}, this.menu));
4925         }
4926         
4927         this.el.on('click', this.onClick, this);
4928         
4929         if(this.badge !== ''){
4930             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4931         }
4932         
4933     },
4934     
4935     onClick : function(e)
4936     {
4937         if(this.disabled){
4938             e.preventDefault();
4939             return;
4940         }
4941         
4942         if(this.preventDefault){
4943             e.preventDefault();
4944         }
4945         
4946         this.fireEvent('click', this);
4947     },
4948     
4949     disable : function()
4950     {
4951         this.setDisabled(true);
4952     },
4953     
4954     enable : function()
4955     {
4956         this.setDisabled(false);
4957     },
4958     
4959     setDisabled : function(state)
4960     {
4961         if(this.disabled == state){
4962             return;
4963         }
4964         
4965         this.disabled = state;
4966         
4967         if (state) {
4968             this.el.addClass('disabled');
4969             return;
4970         }
4971         
4972         this.el.removeClass('disabled');
4973         
4974         return;
4975     },
4976     
4977     setActive : function(state)
4978     {
4979         if(this.active == state){
4980             return;
4981         }
4982         
4983         this.active = state;
4984         
4985         if (state) {
4986             this.el.addClass('active');
4987             return;
4988         }
4989         
4990         this.el.removeClass('active');
4991         
4992         return;
4993     },
4994     
4995     isActive: function () 
4996     {
4997         return this.active;
4998     },
4999     
5000     setBadge : function(str)
5001     {
5002         if(!this.badgeEl){
5003             return;
5004         }
5005         
5006         this.badgeEl.dom.innerHTML = str;
5007     }
5008     
5009    
5010      
5011  
5012 });
5013  
5014
5015  /*
5016  * - LGPL
5017  *
5018  * row
5019  * 
5020  */
5021
5022 /**
5023  * @class Roo.bootstrap.Row
5024  * @extends Roo.bootstrap.Component
5025  * Bootstrap Row class (contains columns...)
5026  * 
5027  * @constructor
5028  * Create a new Row
5029  * @param {Object} config The config object
5030  */
5031
5032 Roo.bootstrap.Row = function(config){
5033     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5034 };
5035
5036 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5037     
5038     getAutoCreate : function(){
5039        return {
5040             cls: 'row clearfix'
5041        };
5042     }
5043     
5044     
5045 });
5046
5047  
5048
5049  /*
5050  * - LGPL
5051  *
5052  * element
5053  * 
5054  */
5055
5056 /**
5057  * @class Roo.bootstrap.Element
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Element class
5060  * @cfg {String} html contents of the element
5061  * @cfg {String} tag tag of the element
5062  * @cfg {String} cls class of the element
5063  * @cfg {Boolean} preventDefault (true|false) default false
5064  * @cfg {Boolean} clickable (true|false) default false
5065  * 
5066  * @constructor
5067  * Create a new Element
5068  * @param {Object} config The config object
5069  */
5070
5071 Roo.bootstrap.Element = function(config){
5072     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5073     
5074     this.addEvents({
5075         // raw events
5076         /**
5077          * @event click
5078          * When a element is chick
5079          * @param {Roo.bootstrap.Element} this
5080          * @param {Roo.EventObject} e
5081          */
5082         "click" : true
5083     });
5084 };
5085
5086 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5087     
5088     tag: 'div',
5089     cls: '',
5090     html: '',
5091     preventDefault: false, 
5092     clickable: false,
5093     
5094     getAutoCreate : function(){
5095         
5096         var cfg = {
5097             tag: this.tag,
5098             // cls: this.cls, double assign in parent class Component.js :: onRender
5099             html: this.html
5100         };
5101         
5102         return cfg;
5103     },
5104     
5105     initEvents: function() 
5106     {
5107         Roo.bootstrap.Element.superclass.initEvents.call(this);
5108         
5109         if(this.clickable){
5110             this.el.on('click', this.onClick, this);
5111         }
5112         
5113     },
5114     
5115     onClick : function(e)
5116     {
5117         if(this.preventDefault){
5118             e.preventDefault();
5119         }
5120         
5121         this.fireEvent('click', this, e);
5122     },
5123     
5124     getValue : function()
5125     {
5126         return this.el.dom.innerHTML;
5127     },
5128     
5129     setValue : function(value)
5130     {
5131         this.el.dom.innerHTML = value;
5132     }
5133    
5134 });
5135
5136  
5137
5138  /*
5139  * - LGPL
5140  *
5141  * pagination
5142  * 
5143  */
5144
5145 /**
5146  * @class Roo.bootstrap.Pagination
5147  * @extends Roo.bootstrap.Component
5148  * Bootstrap Pagination class
5149  * @cfg {String} size xs | sm | md | lg
5150  * @cfg {Boolean} inverse false | true
5151  * 
5152  * @constructor
5153  * Create a new Pagination
5154  * @param {Object} config The config object
5155  */
5156
5157 Roo.bootstrap.Pagination = function(config){
5158     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5159 };
5160
5161 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5162     
5163     cls: false,
5164     size: false,
5165     inverse: false,
5166     
5167     getAutoCreate : function(){
5168         var cfg = {
5169             tag: 'ul',
5170                 cls: 'pagination'
5171         };
5172         if (this.inverse) {
5173             cfg.cls += ' inverse';
5174         }
5175         if (this.html) {
5176             cfg.html=this.html;
5177         }
5178         if (this.cls) {
5179             cfg.cls += " " + this.cls;
5180         }
5181         return cfg;
5182     }
5183    
5184 });
5185
5186  
5187
5188  /*
5189  * - LGPL
5190  *
5191  * Pagination item
5192  * 
5193  */
5194
5195
5196 /**
5197  * @class Roo.bootstrap.PaginationItem
5198  * @extends Roo.bootstrap.Component
5199  * Bootstrap PaginationItem class
5200  * @cfg {String} html text
5201  * @cfg {String} href the link
5202  * @cfg {Boolean} preventDefault (true | false) default true
5203  * @cfg {Boolean} active (true | false) default false
5204  * @cfg {Boolean} disabled default false
5205  * 
5206  * 
5207  * @constructor
5208  * Create a new PaginationItem
5209  * @param {Object} config The config object
5210  */
5211
5212
5213 Roo.bootstrap.PaginationItem = function(config){
5214     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5215     this.addEvents({
5216         // raw events
5217         /**
5218          * @event click
5219          * The raw click event for the entire grid.
5220          * @param {Roo.EventObject} e
5221          */
5222         "click" : true
5223     });
5224 };
5225
5226 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5227     
5228     href : false,
5229     html : false,
5230     preventDefault: true,
5231     active : false,
5232     cls : false,
5233     disabled: false,
5234     
5235     getAutoCreate : function(){
5236         var cfg= {
5237             tag: 'li',
5238             cn: [
5239                 {
5240                     tag : 'a',
5241                     href : this.href ? this.href : '#',
5242                     html : this.html ? this.html : ''
5243                 }
5244             ]
5245         };
5246         
5247         if(this.cls){
5248             cfg.cls = this.cls;
5249         }
5250         
5251         if(this.disabled){
5252             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5253         }
5254         
5255         if(this.active){
5256             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5257         }
5258         
5259         return cfg;
5260     },
5261     
5262     initEvents: function() {
5263         
5264         this.el.on('click', this.onClick, this);
5265         
5266     },
5267     onClick : function(e)
5268     {
5269         Roo.log('PaginationItem on click ');
5270         if(this.preventDefault){
5271             e.preventDefault();
5272         }
5273         
5274         if(this.disabled){
5275             return;
5276         }
5277         
5278         this.fireEvent('click', this, e);
5279     }
5280    
5281 });
5282
5283  
5284
5285  /*
5286  * - LGPL
5287  *
5288  * slider
5289  * 
5290  */
5291
5292
5293 /**
5294  * @class Roo.bootstrap.Slider
5295  * @extends Roo.bootstrap.Component
5296  * Bootstrap Slider class
5297  *    
5298  * @constructor
5299  * Create a new Slider
5300  * @param {Object} config The config object
5301  */
5302
5303 Roo.bootstrap.Slider = function(config){
5304     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5305 };
5306
5307 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5308     
5309     getAutoCreate : function(){
5310         
5311         var cfg = {
5312             tag: 'div',
5313             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5314             cn: [
5315                 {
5316                     tag: 'a',
5317                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5318                 }
5319             ]
5320         };
5321         
5322         return cfg;
5323     }
5324    
5325 });
5326
5327  /*
5328  * Based on:
5329  * Ext JS Library 1.1.1
5330  * Copyright(c) 2006-2007, Ext JS, LLC.
5331  *
5332  * Originally Released Under LGPL - original licence link has changed is not relivant.
5333  *
5334  * Fork - LGPL
5335  * <script type="text/javascript">
5336  */
5337  
5338
5339 /**
5340  * @class Roo.grid.ColumnModel
5341  * @extends Roo.util.Observable
5342  * This is the default implementation of a ColumnModel used by the Grid. It defines
5343  * the columns in the grid.
5344  * <br>Usage:<br>
5345  <pre><code>
5346  var colModel = new Roo.grid.ColumnModel([
5347         {header: "Ticker", width: 60, sortable: true, locked: true},
5348         {header: "Company Name", width: 150, sortable: true},
5349         {header: "Market Cap.", width: 100, sortable: true},
5350         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5351         {header: "Employees", width: 100, sortable: true, resizable: false}
5352  ]);
5353  </code></pre>
5354  * <p>
5355  
5356  * The config options listed for this class are options which may appear in each
5357  * individual column definition.
5358  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5359  * @constructor
5360  * @param {Object} config An Array of column config objects. See this class's
5361  * config objects for details.
5362 */
5363 Roo.grid.ColumnModel = function(config){
5364         /**
5365      * The config passed into the constructor
5366      */
5367     this.config = config;
5368     this.lookup = {};
5369
5370     // if no id, create one
5371     // if the column does not have a dataIndex mapping,
5372     // map it to the order it is in the config
5373     for(var i = 0, len = config.length; i < len; i++){
5374         var c = config[i];
5375         if(typeof c.dataIndex == "undefined"){
5376             c.dataIndex = i;
5377         }
5378         if(typeof c.renderer == "string"){
5379             c.renderer = Roo.util.Format[c.renderer];
5380         }
5381         if(typeof c.id == "undefined"){
5382             c.id = Roo.id();
5383         }
5384         if(c.editor && c.editor.xtype){
5385             c.editor  = Roo.factory(c.editor, Roo.grid);
5386         }
5387         if(c.editor && c.editor.isFormField){
5388             c.editor = new Roo.grid.GridEditor(c.editor);
5389         }
5390         this.lookup[c.id] = c;
5391     }
5392
5393     /**
5394      * The width of columns which have no width specified (defaults to 100)
5395      * @type Number
5396      */
5397     this.defaultWidth = 100;
5398
5399     /**
5400      * Default sortable of columns which have no sortable specified (defaults to false)
5401      * @type Boolean
5402      */
5403     this.defaultSortable = false;
5404
5405     this.addEvents({
5406         /**
5407              * @event widthchange
5408              * Fires when the width of a column changes.
5409              * @param {ColumnModel} this
5410              * @param {Number} columnIndex The column index
5411              * @param {Number} newWidth The new width
5412              */
5413             "widthchange": true,
5414         /**
5415              * @event headerchange
5416              * Fires when the text of a header changes.
5417              * @param {ColumnModel} this
5418              * @param {Number} columnIndex The column index
5419              * @param {Number} newText The new header text
5420              */
5421             "headerchange": true,
5422         /**
5423              * @event hiddenchange
5424              * Fires when a column is hidden or "unhidden".
5425              * @param {ColumnModel} this
5426              * @param {Number} columnIndex The column index
5427              * @param {Boolean} hidden true if hidden, false otherwise
5428              */
5429             "hiddenchange": true,
5430             /**
5431          * @event columnmoved
5432          * Fires when a column is moved.
5433          * @param {ColumnModel} this
5434          * @param {Number} oldIndex
5435          * @param {Number} newIndex
5436          */
5437         "columnmoved" : true,
5438         /**
5439          * @event columlockchange
5440          * Fires when a column's locked state is changed
5441          * @param {ColumnModel} this
5442          * @param {Number} colIndex
5443          * @param {Boolean} locked true if locked
5444          */
5445         "columnlockchange" : true
5446     });
5447     Roo.grid.ColumnModel.superclass.constructor.call(this);
5448 };
5449 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5450     /**
5451      * @cfg {String} header The header text to display in the Grid view.
5452      */
5453     /**
5454      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5455      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5456      * specified, the column's index is used as an index into the Record's data Array.
5457      */
5458     /**
5459      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5460      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5461      */
5462     /**
5463      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5464      * Defaults to the value of the {@link #defaultSortable} property.
5465      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5466      */
5467     /**
5468      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5469      */
5470     /**
5471      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5472      */
5473     /**
5474      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5475      */
5476     /**
5477      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5478      */
5479     /**
5480      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5481      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5482      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5483      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5484      */
5485        /**
5486      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5487      */
5488     /**
5489      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5490      */
5491     /**
5492      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5493      */
5494     /**
5495      * @cfg {String} cursor (Optional)
5496      */
5497     /**
5498      * @cfg {String} tooltip (Optional)
5499      */
5500     /**
5501      * @cfg {Number} xs (Optional)
5502      */
5503     /**
5504      * @cfg {Number} sm (Optional)
5505      */
5506     /**
5507      * @cfg {Number} md (Optional)
5508      */
5509     /**
5510      * @cfg {Number} lg (Optional)
5511      */
5512     /**
5513      * Returns the id of the column at the specified index.
5514      * @param {Number} index The column index
5515      * @return {String} the id
5516      */
5517     getColumnId : function(index){
5518         return this.config[index].id;
5519     },
5520
5521     /**
5522      * Returns the column for a specified id.
5523      * @param {String} id The column id
5524      * @return {Object} the column
5525      */
5526     getColumnById : function(id){
5527         return this.lookup[id];
5528     },
5529
5530     
5531     /**
5532      * Returns the column for a specified dataIndex.
5533      * @param {String} dataIndex The column dataIndex
5534      * @return {Object|Boolean} the column or false if not found
5535      */
5536     getColumnByDataIndex: function(dataIndex){
5537         var index = this.findColumnIndex(dataIndex);
5538         return index > -1 ? this.config[index] : false;
5539     },
5540     
5541     /**
5542      * Returns the index for a specified column id.
5543      * @param {String} id The column id
5544      * @return {Number} the index, or -1 if not found
5545      */
5546     getIndexById : function(id){
5547         for(var i = 0, len = this.config.length; i < len; i++){
5548             if(this.config[i].id == id){
5549                 return i;
5550             }
5551         }
5552         return -1;
5553     },
5554     
5555     /**
5556      * Returns the index for a specified column dataIndex.
5557      * @param {String} dataIndex The column dataIndex
5558      * @return {Number} the index, or -1 if not found
5559      */
5560     
5561     findColumnIndex : function(dataIndex){
5562         for(var i = 0, len = this.config.length; i < len; i++){
5563             if(this.config[i].dataIndex == dataIndex){
5564                 return i;
5565             }
5566         }
5567         return -1;
5568     },
5569     
5570     
5571     moveColumn : function(oldIndex, newIndex){
5572         var c = this.config[oldIndex];
5573         this.config.splice(oldIndex, 1);
5574         this.config.splice(newIndex, 0, c);
5575         this.dataMap = null;
5576         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5577     },
5578
5579     isLocked : function(colIndex){
5580         return this.config[colIndex].locked === true;
5581     },
5582
5583     setLocked : function(colIndex, value, suppressEvent){
5584         if(this.isLocked(colIndex) == value){
5585             return;
5586         }
5587         this.config[colIndex].locked = value;
5588         if(!suppressEvent){
5589             this.fireEvent("columnlockchange", this, colIndex, value);
5590         }
5591     },
5592
5593     getTotalLockedWidth : function(){
5594         var totalWidth = 0;
5595         for(var i = 0; i < this.config.length; i++){
5596             if(this.isLocked(i) && !this.isHidden(i)){
5597                 this.totalWidth += this.getColumnWidth(i);
5598             }
5599         }
5600         return totalWidth;
5601     },
5602
5603     getLockedCount : function(){
5604         for(var i = 0, len = this.config.length; i < len; i++){
5605             if(!this.isLocked(i)){
5606                 return i;
5607             }
5608         }
5609         
5610         return this.config.length;
5611     },
5612
5613     /**
5614      * Returns the number of columns.
5615      * @return {Number}
5616      */
5617     getColumnCount : function(visibleOnly){
5618         if(visibleOnly === true){
5619             var c = 0;
5620             for(var i = 0, len = this.config.length; i < len; i++){
5621                 if(!this.isHidden(i)){
5622                     c++;
5623                 }
5624             }
5625             return c;
5626         }
5627         return this.config.length;
5628     },
5629
5630     /**
5631      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5632      * @param {Function} fn
5633      * @param {Object} scope (optional)
5634      * @return {Array} result
5635      */
5636     getColumnsBy : function(fn, scope){
5637         var r = [];
5638         for(var i = 0, len = this.config.length; i < len; i++){
5639             var c = this.config[i];
5640             if(fn.call(scope||this, c, i) === true){
5641                 r[r.length] = c;
5642             }
5643         }
5644         return r;
5645     },
5646
5647     /**
5648      * Returns true if the specified column is sortable.
5649      * @param {Number} col The column index
5650      * @return {Boolean}
5651      */
5652     isSortable : function(col){
5653         if(typeof this.config[col].sortable == "undefined"){
5654             return this.defaultSortable;
5655         }
5656         return this.config[col].sortable;
5657     },
5658
5659     /**
5660      * Returns the rendering (formatting) function defined for the column.
5661      * @param {Number} col The column index.
5662      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5663      */
5664     getRenderer : function(col){
5665         if(!this.config[col].renderer){
5666             return Roo.grid.ColumnModel.defaultRenderer;
5667         }
5668         return this.config[col].renderer;
5669     },
5670
5671     /**
5672      * Sets the rendering (formatting) function for a column.
5673      * @param {Number} col The column index
5674      * @param {Function} fn The function to use to process the cell's raw data
5675      * to return HTML markup for the grid view. The render function is called with
5676      * the following parameters:<ul>
5677      * <li>Data value.</li>
5678      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5679      * <li>css A CSS style string to apply to the table cell.</li>
5680      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5681      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5682      * <li>Row index</li>
5683      * <li>Column index</li>
5684      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5685      */
5686     setRenderer : function(col, fn){
5687         this.config[col].renderer = fn;
5688     },
5689
5690     /**
5691      * Returns the width for the specified column.
5692      * @param {Number} col The column index
5693      * @return {Number}
5694      */
5695     getColumnWidth : function(col){
5696         return this.config[col].width * 1 || this.defaultWidth;
5697     },
5698
5699     /**
5700      * Sets the width for a column.
5701      * @param {Number} col The column index
5702      * @param {Number} width The new width
5703      */
5704     setColumnWidth : function(col, width, suppressEvent){
5705         this.config[col].width = width;
5706         this.totalWidth = null;
5707         if(!suppressEvent){
5708              this.fireEvent("widthchange", this, col, width);
5709         }
5710     },
5711
5712     /**
5713      * Returns the total width of all columns.
5714      * @param {Boolean} includeHidden True to include hidden column widths
5715      * @return {Number}
5716      */
5717     getTotalWidth : function(includeHidden){
5718         if(!this.totalWidth){
5719             this.totalWidth = 0;
5720             for(var i = 0, len = this.config.length; i < len; i++){
5721                 if(includeHidden || !this.isHidden(i)){
5722                     this.totalWidth += this.getColumnWidth(i);
5723                 }
5724             }
5725         }
5726         return this.totalWidth;
5727     },
5728
5729     /**
5730      * Returns the header for the specified column.
5731      * @param {Number} col The column index
5732      * @return {String}
5733      */
5734     getColumnHeader : function(col){
5735         return this.config[col].header;
5736     },
5737
5738     /**
5739      * Sets the header for a column.
5740      * @param {Number} col The column index
5741      * @param {String} header The new header
5742      */
5743     setColumnHeader : function(col, header){
5744         this.config[col].header = header;
5745         this.fireEvent("headerchange", this, col, header);
5746     },
5747
5748     /**
5749      * Returns the tooltip for the specified column.
5750      * @param {Number} col The column index
5751      * @return {String}
5752      */
5753     getColumnTooltip : function(col){
5754             return this.config[col].tooltip;
5755     },
5756     /**
5757      * Sets the tooltip for a column.
5758      * @param {Number} col The column index
5759      * @param {String} tooltip The new tooltip
5760      */
5761     setColumnTooltip : function(col, tooltip){
5762             this.config[col].tooltip = tooltip;
5763     },
5764
5765     /**
5766      * Returns the dataIndex for the specified column.
5767      * @param {Number} col The column index
5768      * @return {Number}
5769      */
5770     getDataIndex : function(col){
5771         return this.config[col].dataIndex;
5772     },
5773
5774     /**
5775      * Sets the dataIndex for a column.
5776      * @param {Number} col The column index
5777      * @param {Number} dataIndex The new dataIndex
5778      */
5779     setDataIndex : function(col, dataIndex){
5780         this.config[col].dataIndex = dataIndex;
5781     },
5782
5783     
5784     
5785     /**
5786      * Returns true if the cell is editable.
5787      * @param {Number} colIndex The column index
5788      * @param {Number} rowIndex The row index - this is nto actually used..?
5789      * @return {Boolean}
5790      */
5791     isCellEditable : function(colIndex, rowIndex){
5792         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5793     },
5794
5795     /**
5796      * Returns the editor defined for the cell/column.
5797      * return false or null to disable editing.
5798      * @param {Number} colIndex The column index
5799      * @param {Number} rowIndex The row index
5800      * @return {Object}
5801      */
5802     getCellEditor : function(colIndex, rowIndex){
5803         return this.config[colIndex].editor;
5804     },
5805
5806     /**
5807      * Sets if a column is editable.
5808      * @param {Number} col The column index
5809      * @param {Boolean} editable True if the column is editable
5810      */
5811     setEditable : function(col, editable){
5812         this.config[col].editable = editable;
5813     },
5814
5815
5816     /**
5817      * Returns true if the column is hidden.
5818      * @param {Number} colIndex The column index
5819      * @return {Boolean}
5820      */
5821     isHidden : function(colIndex){
5822         return this.config[colIndex].hidden;
5823     },
5824
5825
5826     /**
5827      * Returns true if the column width cannot be changed
5828      */
5829     isFixed : function(colIndex){
5830         return this.config[colIndex].fixed;
5831     },
5832
5833     /**
5834      * Returns true if the column can be resized
5835      * @return {Boolean}
5836      */
5837     isResizable : function(colIndex){
5838         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5839     },
5840     /**
5841      * Sets if a column is hidden.
5842      * @param {Number} colIndex The column index
5843      * @param {Boolean} hidden True if the column is hidden
5844      */
5845     setHidden : function(colIndex, hidden){
5846         this.config[colIndex].hidden = hidden;
5847         this.totalWidth = null;
5848         this.fireEvent("hiddenchange", this, colIndex, hidden);
5849     },
5850
5851     /**
5852      * Sets the editor for a column.
5853      * @param {Number} col The column index
5854      * @param {Object} editor The editor object
5855      */
5856     setEditor : function(col, editor){
5857         this.config[col].editor = editor;
5858     }
5859 });
5860
5861 Roo.grid.ColumnModel.defaultRenderer = function(value)
5862 {
5863     if(typeof value == "object") {
5864         return value;
5865     }
5866         if(typeof value == "string" && value.length < 1){
5867             return "&#160;";
5868         }
5869     
5870         return String.format("{0}", value);
5871 };
5872
5873 // Alias for backwards compatibility
5874 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5875 /*
5876  * Based on:
5877  * Ext JS Library 1.1.1
5878  * Copyright(c) 2006-2007, Ext JS, LLC.
5879  *
5880  * Originally Released Under LGPL - original licence link has changed is not relivant.
5881  *
5882  * Fork - LGPL
5883  * <script type="text/javascript">
5884  */
5885  
5886 /**
5887  * @class Roo.LoadMask
5888  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5889  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5890  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5891  * element's UpdateManager load indicator and will be destroyed after the initial load.
5892  * @constructor
5893  * Create a new LoadMask
5894  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5895  * @param {Object} config The config object
5896  */
5897 Roo.LoadMask = function(el, config){
5898     this.el = Roo.get(el);
5899     Roo.apply(this, config);
5900     if(this.store){
5901         this.store.on('beforeload', this.onBeforeLoad, this);
5902         this.store.on('load', this.onLoad, this);
5903         this.store.on('loadexception', this.onLoadException, this);
5904         this.removeMask = false;
5905     }else{
5906         var um = this.el.getUpdateManager();
5907         um.showLoadIndicator = false; // disable the default indicator
5908         um.on('beforeupdate', this.onBeforeLoad, this);
5909         um.on('update', this.onLoad, this);
5910         um.on('failure', this.onLoad, this);
5911         this.removeMask = true;
5912     }
5913 };
5914
5915 Roo.LoadMask.prototype = {
5916     /**
5917      * @cfg {Boolean} removeMask
5918      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5919      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5920      */
5921     /**
5922      * @cfg {String} msg
5923      * The text to display in a centered loading message box (defaults to 'Loading...')
5924      */
5925     msg : 'Loading...',
5926     /**
5927      * @cfg {String} msgCls
5928      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5929      */
5930     msgCls : 'x-mask-loading',
5931
5932     /**
5933      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5934      * @type Boolean
5935      */
5936     disabled: false,
5937
5938     /**
5939      * Disables the mask to prevent it from being displayed
5940      */
5941     disable : function(){
5942        this.disabled = true;
5943     },
5944
5945     /**
5946      * Enables the mask so that it can be displayed
5947      */
5948     enable : function(){
5949         this.disabled = false;
5950     },
5951     
5952     onLoadException : function()
5953     {
5954         Roo.log(arguments);
5955         
5956         if (typeof(arguments[3]) != 'undefined') {
5957             Roo.MessageBox.alert("Error loading",arguments[3]);
5958         } 
5959         /*
5960         try {
5961             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5962                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5963             }   
5964         } catch(e) {
5965             
5966         }
5967         */
5968     
5969         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5970     },
5971     // private
5972     onLoad : function()
5973     {
5974         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5975     },
5976
5977     // private
5978     onBeforeLoad : function(){
5979         if(!this.disabled){
5980             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5981         }
5982     },
5983
5984     // private
5985     destroy : function(){
5986         if(this.store){
5987             this.store.un('beforeload', this.onBeforeLoad, this);
5988             this.store.un('load', this.onLoad, this);
5989             this.store.un('loadexception', this.onLoadException, this);
5990         }else{
5991             var um = this.el.getUpdateManager();
5992             um.un('beforeupdate', this.onBeforeLoad, this);
5993             um.un('update', this.onLoad, this);
5994             um.un('failure', this.onLoad, this);
5995         }
5996     }
5997 };/*
5998  * - LGPL
5999  *
6000  * table
6001  * 
6002  */
6003
6004 /**
6005  * @class Roo.bootstrap.Table
6006  * @extends Roo.bootstrap.Component
6007  * Bootstrap Table class
6008  * @cfg {String} cls table class
6009  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6010  * @cfg {String} bgcolor Specifies the background color for a table
6011  * @cfg {Number} border Specifies whether the table cells should have borders or not
6012  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6013  * @cfg {Number} cellspacing Specifies the space between cells
6014  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6015  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6016  * @cfg {String} sortable Specifies that the table should be sortable
6017  * @cfg {String} summary Specifies a summary of the content of a table
6018  * @cfg {Number} width Specifies the width of a table
6019  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6020  * 
6021  * @cfg {boolean} striped Should the rows be alternative striped
6022  * @cfg {boolean} bordered Add borders to the table
6023  * @cfg {boolean} hover Add hover highlighting
6024  * @cfg {boolean} condensed Format condensed
6025  * @cfg {boolean} responsive Format condensed
6026  * @cfg {Boolean} loadMask (true|false) default false
6027  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6028  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6029  * @cfg {Boolean} rowSelection (true|false) default false
6030  * @cfg {Boolean} cellSelection (true|false) default false
6031  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6032  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6033  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6034  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6035  
6036  * 
6037  * @constructor
6038  * Create a new Table
6039  * @param {Object} config The config object
6040  */
6041
6042 Roo.bootstrap.Table = function(config){
6043     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6044     
6045   
6046     
6047     // BC...
6048     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6049     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6050     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6051     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6052     
6053     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6054     if (this.sm) {
6055         this.sm.grid = this;
6056         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6057         this.sm = this.selModel;
6058         this.sm.xmodule = this.xmodule || false;
6059     }
6060     
6061     if (this.cm && typeof(this.cm.config) == 'undefined') {
6062         this.colModel = new Roo.grid.ColumnModel(this.cm);
6063         this.cm = this.colModel;
6064         this.cm.xmodule = this.xmodule || false;
6065     }
6066     if (this.store) {
6067         this.store= Roo.factory(this.store, Roo.data);
6068         this.ds = this.store;
6069         this.ds.xmodule = this.xmodule || false;
6070          
6071     }
6072     if (this.footer && this.store) {
6073         this.footer.dataSource = this.ds;
6074         this.footer = Roo.factory(this.footer);
6075     }
6076     
6077     /** @private */
6078     this.addEvents({
6079         /**
6080          * @event cellclick
6081          * Fires when a cell is clicked
6082          * @param {Roo.bootstrap.Table} this
6083          * @param {Roo.Element} el
6084          * @param {Number} rowIndex
6085          * @param {Number} columnIndex
6086          * @param {Roo.EventObject} e
6087          */
6088         "cellclick" : true,
6089         /**
6090          * @event celldblclick
6091          * Fires when a cell is double clicked
6092          * @param {Roo.bootstrap.Table} this
6093          * @param {Roo.Element} el
6094          * @param {Number} rowIndex
6095          * @param {Number} columnIndex
6096          * @param {Roo.EventObject} e
6097          */
6098         "celldblclick" : true,
6099         /**
6100          * @event rowclick
6101          * Fires when a row is clicked
6102          * @param {Roo.bootstrap.Table} this
6103          * @param {Roo.Element} el
6104          * @param {Number} rowIndex
6105          * @param {Roo.EventObject} e
6106          */
6107         "rowclick" : true,
6108         /**
6109          * @event rowdblclick
6110          * Fires when a row is double clicked
6111          * @param {Roo.bootstrap.Table} this
6112          * @param {Roo.Element} el
6113          * @param {Number} rowIndex
6114          * @param {Roo.EventObject} e
6115          */
6116         "rowdblclick" : true,
6117         /**
6118          * @event mouseover
6119          * Fires when a mouseover occur
6120          * @param {Roo.bootstrap.Table} this
6121          * @param {Roo.Element} el
6122          * @param {Number} rowIndex
6123          * @param {Number} columnIndex
6124          * @param {Roo.EventObject} e
6125          */
6126         "mouseover" : true,
6127         /**
6128          * @event mouseout
6129          * Fires when a mouseout occur
6130          * @param {Roo.bootstrap.Table} this
6131          * @param {Roo.Element} el
6132          * @param {Number} rowIndex
6133          * @param {Number} columnIndex
6134          * @param {Roo.EventObject} e
6135          */
6136         "mouseout" : true,
6137         /**
6138          * @event rowclass
6139          * Fires when a row is rendered, so you can change add a style to it.
6140          * @param {Roo.bootstrap.Table} this
6141          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6142          */
6143         'rowclass' : true,
6144           /**
6145          * @event rowsrendered
6146          * Fires when all the  rows have been rendered
6147          * @param {Roo.bootstrap.Table} this
6148          */
6149         'rowsrendered' : true,
6150         /**
6151          * @event contextmenu
6152          * The raw contextmenu event for the entire grid.
6153          * @param {Roo.EventObject} e
6154          */
6155         "contextmenu" : true,
6156         /**
6157          * @event rowcontextmenu
6158          * Fires when a row is right clicked
6159          * @param {Roo.bootstrap.Table} this
6160          * @param {Number} rowIndex
6161          * @param {Roo.EventObject} e
6162          */
6163         "rowcontextmenu" : true,
6164         /**
6165          * @event cellcontextmenu
6166          * Fires when a cell is right clicked
6167          * @param {Roo.bootstrap.Table} this
6168          * @param {Number} rowIndex
6169          * @param {Number} cellIndex
6170          * @param {Roo.EventObject} e
6171          */
6172          "cellcontextmenu" : true,
6173          /**
6174          * @event headercontextmenu
6175          * Fires when a header is right clicked
6176          * @param {Roo.bootstrap.Table} this
6177          * @param {Number} columnIndex
6178          * @param {Roo.EventObject} e
6179          */
6180         "headercontextmenu" : true
6181     });
6182 };
6183
6184 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6185     
6186     cls: false,
6187     align: false,
6188     bgcolor: false,
6189     border: false,
6190     cellpadding: false,
6191     cellspacing: false,
6192     frame: false,
6193     rules: false,
6194     sortable: false,
6195     summary: false,
6196     width: false,
6197     striped : false,
6198     scrollBody : false,
6199     bordered: false,
6200     hover:  false,
6201     condensed : false,
6202     responsive : false,
6203     sm : false,
6204     cm : false,
6205     store : false,
6206     loadMask : false,
6207     footerShow : true,
6208     headerShow : true,
6209   
6210     rowSelection : false,
6211     cellSelection : false,
6212     layout : false,
6213     
6214     // Roo.Element - the tbody
6215     mainBody: false,
6216     // Roo.Element - thead element
6217     mainHead: false,
6218     
6219     container: false, // used by gridpanel...
6220     
6221     lazyLoad : false,
6222     
6223     CSS : Roo.util.CSS,
6224     
6225     auto_hide_footer : false,
6226     
6227     getAutoCreate : function()
6228     {
6229         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6230         
6231         cfg = {
6232             tag: 'table',
6233             cls : 'table',
6234             cn : []
6235         };
6236         if (this.scrollBody) {
6237             cfg.cls += ' table-body-fixed';
6238         }    
6239         if (this.striped) {
6240             cfg.cls += ' table-striped';
6241         }
6242         
6243         if (this.hover) {
6244             cfg.cls += ' table-hover';
6245         }
6246         if (this.bordered) {
6247             cfg.cls += ' table-bordered';
6248         }
6249         if (this.condensed) {
6250             cfg.cls += ' table-condensed';
6251         }
6252         if (this.responsive) {
6253             cfg.cls += ' table-responsive';
6254         }
6255         
6256         if (this.cls) {
6257             cfg.cls+=  ' ' +this.cls;
6258         }
6259         
6260         // this lot should be simplifed...
6261         var _t = this;
6262         var cp = [
6263             'align',
6264             'bgcolor',
6265             'border',
6266             'cellpadding',
6267             'cellspacing',
6268             'frame',
6269             'rules',
6270             'sortable',
6271             'summary',
6272             'width'
6273         ].forEach(function(k) {
6274             if (_t[k]) {
6275                 cfg[k] = _t[k];
6276             }
6277         });
6278         
6279         
6280         if (this.layout) {
6281             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6282         }
6283         
6284         if(this.store || this.cm){
6285             if(this.headerShow){
6286                 cfg.cn.push(this.renderHeader());
6287             }
6288             
6289             cfg.cn.push(this.renderBody());
6290             
6291             if(this.footerShow){
6292                 cfg.cn.push(this.renderFooter());
6293             }
6294             // where does this come from?
6295             //cfg.cls+=  ' TableGrid';
6296         }
6297         
6298         return { cn : [ cfg ] };
6299     },
6300     
6301     initEvents : function()
6302     {   
6303         if(!this.store || !this.cm){
6304             return;
6305         }
6306         if (this.selModel) {
6307             this.selModel.initEvents();
6308         }
6309         
6310         
6311         //Roo.log('initEvents with ds!!!!');
6312         
6313         this.mainBody = this.el.select('tbody', true).first();
6314         this.mainHead = this.el.select('thead', true).first();
6315         this.mainFoot = this.el.select('tfoot', true).first();
6316         
6317         
6318         
6319         var _this = this;
6320         
6321         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6322             e.on('click', _this.sort, _this);
6323         });
6324         
6325         this.mainBody.on("click", this.onClick, this);
6326         this.mainBody.on("dblclick", this.onDblClick, this);
6327         
6328         // why is this done????? = it breaks dialogs??
6329         //this.parent().el.setStyle('position', 'relative');
6330         
6331         
6332         if (this.footer) {
6333             this.footer.parentId = this.id;
6334             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6335             
6336             if(this.lazyLoad){
6337                 this.el.select('tfoot tr td').first().addClass('hide');
6338             }
6339         } 
6340         
6341         if(this.loadMask) {
6342             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6343         }
6344         
6345         this.store.on('load', this.onLoad, this);
6346         this.store.on('beforeload', this.onBeforeLoad, this);
6347         this.store.on('update', this.onUpdate, this);
6348         this.store.on('add', this.onAdd, this);
6349         this.store.on("clear", this.clear, this);
6350         
6351         this.el.on("contextmenu", this.onContextMenu, this);
6352         
6353         this.mainBody.on('scroll', this.onBodyScroll, this);
6354         
6355         this.cm.on("headerchange", this.onHeaderChange, this);
6356         
6357         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6358         
6359     },
6360     
6361     onContextMenu : function(e, t)
6362     {
6363         this.processEvent("contextmenu", e);
6364     },
6365     
6366     processEvent : function(name, e)
6367     {
6368         if (name != 'touchstart' ) {
6369             this.fireEvent(name, e);    
6370         }
6371         
6372         var t = e.getTarget();
6373         
6374         var cell = Roo.get(t);
6375         
6376         if(!cell){
6377             return;
6378         }
6379         
6380         if(cell.findParent('tfoot', false, true)){
6381             return;
6382         }
6383         
6384         if(cell.findParent('thead', false, true)){
6385             
6386             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6387                 cell = Roo.get(t).findParent('th', false, true);
6388                 if (!cell) {
6389                     Roo.log("failed to find th in thead?");
6390                     Roo.log(e.getTarget());
6391                     return;
6392                 }
6393             }
6394             
6395             var cellIndex = cell.dom.cellIndex;
6396             
6397             var ename = name == 'touchstart' ? 'click' : name;
6398             this.fireEvent("header" + ename, this, cellIndex, e);
6399             
6400             return;
6401         }
6402         
6403         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6404             cell = Roo.get(t).findParent('td', false, true);
6405             if (!cell) {
6406                 Roo.log("failed to find th in tbody?");
6407                 Roo.log(e.getTarget());
6408                 return;
6409             }
6410         }
6411         
6412         var row = cell.findParent('tr', false, true);
6413         var cellIndex = cell.dom.cellIndex;
6414         var rowIndex = row.dom.rowIndex - 1;
6415         
6416         if(row !== false){
6417             
6418             this.fireEvent("row" + name, this, rowIndex, e);
6419             
6420             if(cell !== false){
6421             
6422                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6423             }
6424         }
6425         
6426     },
6427     
6428     onMouseover : function(e, el)
6429     {
6430         var cell = Roo.get(el);
6431         
6432         if(!cell){
6433             return;
6434         }
6435         
6436         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6437             cell = cell.findParent('td', false, true);
6438         }
6439         
6440         var row = cell.findParent('tr', false, true);
6441         var cellIndex = cell.dom.cellIndex;
6442         var rowIndex = row.dom.rowIndex - 1; // start from 0
6443         
6444         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6445         
6446     },
6447     
6448     onMouseout : function(e, el)
6449     {
6450         var cell = Roo.get(el);
6451         
6452         if(!cell){
6453             return;
6454         }
6455         
6456         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6457             cell = cell.findParent('td', false, true);
6458         }
6459         
6460         var row = cell.findParent('tr', false, true);
6461         var cellIndex = cell.dom.cellIndex;
6462         var rowIndex = row.dom.rowIndex - 1; // start from 0
6463         
6464         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6465         
6466     },
6467     
6468     onClick : function(e, el)
6469     {
6470         var cell = Roo.get(el);
6471         
6472         if(!cell || (!this.cellSelection && !this.rowSelection)){
6473             return;
6474         }
6475         
6476         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6477             cell = cell.findParent('td', false, true);
6478         }
6479         
6480         if(!cell || typeof(cell) == 'undefined'){
6481             return;
6482         }
6483         
6484         var row = cell.findParent('tr', false, true);
6485         
6486         if(!row || typeof(row) == 'undefined'){
6487             return;
6488         }
6489         
6490         var cellIndex = cell.dom.cellIndex;
6491         var rowIndex = this.getRowIndex(row);
6492         
6493         // why??? - should these not be based on SelectionModel?
6494         if(this.cellSelection){
6495             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6496         }
6497         
6498         if(this.rowSelection){
6499             this.fireEvent('rowclick', this, row, rowIndex, e);
6500         }
6501         
6502         
6503     },
6504         
6505     onDblClick : function(e,el)
6506     {
6507         var cell = Roo.get(el);
6508         
6509         if(!cell || (!this.cellSelection && !this.rowSelection)){
6510             return;
6511         }
6512         
6513         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6514             cell = cell.findParent('td', false, true);
6515         }
6516         
6517         if(!cell || typeof(cell) == 'undefined'){
6518             return;
6519         }
6520         
6521         var row = cell.findParent('tr', false, true);
6522         
6523         if(!row || typeof(row) == 'undefined'){
6524             return;
6525         }
6526         
6527         var cellIndex = cell.dom.cellIndex;
6528         var rowIndex = this.getRowIndex(row);
6529         
6530         if(this.cellSelection){
6531             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6532         }
6533         
6534         if(this.rowSelection){
6535             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6536         }
6537     },
6538     
6539     sort : function(e,el)
6540     {
6541         var col = Roo.get(el);
6542         
6543         if(!col.hasClass('sortable')){
6544             return;
6545         }
6546         
6547         var sort = col.attr('sort');
6548         var dir = 'ASC';
6549         
6550         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6551             dir = 'DESC';
6552         }
6553         
6554         this.store.sortInfo = {field : sort, direction : dir};
6555         
6556         if (this.footer) {
6557             Roo.log("calling footer first");
6558             this.footer.onClick('first');
6559         } else {
6560         
6561             this.store.load({ params : { start : 0 } });
6562         }
6563     },
6564     
6565     renderHeader : function()
6566     {
6567         var header = {
6568             tag: 'thead',
6569             cn : []
6570         };
6571         
6572         var cm = this.cm;
6573         this.totalWidth = 0;
6574         
6575         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6576             
6577             var config = cm.config[i];
6578             
6579             var c = {
6580                 tag: 'th',
6581                 cls : 'x-hcol-' + i,
6582                 style : '',
6583                 html: cm.getColumnHeader(i)
6584             };
6585             
6586             var hh = '';
6587             
6588             if(typeof(config.sortable) != 'undefined' && config.sortable){
6589                 c.cls = 'sortable';
6590                 c.html = '<i class="glyphicon"></i>' + c.html;
6591             }
6592             
6593             if(typeof(config.lgHeader) != 'undefined'){
6594                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6595             }
6596             
6597             if(typeof(config.mdHeader) != 'undefined'){
6598                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6599             }
6600             
6601             if(typeof(config.smHeader) != 'undefined'){
6602                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6603             }
6604             
6605             if(typeof(config.xsHeader) != 'undefined'){
6606                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6607             }
6608             
6609             if(hh.length){
6610                 c.html = hh;
6611             }
6612             
6613             if(typeof(config.tooltip) != 'undefined'){
6614                 c.tooltip = config.tooltip;
6615             }
6616             
6617             if(typeof(config.colspan) != 'undefined'){
6618                 c.colspan = config.colspan;
6619             }
6620             
6621             if(typeof(config.hidden) != 'undefined' && config.hidden){
6622                 c.style += ' display:none;';
6623             }
6624             
6625             if(typeof(config.dataIndex) != 'undefined'){
6626                 c.sort = config.dataIndex;
6627             }
6628             
6629            
6630             
6631             if(typeof(config.align) != 'undefined' && config.align.length){
6632                 c.style += ' text-align:' + config.align + ';';
6633             }
6634             
6635             if(typeof(config.width) != 'undefined'){
6636                 c.style += ' width:' + config.width + 'px;';
6637                 this.totalWidth += config.width;
6638             } else {
6639                 this.totalWidth += 100; // assume minimum of 100 per column?
6640             }
6641             
6642             if(typeof(config.cls) != 'undefined'){
6643                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6644             }
6645             
6646             ['xs','sm','md','lg'].map(function(size){
6647                 
6648                 if(typeof(config[size]) == 'undefined'){
6649                     return;
6650                 }
6651                 
6652                 if (!config[size]) { // 0 = hidden
6653                     c.cls += ' hidden-' + size;
6654                     return;
6655                 }
6656                 
6657                 c.cls += ' col-' + size + '-' + config[size];
6658
6659             });
6660             
6661             header.cn.push(c)
6662         }
6663         
6664         return header;
6665     },
6666     
6667     renderBody : function()
6668     {
6669         var body = {
6670             tag: 'tbody',
6671             cn : [
6672                 {
6673                     tag: 'tr',
6674                     cn : [
6675                         {
6676                             tag : 'td',
6677                             colspan :  this.cm.getColumnCount()
6678                         }
6679                     ]
6680                 }
6681             ]
6682         };
6683         
6684         return body;
6685     },
6686     
6687     renderFooter : function()
6688     {
6689         var footer = {
6690             tag: 'tfoot',
6691             cn : [
6692                 {
6693                     tag: 'tr',
6694                     cn : [
6695                         {
6696                             tag : 'td',
6697                             colspan :  this.cm.getColumnCount()
6698                         }
6699                     ]
6700                 }
6701             ]
6702         };
6703         
6704         return footer;
6705     },
6706     
6707     
6708     
6709     onLoad : function()
6710     {
6711 //        Roo.log('ds onload');
6712         this.clear();
6713         
6714         var _this = this;
6715         var cm = this.cm;
6716         var ds = this.store;
6717         
6718         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6719             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6720             if (_this.store.sortInfo) {
6721                     
6722                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6723                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6724                 }
6725                 
6726                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6727                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6728                 }
6729             }
6730         });
6731         
6732         var tbody =  this.mainBody;
6733               
6734         if(ds.getCount() > 0){
6735             ds.data.each(function(d,rowIndex){
6736                 var row =  this.renderRow(cm, ds, rowIndex);
6737                 
6738                 tbody.createChild(row);
6739                 
6740                 var _this = this;
6741                 
6742                 if(row.cellObjects.length){
6743                     Roo.each(row.cellObjects, function(r){
6744                         _this.renderCellObject(r);
6745                     })
6746                 }
6747                 
6748             }, this);
6749         }
6750         
6751         var tfoot = this.el.select('tfoot', true).first();
6752         
6753         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6754             
6755             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6756             
6757             var total = this.ds.getTotalCount();
6758             
6759             if(this.footer.pageSize < total){
6760                 this.mainFoot.show();
6761             }
6762         }
6763         
6764         Roo.each(this.el.select('tbody td', true).elements, function(e){
6765             e.on('mouseover', _this.onMouseover, _this);
6766         });
6767         
6768         Roo.each(this.el.select('tbody td', true).elements, function(e){
6769             e.on('mouseout', _this.onMouseout, _this);
6770         });
6771         this.fireEvent('rowsrendered', this);
6772         
6773         this.autoSize();
6774     },
6775     
6776     
6777     onUpdate : function(ds,record)
6778     {
6779         this.refreshRow(record);
6780         this.autoSize();
6781     },
6782     
6783     onRemove : function(ds, record, index, isUpdate){
6784         if(isUpdate !== true){
6785             this.fireEvent("beforerowremoved", this, index, record);
6786         }
6787         var bt = this.mainBody.dom;
6788         
6789         var rows = this.el.select('tbody > tr', true).elements;
6790         
6791         if(typeof(rows[index]) != 'undefined'){
6792             bt.removeChild(rows[index].dom);
6793         }
6794         
6795 //        if(bt.rows[index]){
6796 //            bt.removeChild(bt.rows[index]);
6797 //        }
6798         
6799         if(isUpdate !== true){
6800             //this.stripeRows(index);
6801             //this.syncRowHeights(index, index);
6802             //this.layout();
6803             this.fireEvent("rowremoved", this, index, record);
6804         }
6805     },
6806     
6807     onAdd : function(ds, records, rowIndex)
6808     {
6809         //Roo.log('on Add called');
6810         // - note this does not handle multiple adding very well..
6811         var bt = this.mainBody.dom;
6812         for (var i =0 ; i < records.length;i++) {
6813             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6814             //Roo.log(records[i]);
6815             //Roo.log(this.store.getAt(rowIndex+i));
6816             this.insertRow(this.store, rowIndex + i, false);
6817             return;
6818         }
6819         
6820     },
6821     
6822     
6823     refreshRow : function(record){
6824         var ds = this.store, index;
6825         if(typeof record == 'number'){
6826             index = record;
6827             record = ds.getAt(index);
6828         }else{
6829             index = ds.indexOf(record);
6830         }
6831         this.insertRow(ds, index, true);
6832         this.autoSize();
6833         this.onRemove(ds, record, index+1, true);
6834         this.autoSize();
6835         //this.syncRowHeights(index, index);
6836         //this.layout();
6837         this.fireEvent("rowupdated", this, index, record);
6838     },
6839     
6840     insertRow : function(dm, rowIndex, isUpdate){
6841         
6842         if(!isUpdate){
6843             this.fireEvent("beforerowsinserted", this, rowIndex);
6844         }
6845             //var s = this.getScrollState();
6846         var row = this.renderRow(this.cm, this.store, rowIndex);
6847         // insert before rowIndex..
6848         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6849         
6850         var _this = this;
6851                 
6852         if(row.cellObjects.length){
6853             Roo.each(row.cellObjects, function(r){
6854                 _this.renderCellObject(r);
6855             })
6856         }
6857             
6858         if(!isUpdate){
6859             this.fireEvent("rowsinserted", this, rowIndex);
6860             //this.syncRowHeights(firstRow, lastRow);
6861             //this.stripeRows(firstRow);
6862             //this.layout();
6863         }
6864         
6865     },
6866     
6867     
6868     getRowDom : function(rowIndex)
6869     {
6870         var rows = this.el.select('tbody > tr', true).elements;
6871         
6872         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6873         
6874     },
6875     // returns the object tree for a tr..
6876   
6877     
6878     renderRow : function(cm, ds, rowIndex) 
6879     {
6880         var d = ds.getAt(rowIndex);
6881         
6882         var row = {
6883             tag : 'tr',
6884             cls : 'x-row-' + rowIndex,
6885             cn : []
6886         };
6887             
6888         var cellObjects = [];
6889         
6890         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6891             var config = cm.config[i];
6892             
6893             var renderer = cm.getRenderer(i);
6894             var value = '';
6895             var id = false;
6896             
6897             if(typeof(renderer) !== 'undefined'){
6898                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6899             }
6900             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6901             // and are rendered into the cells after the row is rendered - using the id for the element.
6902             
6903             if(typeof(value) === 'object'){
6904                 id = Roo.id();
6905                 cellObjects.push({
6906                     container : id,
6907                     cfg : value 
6908                 })
6909             }
6910             
6911             var rowcfg = {
6912                 record: d,
6913                 rowIndex : rowIndex,
6914                 colIndex : i,
6915                 rowClass : ''
6916             };
6917
6918             this.fireEvent('rowclass', this, rowcfg);
6919             
6920             var td = {
6921                 tag: 'td',
6922                 cls : rowcfg.rowClass + ' x-col-' + i,
6923                 style: '',
6924                 html: (typeof(value) === 'object') ? '' : value
6925             };
6926             
6927             if (id) {
6928                 td.id = id;
6929             }
6930             
6931             if(typeof(config.colspan) != 'undefined'){
6932                 td.colspan = config.colspan;
6933             }
6934             
6935             if(typeof(config.hidden) != 'undefined' && config.hidden){
6936                 td.style += ' display:none;';
6937             }
6938             
6939             if(typeof(config.align) != 'undefined' && config.align.length){
6940                 td.style += ' text-align:' + config.align + ';';
6941             }
6942             if(typeof(config.valign) != 'undefined' && config.valign.length){
6943                 td.style += ' vertical-align:' + config.valign + ';';
6944             }
6945             
6946             if(typeof(config.width) != 'undefined'){
6947                 td.style += ' width:' +  config.width + 'px;';
6948             }
6949             
6950             if(typeof(config.cursor) != 'undefined'){
6951                 td.style += ' cursor:' +  config.cursor + ';';
6952             }
6953             
6954             if(typeof(config.cls) != 'undefined'){
6955                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6956             }
6957             
6958             ['xs','sm','md','lg'].map(function(size){
6959                 
6960                 if(typeof(config[size]) == 'undefined'){
6961                     return;
6962                 }
6963                 
6964                 if (!config[size]) { // 0 = hidden
6965                     td.cls += ' hidden-' + size;
6966                     return;
6967                 }
6968                 
6969                 td.cls += ' col-' + size + '-' + config[size];
6970
6971             });
6972             
6973             row.cn.push(td);
6974            
6975         }
6976         
6977         row.cellObjects = cellObjects;
6978         
6979         return row;
6980           
6981     },
6982     
6983     
6984     
6985     onBeforeLoad : function()
6986     {
6987         
6988     },
6989      /**
6990      * Remove all rows
6991      */
6992     clear : function()
6993     {
6994         this.el.select('tbody', true).first().dom.innerHTML = '';
6995     },
6996     /**
6997      * Show or hide a row.
6998      * @param {Number} rowIndex to show or hide
6999      * @param {Boolean} state hide
7000      */
7001     setRowVisibility : function(rowIndex, state)
7002     {
7003         var bt = this.mainBody.dom;
7004         
7005         var rows = this.el.select('tbody > tr', true).elements;
7006         
7007         if(typeof(rows[rowIndex]) == 'undefined'){
7008             return;
7009         }
7010         rows[rowIndex].dom.style.display = state ? '' : 'none';
7011     },
7012     
7013     
7014     getSelectionModel : function(){
7015         if(!this.selModel){
7016             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7017         }
7018         return this.selModel;
7019     },
7020     /*
7021      * Render the Roo.bootstrap object from renderder
7022      */
7023     renderCellObject : function(r)
7024     {
7025         var _this = this;
7026         
7027         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7028         
7029         var t = r.cfg.render(r.container);
7030         
7031         if(r.cfg.cn){
7032             Roo.each(r.cfg.cn, function(c){
7033                 var child = {
7034                     container: t.getChildContainer(),
7035                     cfg: c
7036                 };
7037                 _this.renderCellObject(child);
7038             })
7039         }
7040     },
7041     
7042     getRowIndex : function(row)
7043     {
7044         var rowIndex = -1;
7045         
7046         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7047             if(el != row){
7048                 return;
7049             }
7050             
7051             rowIndex = index;
7052         });
7053         
7054         return rowIndex;
7055     },
7056      /**
7057      * Returns the grid's underlying element = used by panel.Grid
7058      * @return {Element} The element
7059      */
7060     getGridEl : function(){
7061         return this.el;
7062     },
7063      /**
7064      * Forces a resize - used by panel.Grid
7065      * @return {Element} The element
7066      */
7067     autoSize : function()
7068     {
7069         //var ctr = Roo.get(this.container.dom.parentElement);
7070         var ctr = Roo.get(this.el.dom);
7071         
7072         var thd = this.getGridEl().select('thead',true).first();
7073         var tbd = this.getGridEl().select('tbody', true).first();
7074         var tfd = this.getGridEl().select('tfoot', true).first();
7075         
7076         var cw = ctr.getWidth();
7077         
7078         if (tbd) {
7079             
7080             tbd.setSize(ctr.getWidth(),
7081                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7082             );
7083             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7084             cw -= barsize;
7085         }
7086         cw = Math.max(cw, this.totalWidth);
7087         this.getGridEl().select('tr',true).setWidth(cw);
7088         // resize 'expandable coloumn?
7089         
7090         return; // we doe not have a view in this design..
7091         
7092     },
7093     onBodyScroll: function()
7094     {
7095         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7096         if(this.mainHead){
7097             this.mainHead.setStyle({
7098                 'position' : 'relative',
7099                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7100             });
7101         }
7102         
7103         if(this.lazyLoad){
7104             
7105             var scrollHeight = this.mainBody.dom.scrollHeight;
7106             
7107             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7108             
7109             var height = this.mainBody.getHeight();
7110             
7111             if(scrollHeight - height == scrollTop) {
7112                 
7113                 var total = this.ds.getTotalCount();
7114                 
7115                 if(this.footer.cursor + this.footer.pageSize < total){
7116                     
7117                     this.footer.ds.load({
7118                         params : {
7119                             start : this.footer.cursor + this.footer.pageSize,
7120                             limit : this.footer.pageSize
7121                         },
7122                         add : true
7123                     });
7124                 }
7125             }
7126             
7127         }
7128     },
7129     
7130     onHeaderChange : function()
7131     {
7132         var header = this.renderHeader();
7133         var table = this.el.select('table', true).first();
7134         
7135         this.mainHead.remove();
7136         this.mainHead = table.createChild(header, this.mainBody, false);
7137     },
7138     
7139     onHiddenChange : function(colModel, colIndex, hidden)
7140     {
7141         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7142         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7143         
7144         this.CSS.updateRule(thSelector, "display", "");
7145         this.CSS.updateRule(tdSelector, "display", "");
7146         
7147         if(hidden){
7148             this.CSS.updateRule(thSelector, "display", "none");
7149             this.CSS.updateRule(tdSelector, "display", "none");
7150         }
7151         
7152         this.onHeaderChange();
7153         this.onLoad();
7154     },
7155     
7156     setColumnWidth: function(col_index, width)
7157     {
7158         // width = "md-2 xs-2..."
7159         if(!this.colModel.config[col_index]) {
7160             return;
7161         }
7162         
7163         var w = width.split(" ");
7164         
7165         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7166         
7167         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7168         
7169         
7170         for(var j = 0; j < w.length; j++) {
7171             
7172             if(!w[j]) {
7173                 continue;
7174             }
7175             
7176             var size_cls = w[j].split("-");
7177             
7178             if(!Number.isInteger(size_cls[1] * 1)) {
7179                 continue;
7180             }
7181             
7182             if(!this.colModel.config[col_index][size_cls[0]]) {
7183                 continue;
7184             }
7185             
7186             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7187                 continue;
7188             }
7189             
7190             h_row[0].classList.replace(
7191                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7192                 "col-"+size_cls[0]+"-"+size_cls[1]
7193             );
7194             
7195             for(var i = 0; i < rows.length; i++) {
7196                 
7197                 var size_cls = w[j].split("-");
7198                 
7199                 if(!Number.isInteger(size_cls[1] * 1)) {
7200                     continue;
7201                 }
7202                 
7203                 if(!this.colModel.config[col_index][size_cls[0]]) {
7204                     continue;
7205                 }
7206                 
7207                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7208                     continue;
7209                 }
7210                 
7211                 rows[i].classList.replace(
7212                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7213                     "col-"+size_cls[0]+"-"+size_cls[1]
7214                 );
7215             }
7216             
7217             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7218         }
7219     }
7220 });
7221
7222  
7223
7224  /*
7225  * - LGPL
7226  *
7227  * table cell
7228  * 
7229  */
7230
7231 /**
7232  * @class Roo.bootstrap.TableCell
7233  * @extends Roo.bootstrap.Component
7234  * Bootstrap TableCell class
7235  * @cfg {String} html cell contain text
7236  * @cfg {String} cls cell class
7237  * @cfg {String} tag cell tag (td|th) default td
7238  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7239  * @cfg {String} align Aligns the content in a cell
7240  * @cfg {String} axis Categorizes cells
7241  * @cfg {String} bgcolor Specifies the background color of a cell
7242  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7243  * @cfg {Number} colspan Specifies the number of columns a cell should span
7244  * @cfg {String} headers Specifies one or more header cells a cell is related to
7245  * @cfg {Number} height Sets the height of a cell
7246  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7247  * @cfg {Number} rowspan Sets the number of rows a cell should span
7248  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7249  * @cfg {String} valign Vertical aligns the content in a cell
7250  * @cfg {Number} width Specifies the width of a cell
7251  * 
7252  * @constructor
7253  * Create a new TableCell
7254  * @param {Object} config The config object
7255  */
7256
7257 Roo.bootstrap.TableCell = function(config){
7258     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7259 };
7260
7261 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7262     
7263     html: false,
7264     cls: false,
7265     tag: false,
7266     abbr: false,
7267     align: false,
7268     axis: false,
7269     bgcolor: false,
7270     charoff: false,
7271     colspan: false,
7272     headers: false,
7273     height: false,
7274     nowrap: false,
7275     rowspan: false,
7276     scope: false,
7277     valign: false,
7278     width: false,
7279     
7280     
7281     getAutoCreate : function(){
7282         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7283         
7284         cfg = {
7285             tag: 'td'
7286         };
7287         
7288         if(this.tag){
7289             cfg.tag = this.tag;
7290         }
7291         
7292         if (this.html) {
7293             cfg.html=this.html
7294         }
7295         if (this.cls) {
7296             cfg.cls=this.cls
7297         }
7298         if (this.abbr) {
7299             cfg.abbr=this.abbr
7300         }
7301         if (this.align) {
7302             cfg.align=this.align
7303         }
7304         if (this.axis) {
7305             cfg.axis=this.axis
7306         }
7307         if (this.bgcolor) {
7308             cfg.bgcolor=this.bgcolor
7309         }
7310         if (this.charoff) {
7311             cfg.charoff=this.charoff
7312         }
7313         if (this.colspan) {
7314             cfg.colspan=this.colspan
7315         }
7316         if (this.headers) {
7317             cfg.headers=this.headers
7318         }
7319         if (this.height) {
7320             cfg.height=this.height
7321         }
7322         if (this.nowrap) {
7323             cfg.nowrap=this.nowrap
7324         }
7325         if (this.rowspan) {
7326             cfg.rowspan=this.rowspan
7327         }
7328         if (this.scope) {
7329             cfg.scope=this.scope
7330         }
7331         if (this.valign) {
7332             cfg.valign=this.valign
7333         }
7334         if (this.width) {
7335             cfg.width=this.width
7336         }
7337         
7338         
7339         return cfg;
7340     }
7341    
7342 });
7343
7344  
7345
7346  /*
7347  * - LGPL
7348  *
7349  * table row
7350  * 
7351  */
7352
7353 /**
7354  * @class Roo.bootstrap.TableRow
7355  * @extends Roo.bootstrap.Component
7356  * Bootstrap TableRow class
7357  * @cfg {String} cls row class
7358  * @cfg {String} align Aligns the content in a table row
7359  * @cfg {String} bgcolor Specifies a background color for a table row
7360  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7361  * @cfg {String} valign Vertical aligns the content in a table row
7362  * 
7363  * @constructor
7364  * Create a new TableRow
7365  * @param {Object} config The config object
7366  */
7367
7368 Roo.bootstrap.TableRow = function(config){
7369     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7370 };
7371
7372 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7373     
7374     cls: false,
7375     align: false,
7376     bgcolor: false,
7377     charoff: false,
7378     valign: false,
7379     
7380     getAutoCreate : function(){
7381         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7382         
7383         cfg = {
7384             tag: 'tr'
7385         };
7386             
7387         if(this.cls){
7388             cfg.cls = this.cls;
7389         }
7390         if(this.align){
7391             cfg.align = this.align;
7392         }
7393         if(this.bgcolor){
7394             cfg.bgcolor = this.bgcolor;
7395         }
7396         if(this.charoff){
7397             cfg.charoff = this.charoff;
7398         }
7399         if(this.valign){
7400             cfg.valign = this.valign;
7401         }
7402         
7403         return cfg;
7404     }
7405    
7406 });
7407
7408  
7409
7410  /*
7411  * - LGPL
7412  *
7413  * table body
7414  * 
7415  */
7416
7417 /**
7418  * @class Roo.bootstrap.TableBody
7419  * @extends Roo.bootstrap.Component
7420  * Bootstrap TableBody class
7421  * @cfg {String} cls element class
7422  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7423  * @cfg {String} align Aligns the content inside the element
7424  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7425  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7426  * 
7427  * @constructor
7428  * Create a new TableBody
7429  * @param {Object} config The config object
7430  */
7431
7432 Roo.bootstrap.TableBody = function(config){
7433     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7434 };
7435
7436 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7437     
7438     cls: false,
7439     tag: false,
7440     align: false,
7441     charoff: false,
7442     valign: false,
7443     
7444     getAutoCreate : function(){
7445         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7446         
7447         cfg = {
7448             tag: 'tbody'
7449         };
7450             
7451         if (this.cls) {
7452             cfg.cls=this.cls
7453         }
7454         if(this.tag){
7455             cfg.tag = this.tag;
7456         }
7457         
7458         if(this.align){
7459             cfg.align = this.align;
7460         }
7461         if(this.charoff){
7462             cfg.charoff = this.charoff;
7463         }
7464         if(this.valign){
7465             cfg.valign = this.valign;
7466         }
7467         
7468         return cfg;
7469     }
7470     
7471     
7472 //    initEvents : function()
7473 //    {
7474 //        
7475 //        if(!this.store){
7476 //            return;
7477 //        }
7478 //        
7479 //        this.store = Roo.factory(this.store, Roo.data);
7480 //        this.store.on('load', this.onLoad, this);
7481 //        
7482 //        this.store.load();
7483 //        
7484 //    },
7485 //    
7486 //    onLoad: function () 
7487 //    {   
7488 //        this.fireEvent('load', this);
7489 //    }
7490 //    
7491 //   
7492 });
7493
7494  
7495
7496  /*
7497  * Based on:
7498  * Ext JS Library 1.1.1
7499  * Copyright(c) 2006-2007, Ext JS, LLC.
7500  *
7501  * Originally Released Under LGPL - original licence link has changed is not relivant.
7502  *
7503  * Fork - LGPL
7504  * <script type="text/javascript">
7505  */
7506
7507 // as we use this in bootstrap.
7508 Roo.namespace('Roo.form');
7509  /**
7510  * @class Roo.form.Action
7511  * Internal Class used to handle form actions
7512  * @constructor
7513  * @param {Roo.form.BasicForm} el The form element or its id
7514  * @param {Object} config Configuration options
7515  */
7516
7517  
7518  
7519 // define the action interface
7520 Roo.form.Action = function(form, options){
7521     this.form = form;
7522     this.options = options || {};
7523 };
7524 /**
7525  * Client Validation Failed
7526  * @const 
7527  */
7528 Roo.form.Action.CLIENT_INVALID = 'client';
7529 /**
7530  * Server Validation Failed
7531  * @const 
7532  */
7533 Roo.form.Action.SERVER_INVALID = 'server';
7534  /**
7535  * Connect to Server Failed
7536  * @const 
7537  */
7538 Roo.form.Action.CONNECT_FAILURE = 'connect';
7539 /**
7540  * Reading Data from Server Failed
7541  * @const 
7542  */
7543 Roo.form.Action.LOAD_FAILURE = 'load';
7544
7545 Roo.form.Action.prototype = {
7546     type : 'default',
7547     failureType : undefined,
7548     response : undefined,
7549     result : undefined,
7550
7551     // interface method
7552     run : function(options){
7553
7554     },
7555
7556     // interface method
7557     success : function(response){
7558
7559     },
7560
7561     // interface method
7562     handleResponse : function(response){
7563
7564     },
7565
7566     // default connection failure
7567     failure : function(response){
7568         
7569         this.response = response;
7570         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7571         this.form.afterAction(this, false);
7572     },
7573
7574     processResponse : function(response){
7575         this.response = response;
7576         if(!response.responseText){
7577             return true;
7578         }
7579         this.result = this.handleResponse(response);
7580         return this.result;
7581     },
7582
7583     // utility functions used internally
7584     getUrl : function(appendParams){
7585         var url = this.options.url || this.form.url || this.form.el.dom.action;
7586         if(appendParams){
7587             var p = this.getParams();
7588             if(p){
7589                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7590             }
7591         }
7592         return url;
7593     },
7594
7595     getMethod : function(){
7596         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7597     },
7598
7599     getParams : function(){
7600         var bp = this.form.baseParams;
7601         var p = this.options.params;
7602         if(p){
7603             if(typeof p == "object"){
7604                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7605             }else if(typeof p == 'string' && bp){
7606                 p += '&' + Roo.urlEncode(bp);
7607             }
7608         }else if(bp){
7609             p = Roo.urlEncode(bp);
7610         }
7611         return p;
7612     },
7613
7614     createCallback : function(){
7615         return {
7616             success: this.success,
7617             failure: this.failure,
7618             scope: this,
7619             timeout: (this.form.timeout*1000),
7620             upload: this.form.fileUpload ? this.success : undefined
7621         };
7622     }
7623 };
7624
7625 Roo.form.Action.Submit = function(form, options){
7626     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7627 };
7628
7629 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7630     type : 'submit',
7631
7632     haveProgress : false,
7633     uploadComplete : false,
7634     
7635     // uploadProgress indicator.
7636     uploadProgress : function()
7637     {
7638         if (!this.form.progressUrl) {
7639             return;
7640         }
7641         
7642         if (!this.haveProgress) {
7643             Roo.MessageBox.progress("Uploading", "Uploading");
7644         }
7645         if (this.uploadComplete) {
7646            Roo.MessageBox.hide();
7647            return;
7648         }
7649         
7650         this.haveProgress = true;
7651    
7652         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7653         
7654         var c = new Roo.data.Connection();
7655         c.request({
7656             url : this.form.progressUrl,
7657             params: {
7658                 id : uid
7659             },
7660             method: 'GET',
7661             success : function(req){
7662                //console.log(data);
7663                 var rdata = false;
7664                 var edata;
7665                 try  {
7666                    rdata = Roo.decode(req.responseText)
7667                 } catch (e) {
7668                     Roo.log("Invalid data from server..");
7669                     Roo.log(edata);
7670                     return;
7671                 }
7672                 if (!rdata || !rdata.success) {
7673                     Roo.log(rdata);
7674                     Roo.MessageBox.alert(Roo.encode(rdata));
7675                     return;
7676                 }
7677                 var data = rdata.data;
7678                 
7679                 if (this.uploadComplete) {
7680                    Roo.MessageBox.hide();
7681                    return;
7682                 }
7683                    
7684                 if (data){
7685                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7686                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7687                     );
7688                 }
7689                 this.uploadProgress.defer(2000,this);
7690             },
7691        
7692             failure: function(data) {
7693                 Roo.log('progress url failed ');
7694                 Roo.log(data);
7695             },
7696             scope : this
7697         });
7698            
7699     },
7700     
7701     
7702     run : function()
7703     {
7704         // run get Values on the form, so it syncs any secondary forms.
7705         this.form.getValues();
7706         
7707         var o = this.options;
7708         var method = this.getMethod();
7709         var isPost = method == 'POST';
7710         if(o.clientValidation === false || this.form.isValid()){
7711             
7712             if (this.form.progressUrl) {
7713                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7714                     (new Date() * 1) + '' + Math.random());
7715                     
7716             } 
7717             
7718             
7719             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7720                 form:this.form.el.dom,
7721                 url:this.getUrl(!isPost),
7722                 method: method,
7723                 params:isPost ? this.getParams() : null,
7724                 isUpload: this.form.fileUpload
7725             }));
7726             
7727             this.uploadProgress();
7728
7729         }else if (o.clientValidation !== false){ // client validation failed
7730             this.failureType = Roo.form.Action.CLIENT_INVALID;
7731             this.form.afterAction(this, false);
7732         }
7733     },
7734
7735     success : function(response)
7736     {
7737         this.uploadComplete= true;
7738         if (this.haveProgress) {
7739             Roo.MessageBox.hide();
7740         }
7741         
7742         
7743         var result = this.processResponse(response);
7744         if(result === true || result.success){
7745             this.form.afterAction(this, true);
7746             return;
7747         }
7748         if(result.errors){
7749             this.form.markInvalid(result.errors);
7750             this.failureType = Roo.form.Action.SERVER_INVALID;
7751         }
7752         this.form.afterAction(this, false);
7753     },
7754     failure : function(response)
7755     {
7756         this.uploadComplete= true;
7757         if (this.haveProgress) {
7758             Roo.MessageBox.hide();
7759         }
7760         
7761         this.response = response;
7762         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7763         this.form.afterAction(this, false);
7764     },
7765     
7766     handleResponse : function(response){
7767         if(this.form.errorReader){
7768             var rs = this.form.errorReader.read(response);
7769             var errors = [];
7770             if(rs.records){
7771                 for(var i = 0, len = rs.records.length; i < len; i++) {
7772                     var r = rs.records[i];
7773                     errors[i] = r.data;
7774                 }
7775             }
7776             if(errors.length < 1){
7777                 errors = null;
7778             }
7779             return {
7780                 success : rs.success,
7781                 errors : errors
7782             };
7783         }
7784         var ret = false;
7785         try {
7786             ret = Roo.decode(response.responseText);
7787         } catch (e) {
7788             ret = {
7789                 success: false,
7790                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7791                 errors : []
7792             };
7793         }
7794         return ret;
7795         
7796     }
7797 });
7798
7799
7800 Roo.form.Action.Load = function(form, options){
7801     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7802     this.reader = this.form.reader;
7803 };
7804
7805 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7806     type : 'load',
7807
7808     run : function(){
7809         
7810         Roo.Ajax.request(Roo.apply(
7811                 this.createCallback(), {
7812                     method:this.getMethod(),
7813                     url:this.getUrl(false),
7814                     params:this.getParams()
7815         }));
7816     },
7817
7818     success : function(response){
7819         
7820         var result = this.processResponse(response);
7821         if(result === true || !result.success || !result.data){
7822             this.failureType = Roo.form.Action.LOAD_FAILURE;
7823             this.form.afterAction(this, false);
7824             return;
7825         }
7826         this.form.clearInvalid();
7827         this.form.setValues(result.data);
7828         this.form.afterAction(this, true);
7829     },
7830
7831     handleResponse : function(response){
7832         if(this.form.reader){
7833             var rs = this.form.reader.read(response);
7834             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7835             return {
7836                 success : rs.success,
7837                 data : data
7838             };
7839         }
7840         return Roo.decode(response.responseText);
7841     }
7842 });
7843
7844 Roo.form.Action.ACTION_TYPES = {
7845     'load' : Roo.form.Action.Load,
7846     'submit' : Roo.form.Action.Submit
7847 };/*
7848  * - LGPL
7849  *
7850  * form
7851  *
7852  */
7853
7854 /**
7855  * @class Roo.bootstrap.Form
7856  * @extends Roo.bootstrap.Component
7857  * Bootstrap Form class
7858  * @cfg {String} method  GET | POST (default POST)
7859  * @cfg {String} labelAlign top | left (default top)
7860  * @cfg {String} align left  | right - for navbars
7861  * @cfg {Boolean} loadMask load mask when submit (default true)
7862
7863  *
7864  * @constructor
7865  * Create a new Form
7866  * @param {Object} config The config object
7867  */
7868
7869
7870 Roo.bootstrap.Form = function(config){
7871     
7872     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7873     
7874     Roo.bootstrap.Form.popover.apply();
7875     
7876     this.addEvents({
7877         /**
7878          * @event clientvalidation
7879          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7880          * @param {Form} this
7881          * @param {Boolean} valid true if the form has passed client-side validation
7882          */
7883         clientvalidation: true,
7884         /**
7885          * @event beforeaction
7886          * Fires before any action is performed. Return false to cancel the action.
7887          * @param {Form} this
7888          * @param {Action} action The action to be performed
7889          */
7890         beforeaction: true,
7891         /**
7892          * @event actionfailed
7893          * Fires when an action fails.
7894          * @param {Form} this
7895          * @param {Action} action The action that failed
7896          */
7897         actionfailed : true,
7898         /**
7899          * @event actioncomplete
7900          * Fires when an action is completed.
7901          * @param {Form} this
7902          * @param {Action} action The action that completed
7903          */
7904         actioncomplete : true
7905     });
7906 };
7907
7908 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7909
7910      /**
7911      * @cfg {String} method
7912      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7913      */
7914     method : 'POST',
7915     /**
7916      * @cfg {String} url
7917      * The URL to use for form actions if one isn't supplied in the action options.
7918      */
7919     /**
7920      * @cfg {Boolean} fileUpload
7921      * Set to true if this form is a file upload.
7922      */
7923
7924     /**
7925      * @cfg {Object} baseParams
7926      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7927      */
7928
7929     /**
7930      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7931      */
7932     timeout: 30,
7933     /**
7934      * @cfg {Sting} align (left|right) for navbar forms
7935      */
7936     align : 'left',
7937
7938     // private
7939     activeAction : null,
7940
7941     /**
7942      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7943      * element by passing it or its id or mask the form itself by passing in true.
7944      * @type Mixed
7945      */
7946     waitMsgTarget : false,
7947
7948     loadMask : true,
7949     
7950     /**
7951      * @cfg {Boolean} errorMask (true|false) default false
7952      */
7953     errorMask : false,
7954     
7955     /**
7956      * @cfg {Number} maskOffset Default 100
7957      */
7958     maskOffset : 100,
7959     
7960     /**
7961      * @cfg {Boolean} maskBody
7962      */
7963     maskBody : false,
7964
7965     getAutoCreate : function(){
7966
7967         var cfg = {
7968             tag: 'form',
7969             method : this.method || 'POST',
7970             id : this.id || Roo.id(),
7971             cls : ''
7972         };
7973         if (this.parent().xtype.match(/^Nav/)) {
7974             cfg.cls = 'navbar-form navbar-' + this.align;
7975
7976         }
7977
7978         if (this.labelAlign == 'left' ) {
7979             cfg.cls += ' form-horizontal';
7980         }
7981
7982
7983         return cfg;
7984     },
7985     initEvents : function()
7986     {
7987         this.el.on('submit', this.onSubmit, this);
7988         // this was added as random key presses on the form where triggering form submit.
7989         this.el.on('keypress', function(e) {
7990             if (e.getCharCode() != 13) {
7991                 return true;
7992             }
7993             // we might need to allow it for textareas.. and some other items.
7994             // check e.getTarget().
7995
7996             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7997                 return true;
7998             }
7999
8000             Roo.log("keypress blocked");
8001
8002             e.preventDefault();
8003             return false;
8004         });
8005         
8006     },
8007     // private
8008     onSubmit : function(e){
8009         e.stopEvent();
8010     },
8011
8012      /**
8013      * Returns true if client-side validation on the form is successful.
8014      * @return Boolean
8015      */
8016     isValid : function(){
8017         var items = this.getItems();
8018         var valid = true;
8019         var target = false;
8020         
8021         items.each(function(f){
8022             
8023             if(f.validate()){
8024                 return;
8025             }
8026             
8027             Roo.log('invalid field: ' + f.name);
8028             
8029             valid = false;
8030
8031             if(!target && f.el.isVisible(true)){
8032                 target = f;
8033             }
8034            
8035         });
8036         
8037         if(this.errorMask && !valid){
8038             Roo.bootstrap.Form.popover.mask(this, target);
8039         }
8040         
8041         return valid;
8042     },
8043     
8044     /**
8045      * Returns true if any fields in this form have changed since their original load.
8046      * @return Boolean
8047      */
8048     isDirty : function(){
8049         var dirty = false;
8050         var items = this.getItems();
8051         items.each(function(f){
8052            if(f.isDirty()){
8053                dirty = true;
8054                return false;
8055            }
8056            return true;
8057         });
8058         return dirty;
8059     },
8060      /**
8061      * Performs a predefined action (submit or load) or custom actions you define on this form.
8062      * @param {String} actionName The name of the action type
8063      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8064      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8065      * accept other config options):
8066      * <pre>
8067 Property          Type             Description
8068 ----------------  ---------------  ----------------------------------------------------------------------------------
8069 url               String           The url for the action (defaults to the form's url)
8070 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8071 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8072 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8073                                    validate the form on the client (defaults to false)
8074      * </pre>
8075      * @return {BasicForm} this
8076      */
8077     doAction : function(action, options){
8078         if(typeof action == 'string'){
8079             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8080         }
8081         if(this.fireEvent('beforeaction', this, action) !== false){
8082             this.beforeAction(action);
8083             action.run.defer(100, action);
8084         }
8085         return this;
8086     },
8087
8088     // private
8089     beforeAction : function(action){
8090         var o = action.options;
8091         
8092         if(this.loadMask){
8093             
8094             if(this.maskBody){
8095                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8096             } else {
8097                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8098             }
8099         }
8100         // not really supported yet.. ??
8101
8102         //if(this.waitMsgTarget === true){
8103         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8104         //}else if(this.waitMsgTarget){
8105         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8106         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8107         //}else {
8108         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8109        // }
8110
8111     },
8112
8113     // private
8114     afterAction : function(action, success){
8115         this.activeAction = null;
8116         var o = action.options;
8117
8118         if(this.loadMask){
8119             
8120             if(this.maskBody){
8121                 Roo.get(document.body).unmask();
8122             } else {
8123                 this.el.unmask();
8124             }
8125         }
8126         
8127         //if(this.waitMsgTarget === true){
8128 //            this.el.unmask();
8129         //}else if(this.waitMsgTarget){
8130         //    this.waitMsgTarget.unmask();
8131         //}else{
8132         //    Roo.MessageBox.updateProgress(1);
8133         //    Roo.MessageBox.hide();
8134        // }
8135         //
8136         if(success){
8137             if(o.reset){
8138                 this.reset();
8139             }
8140             Roo.callback(o.success, o.scope, [this, action]);
8141             this.fireEvent('actioncomplete', this, action);
8142
8143         }else{
8144
8145             // failure condition..
8146             // we have a scenario where updates need confirming.
8147             // eg. if a locking scenario exists..
8148             // we look for { errors : { needs_confirm : true }} in the response.
8149             if (
8150                 (typeof(action.result) != 'undefined')  &&
8151                 (typeof(action.result.errors) != 'undefined')  &&
8152                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8153            ){
8154                 var _t = this;
8155                 Roo.log("not supported yet");
8156                  /*
8157
8158                 Roo.MessageBox.confirm(
8159                     "Change requires confirmation",
8160                     action.result.errorMsg,
8161                     function(r) {
8162                         if (r != 'yes') {
8163                             return;
8164                         }
8165                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8166                     }
8167
8168                 );
8169                 */
8170
8171
8172                 return;
8173             }
8174
8175             Roo.callback(o.failure, o.scope, [this, action]);
8176             // show an error message if no failed handler is set..
8177             if (!this.hasListener('actionfailed')) {
8178                 Roo.log("need to add dialog support");
8179                 /*
8180                 Roo.MessageBox.alert("Error",
8181                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8182                         action.result.errorMsg :
8183                         "Saving Failed, please check your entries or try again"
8184                 );
8185                 */
8186             }
8187
8188             this.fireEvent('actionfailed', this, action);
8189         }
8190
8191     },
8192     /**
8193      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8194      * @param {String} id The value to search for
8195      * @return Field
8196      */
8197     findField : function(id){
8198         var items = this.getItems();
8199         var field = items.get(id);
8200         if(!field){
8201              items.each(function(f){
8202                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8203                     field = f;
8204                     return false;
8205                 }
8206                 return true;
8207             });
8208         }
8209         return field || null;
8210     },
8211      /**
8212      * Mark fields in this form invalid in bulk.
8213      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8214      * @return {BasicForm} this
8215      */
8216     markInvalid : function(errors){
8217         if(errors instanceof Array){
8218             for(var i = 0, len = errors.length; i < len; i++){
8219                 var fieldError = errors[i];
8220                 var f = this.findField(fieldError.id);
8221                 if(f){
8222                     f.markInvalid(fieldError.msg);
8223                 }
8224             }
8225         }else{
8226             var field, id;
8227             for(id in errors){
8228                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8229                     field.markInvalid(errors[id]);
8230                 }
8231             }
8232         }
8233         //Roo.each(this.childForms || [], function (f) {
8234         //    f.markInvalid(errors);
8235         //});
8236
8237         return this;
8238     },
8239
8240     /**
8241      * Set values for fields in this form in bulk.
8242      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8243      * @return {BasicForm} this
8244      */
8245     setValues : function(values){
8246         if(values instanceof Array){ // array of objects
8247             for(var i = 0, len = values.length; i < len; i++){
8248                 var v = values[i];
8249                 var f = this.findField(v.id);
8250                 if(f){
8251                     f.setValue(v.value);
8252                     if(this.trackResetOnLoad){
8253                         f.originalValue = f.getValue();
8254                     }
8255                 }
8256             }
8257         }else{ // object hash
8258             var field, id;
8259             for(id in values){
8260                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8261
8262                     if (field.setFromData &&
8263                         field.valueField &&
8264                         field.displayField &&
8265                         // combos' with local stores can
8266                         // be queried via setValue()
8267                         // to set their value..
8268                         (field.store && !field.store.isLocal)
8269                         ) {
8270                         // it's a combo
8271                         var sd = { };
8272                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8273                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8274                         field.setFromData(sd);
8275
8276                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8277                         
8278                         field.setFromData(values);
8279                         
8280                     } else {
8281                         field.setValue(values[id]);
8282                     }
8283
8284
8285                     if(this.trackResetOnLoad){
8286                         field.originalValue = field.getValue();
8287                     }
8288                 }
8289             }
8290         }
8291
8292         //Roo.each(this.childForms || [], function (f) {
8293         //    f.setValues(values);
8294         //});
8295
8296         return this;
8297     },
8298
8299     /**
8300      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8301      * they are returned as an array.
8302      * @param {Boolean} asString
8303      * @return {Object}
8304      */
8305     getValues : function(asString){
8306         //if (this.childForms) {
8307             // copy values from the child forms
8308         //    Roo.each(this.childForms, function (f) {
8309         //        this.setValues(f.getValues());
8310         //    }, this);
8311         //}
8312
8313
8314
8315         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8316         if(asString === true){
8317             return fs;
8318         }
8319         return Roo.urlDecode(fs);
8320     },
8321
8322     /**
8323      * Returns the fields in this form as an object with key/value pairs.
8324      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8325      * @return {Object}
8326      */
8327     getFieldValues : function(with_hidden)
8328     {
8329         var items = this.getItems();
8330         var ret = {};
8331         items.each(function(f){
8332             
8333             if (!f.getName()) {
8334                 return;
8335             }
8336             
8337             var v = f.getValue();
8338             
8339             if (f.inputType =='radio') {
8340                 if (typeof(ret[f.getName()]) == 'undefined') {
8341                     ret[f.getName()] = ''; // empty..
8342                 }
8343
8344                 if (!f.el.dom.checked) {
8345                     return;
8346
8347                 }
8348                 v = f.el.dom.value;
8349
8350             }
8351             
8352             if(f.xtype == 'MoneyField'){
8353                 ret[f.currencyName] = f.getCurrency();
8354             }
8355
8356             // not sure if this supported any more..
8357             if ((typeof(v) == 'object') && f.getRawValue) {
8358                 v = f.getRawValue() ; // dates..
8359             }
8360             // combo boxes where name != hiddenName...
8361             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8362                 ret[f.name] = f.getRawValue();
8363             }
8364             ret[f.getName()] = v;
8365         });
8366
8367         return ret;
8368     },
8369
8370     /**
8371      * Clears all invalid messages in this form.
8372      * @return {BasicForm} this
8373      */
8374     clearInvalid : function(){
8375         var items = this.getItems();
8376
8377         items.each(function(f){
8378            f.clearInvalid();
8379         });
8380
8381         return this;
8382     },
8383
8384     /**
8385      * Resets this form.
8386      * @return {BasicForm} this
8387      */
8388     reset : function(){
8389         var items = this.getItems();
8390         items.each(function(f){
8391             f.reset();
8392         });
8393
8394         Roo.each(this.childForms || [], function (f) {
8395             f.reset();
8396         });
8397
8398
8399         return this;
8400     },
8401     
8402     getItems : function()
8403     {
8404         var r=new Roo.util.MixedCollection(false, function(o){
8405             return o.id || (o.id = Roo.id());
8406         });
8407         var iter = function(el) {
8408             if (el.inputEl) {
8409                 r.add(el);
8410             }
8411             if (!el.items) {
8412                 return;
8413             }
8414             Roo.each(el.items,function(e) {
8415                 iter(e);
8416             });
8417         };
8418
8419         iter(this);
8420         return r;
8421     },
8422     
8423     hideFields : function(items)
8424     {
8425         Roo.each(items, function(i){
8426             
8427             var f = this.findField(i);
8428             
8429             if(!f){
8430                 return;
8431             }
8432             
8433             f.hide();
8434             
8435         }, this);
8436     },
8437     
8438     showFields : function(items)
8439     {
8440         Roo.each(items, function(i){
8441             
8442             var f = this.findField(i);
8443             
8444             if(!f){
8445                 return;
8446             }
8447             
8448             f.show();
8449             
8450         }, this);
8451     }
8452
8453 });
8454
8455 Roo.apply(Roo.bootstrap.Form, {
8456     
8457     popover : {
8458         
8459         padding : 5,
8460         
8461         isApplied : false,
8462         
8463         isMasked : false,
8464         
8465         form : false,
8466         
8467         target : false,
8468         
8469         toolTip : false,
8470         
8471         intervalID : false,
8472         
8473         maskEl : false,
8474         
8475         apply : function()
8476         {
8477             if(this.isApplied){
8478                 return;
8479             }
8480             
8481             this.maskEl = {
8482                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8483                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8484                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8485                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8486             };
8487             
8488             this.maskEl.top.enableDisplayMode("block");
8489             this.maskEl.left.enableDisplayMode("block");
8490             this.maskEl.bottom.enableDisplayMode("block");
8491             this.maskEl.right.enableDisplayMode("block");
8492             
8493             this.toolTip = new Roo.bootstrap.Tooltip({
8494                 cls : 'roo-form-error-popover',
8495                 alignment : {
8496                     'left' : ['r-l', [-2,0], 'right'],
8497                     'right' : ['l-r', [2,0], 'left'],
8498                     'bottom' : ['tl-bl', [0,2], 'top'],
8499                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8500                 }
8501             });
8502             
8503             this.toolTip.render(Roo.get(document.body));
8504
8505             this.toolTip.el.enableDisplayMode("block");
8506             
8507             Roo.get(document.body).on('click', function(){
8508                 this.unmask();
8509             }, this);
8510             
8511             Roo.get(document.body).on('touchstart', function(){
8512                 this.unmask();
8513             }, this);
8514             
8515             this.isApplied = true
8516         },
8517         
8518         mask : function(form, target)
8519         {
8520             this.form = form;
8521             
8522             this.target = target;
8523             
8524             if(!this.form.errorMask || !target.el){
8525                 return;
8526             }
8527             
8528             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8529             
8530             Roo.log(scrollable);
8531             
8532             var ot = this.target.el.calcOffsetsTo(scrollable);
8533             
8534             var scrollTo = ot[1] - this.form.maskOffset;
8535             
8536             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8537             
8538             scrollable.scrollTo('top', scrollTo);
8539             
8540             var box = this.target.el.getBox();
8541             Roo.log(box);
8542             var zIndex = Roo.bootstrap.Modal.zIndex++;
8543
8544             
8545             this.maskEl.top.setStyle('position', 'absolute');
8546             this.maskEl.top.setStyle('z-index', zIndex);
8547             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8548             this.maskEl.top.setLeft(0);
8549             this.maskEl.top.setTop(0);
8550             this.maskEl.top.show();
8551             
8552             this.maskEl.left.setStyle('position', 'absolute');
8553             this.maskEl.left.setStyle('z-index', zIndex);
8554             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8555             this.maskEl.left.setLeft(0);
8556             this.maskEl.left.setTop(box.y - this.padding);
8557             this.maskEl.left.show();
8558
8559             this.maskEl.bottom.setStyle('position', 'absolute');
8560             this.maskEl.bottom.setStyle('z-index', zIndex);
8561             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8562             this.maskEl.bottom.setLeft(0);
8563             this.maskEl.bottom.setTop(box.bottom + this.padding);
8564             this.maskEl.bottom.show();
8565
8566             this.maskEl.right.setStyle('position', 'absolute');
8567             this.maskEl.right.setStyle('z-index', zIndex);
8568             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8569             this.maskEl.right.setLeft(box.right + this.padding);
8570             this.maskEl.right.setTop(box.y - this.padding);
8571             this.maskEl.right.show();
8572
8573             this.toolTip.bindEl = this.target.el;
8574
8575             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8576
8577             var tip = this.target.blankText;
8578
8579             if(this.target.getValue() !== '' ) {
8580                 
8581                 if (this.target.invalidText.length) {
8582                     tip = this.target.invalidText;
8583                 } else if (this.target.regexText.length){
8584                     tip = this.target.regexText;
8585                 }
8586             }
8587
8588             this.toolTip.show(tip);
8589
8590             this.intervalID = window.setInterval(function() {
8591                 Roo.bootstrap.Form.popover.unmask();
8592             }, 10000);
8593
8594             window.onwheel = function(){ return false;};
8595             
8596             (function(){ this.isMasked = true; }).defer(500, this);
8597             
8598         },
8599         
8600         unmask : function()
8601         {
8602             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8603                 return;
8604             }
8605             
8606             this.maskEl.top.setStyle('position', 'absolute');
8607             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8608             this.maskEl.top.hide();
8609
8610             this.maskEl.left.setStyle('position', 'absolute');
8611             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8612             this.maskEl.left.hide();
8613
8614             this.maskEl.bottom.setStyle('position', 'absolute');
8615             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8616             this.maskEl.bottom.hide();
8617
8618             this.maskEl.right.setStyle('position', 'absolute');
8619             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8620             this.maskEl.right.hide();
8621             
8622             this.toolTip.hide();
8623             
8624             this.toolTip.el.hide();
8625             
8626             window.onwheel = function(){ return true;};
8627             
8628             if(this.intervalID){
8629                 window.clearInterval(this.intervalID);
8630                 this.intervalID = false;
8631             }
8632             
8633             this.isMasked = false;
8634             
8635         }
8636         
8637     }
8638     
8639 });
8640
8641 /*
8642  * Based on:
8643  * Ext JS Library 1.1.1
8644  * Copyright(c) 2006-2007, Ext JS, LLC.
8645  *
8646  * Originally Released Under LGPL - original licence link has changed is not relivant.
8647  *
8648  * Fork - LGPL
8649  * <script type="text/javascript">
8650  */
8651 /**
8652  * @class Roo.form.VTypes
8653  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8654  * @singleton
8655  */
8656 Roo.form.VTypes = function(){
8657     // closure these in so they are only created once.
8658     var alpha = /^[a-zA-Z_]+$/;
8659     var alphanum = /^[a-zA-Z0-9_]+$/;
8660     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8661     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8662
8663     // All these messages and functions are configurable
8664     return {
8665         /**
8666          * The function used to validate email addresses
8667          * @param {String} value The email address
8668          */
8669         'email' : function(v){
8670             return email.test(v);
8671         },
8672         /**
8673          * The error text to display when the email validation function returns false
8674          * @type String
8675          */
8676         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8677         /**
8678          * The keystroke filter mask to be applied on email input
8679          * @type RegExp
8680          */
8681         'emailMask' : /[a-z0-9_\.\-@]/i,
8682
8683         /**
8684          * The function used to validate URLs
8685          * @param {String} value The URL
8686          */
8687         'url' : function(v){
8688             return url.test(v);
8689         },
8690         /**
8691          * The error text to display when the url validation function returns false
8692          * @type String
8693          */
8694         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8695         
8696         /**
8697          * The function used to validate alpha values
8698          * @param {String} value The value
8699          */
8700         'alpha' : function(v){
8701             return alpha.test(v);
8702         },
8703         /**
8704          * The error text to display when the alpha validation function returns false
8705          * @type String
8706          */
8707         'alphaText' : 'This field should only contain letters and _',
8708         /**
8709          * The keystroke filter mask to be applied on alpha input
8710          * @type RegExp
8711          */
8712         'alphaMask' : /[a-z_]/i,
8713
8714         /**
8715          * The function used to validate alphanumeric values
8716          * @param {String} value The value
8717          */
8718         'alphanum' : function(v){
8719             return alphanum.test(v);
8720         },
8721         /**
8722          * The error text to display when the alphanumeric validation function returns false
8723          * @type String
8724          */
8725         'alphanumText' : 'This field should only contain letters, numbers and _',
8726         /**
8727          * The keystroke filter mask to be applied on alphanumeric input
8728          * @type RegExp
8729          */
8730         'alphanumMask' : /[a-z0-9_]/i
8731     };
8732 }();/*
8733  * - LGPL
8734  *
8735  * Input
8736  * 
8737  */
8738
8739 /**
8740  * @class Roo.bootstrap.Input
8741  * @extends Roo.bootstrap.Component
8742  * Bootstrap Input class
8743  * @cfg {Boolean} disabled is it disabled
8744  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8745  * @cfg {String} name name of the input
8746  * @cfg {string} fieldLabel - the label associated
8747  * @cfg {string} placeholder - placeholder to put in text.
8748  * @cfg {string}  before - input group add on before
8749  * @cfg {string} after - input group add on after
8750  * @cfg {string} size - (lg|sm) or leave empty..
8751  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8752  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8753  * @cfg {Number} md colspan out of 12 for computer-sized screens
8754  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8755  * @cfg {string} value default value of the input
8756  * @cfg {Number} labelWidth set the width of label 
8757  * @cfg {Number} labellg set the width of label (1-12)
8758  * @cfg {Number} labelmd set the width of label (1-12)
8759  * @cfg {Number} labelsm set the width of label (1-12)
8760  * @cfg {Number} labelxs set the width of label (1-12)
8761  * @cfg {String} labelAlign (top|left)
8762  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8763  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8764  * @cfg {String} indicatorpos (left|right) default left
8765  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8766  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8767
8768  * @cfg {String} align (left|center|right) Default left
8769  * @cfg {Boolean} forceFeedback (true|false) Default false
8770  * 
8771  * @constructor
8772  * Create a new Input
8773  * @param {Object} config The config object
8774  */
8775
8776 Roo.bootstrap.Input = function(config){
8777     
8778     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8779     
8780     this.addEvents({
8781         /**
8782          * @event focus
8783          * Fires when this field receives input focus.
8784          * @param {Roo.form.Field} this
8785          */
8786         focus : true,
8787         /**
8788          * @event blur
8789          * Fires when this field loses input focus.
8790          * @param {Roo.form.Field} this
8791          */
8792         blur : true,
8793         /**
8794          * @event specialkey
8795          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8796          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8797          * @param {Roo.form.Field} this
8798          * @param {Roo.EventObject} e The event object
8799          */
8800         specialkey : true,
8801         /**
8802          * @event change
8803          * Fires just before the field blurs if the field value has changed.
8804          * @param {Roo.form.Field} this
8805          * @param {Mixed} newValue The new value
8806          * @param {Mixed} oldValue The original value
8807          */
8808         change : true,
8809         /**
8810          * @event invalid
8811          * Fires after the field has been marked as invalid.
8812          * @param {Roo.form.Field} this
8813          * @param {String} msg The validation message
8814          */
8815         invalid : true,
8816         /**
8817          * @event valid
8818          * Fires after the field has been validated with no errors.
8819          * @param {Roo.form.Field} this
8820          */
8821         valid : true,
8822          /**
8823          * @event keyup
8824          * Fires after the key up
8825          * @param {Roo.form.Field} this
8826          * @param {Roo.EventObject}  e The event Object
8827          */
8828         keyup : true
8829     });
8830 };
8831
8832 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8833      /**
8834      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8835       automatic validation (defaults to "keyup").
8836      */
8837     validationEvent : "keyup",
8838      /**
8839      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8840      */
8841     validateOnBlur : true,
8842     /**
8843      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8844      */
8845     validationDelay : 250,
8846      /**
8847      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8848      */
8849     focusClass : "x-form-focus",  // not needed???
8850     
8851        
8852     /**
8853      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8854      */
8855     invalidClass : "has-warning",
8856     
8857     /**
8858      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8859      */
8860     validClass : "has-success",
8861     
8862     /**
8863      * @cfg {Boolean} hasFeedback (true|false) default true
8864      */
8865     hasFeedback : true,
8866     
8867     /**
8868      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8869      */
8870     invalidFeedbackClass : "glyphicon-warning-sign",
8871     
8872     /**
8873      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8874      */
8875     validFeedbackClass : "glyphicon-ok",
8876     
8877     /**
8878      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8879      */
8880     selectOnFocus : false,
8881     
8882      /**
8883      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8884      */
8885     maskRe : null,
8886        /**
8887      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8888      */
8889     vtype : null,
8890     
8891       /**
8892      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8893      */
8894     disableKeyFilter : false,
8895     
8896        /**
8897      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8898      */
8899     disabled : false,
8900      /**
8901      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8902      */
8903     allowBlank : true,
8904     /**
8905      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8906      */
8907     blankText : "Please complete this mandatory field",
8908     
8909      /**
8910      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8911      */
8912     minLength : 0,
8913     /**
8914      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8915      */
8916     maxLength : Number.MAX_VALUE,
8917     /**
8918      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8919      */
8920     minLengthText : "The minimum length for this field is {0}",
8921     /**
8922      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8923      */
8924     maxLengthText : "The maximum length for this field is {0}",
8925   
8926     
8927     /**
8928      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8929      * If available, this function will be called only after the basic validators all return true, and will be passed the
8930      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8931      */
8932     validator : null,
8933     /**
8934      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8935      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8936      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8937      */
8938     regex : null,
8939     /**
8940      * @cfg {String} regexText -- Depricated - use Invalid Text
8941      */
8942     regexText : "",
8943     
8944     /**
8945      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8946      */
8947     invalidText : "",
8948     
8949     
8950     
8951     autocomplete: false,
8952     
8953     
8954     fieldLabel : '',
8955     inputType : 'text',
8956     
8957     name : false,
8958     placeholder: false,
8959     before : false,
8960     after : false,
8961     size : false,
8962     hasFocus : false,
8963     preventMark: false,
8964     isFormField : true,
8965     value : '',
8966     labelWidth : 2,
8967     labelAlign : false,
8968     readOnly : false,
8969     align : false,
8970     formatedValue : false,
8971     forceFeedback : false,
8972     
8973     indicatorpos : 'left',
8974     
8975     labellg : 0,
8976     labelmd : 0,
8977     labelsm : 0,
8978     labelxs : 0,
8979     
8980     capture : '',
8981     accept : '',
8982     
8983     parentLabelAlign : function()
8984     {
8985         var parent = this;
8986         while (parent.parent()) {
8987             parent = parent.parent();
8988             if (typeof(parent.labelAlign) !='undefined') {
8989                 return parent.labelAlign;
8990             }
8991         }
8992         return 'left';
8993         
8994     },
8995     
8996     getAutoCreate : function()
8997     {
8998         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8999         
9000         var id = Roo.id();
9001         
9002         var cfg = {};
9003         
9004         if(this.inputType != 'hidden'){
9005             cfg.cls = 'form-group' //input-group
9006         }
9007         
9008         var input =  {
9009             tag: 'input',
9010             id : id,
9011             type : this.inputType,
9012             value : this.value,
9013             cls : 'form-control',
9014             placeholder : this.placeholder || '',
9015             autocomplete : this.autocomplete || 'new-password'
9016         };
9017         
9018         if(this.capture.length){
9019             input.capture = this.capture;
9020         }
9021         
9022         if(this.accept.length){
9023             input.accept = this.accept + "/*";
9024         }
9025         
9026         if(this.align){
9027             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9028         }
9029         
9030         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9031             input.maxLength = this.maxLength;
9032         }
9033         
9034         if (this.disabled) {
9035             input.disabled=true;
9036         }
9037         
9038         if (this.readOnly) {
9039             input.readonly=true;
9040         }
9041         
9042         if (this.name) {
9043             input.name = this.name;
9044         }
9045         
9046         if (this.size) {
9047             input.cls += ' input-' + this.size;
9048         }
9049         
9050         var settings=this;
9051         ['xs','sm','md','lg'].map(function(size){
9052             if (settings[size]) {
9053                 cfg.cls += ' col-' + size + '-' + settings[size];
9054             }
9055         });
9056         
9057         var inputblock = input;
9058         
9059         var feedback = {
9060             tag: 'span',
9061             cls: 'glyphicon form-control-feedback'
9062         };
9063             
9064         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9065             
9066             inputblock = {
9067                 cls : 'has-feedback',
9068                 cn :  [
9069                     input,
9070                     feedback
9071                 ] 
9072             };  
9073         }
9074         
9075         if (this.before || this.after) {
9076             
9077             inputblock = {
9078                 cls : 'input-group',
9079                 cn :  [] 
9080             };
9081             
9082             if (this.before && typeof(this.before) == 'string') {
9083                 
9084                 inputblock.cn.push({
9085                     tag :'span',
9086                     cls : 'roo-input-before input-group-addon',
9087                     html : this.before
9088                 });
9089             }
9090             if (this.before && typeof(this.before) == 'object') {
9091                 this.before = Roo.factory(this.before);
9092                 
9093                 inputblock.cn.push({
9094                     tag :'span',
9095                     cls : 'roo-input-before input-group-' +
9096                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9097                 });
9098             }
9099             
9100             inputblock.cn.push(input);
9101             
9102             if (this.after && typeof(this.after) == 'string') {
9103                 inputblock.cn.push({
9104                     tag :'span',
9105                     cls : 'roo-input-after input-group-addon',
9106                     html : this.after
9107                 });
9108             }
9109             if (this.after && typeof(this.after) == 'object') {
9110                 this.after = Roo.factory(this.after);
9111                 
9112                 inputblock.cn.push({
9113                     tag :'span',
9114                     cls : 'roo-input-after input-group-' +
9115                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9116                 });
9117             }
9118             
9119             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9120                 inputblock.cls += ' has-feedback';
9121                 inputblock.cn.push(feedback);
9122             }
9123         };
9124         
9125         if (align ==='left' && this.fieldLabel.length) {
9126             
9127             cfg.cls += ' roo-form-group-label-left';
9128             
9129             cfg.cn = [
9130                 {
9131                     tag : 'i',
9132                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9133                     tooltip : 'This field is required'
9134                 },
9135                 {
9136                     tag: 'label',
9137                     'for' :  id,
9138                     cls : 'control-label',
9139                     html : this.fieldLabel
9140
9141                 },
9142                 {
9143                     cls : "", 
9144                     cn: [
9145                         inputblock
9146                     ]
9147                 }
9148             ];
9149             
9150             var labelCfg = cfg.cn[1];
9151             var contentCfg = cfg.cn[2];
9152             
9153             if(this.indicatorpos == 'right'){
9154                 cfg.cn = [
9155                     {
9156                         tag: 'label',
9157                         'for' :  id,
9158                         cls : 'control-label',
9159                         cn : [
9160                             {
9161                                 tag : 'span',
9162                                 html : this.fieldLabel
9163                             },
9164                             {
9165                                 tag : 'i',
9166                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9167                                 tooltip : 'This field is required'
9168                             }
9169                         ]
9170                     },
9171                     {
9172                         cls : "",
9173                         cn: [
9174                             inputblock
9175                         ]
9176                     }
9177
9178                 ];
9179                 
9180                 labelCfg = cfg.cn[0];
9181                 contentCfg = cfg.cn[1];
9182             
9183             }
9184             
9185             if(this.labelWidth > 12){
9186                 labelCfg.style = "width: " + this.labelWidth + 'px';
9187             }
9188             
9189             if(this.labelWidth < 13 && this.labelmd == 0){
9190                 this.labelmd = this.labelWidth;
9191             }
9192             
9193             if(this.labellg > 0){
9194                 labelCfg.cls += ' col-lg-' + this.labellg;
9195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9196             }
9197             
9198             if(this.labelmd > 0){
9199                 labelCfg.cls += ' col-md-' + this.labelmd;
9200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9201             }
9202             
9203             if(this.labelsm > 0){
9204                 labelCfg.cls += ' col-sm-' + this.labelsm;
9205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9206             }
9207             
9208             if(this.labelxs > 0){
9209                 labelCfg.cls += ' col-xs-' + this.labelxs;
9210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9211             }
9212             
9213             
9214         } else if ( this.fieldLabel.length) {
9215                 
9216             cfg.cn = [
9217                 {
9218                     tag : 'i',
9219                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9220                     tooltip : 'This field is required'
9221                 },
9222                 {
9223                     tag: 'label',
9224                    //cls : 'input-group-addon',
9225                     html : this.fieldLabel
9226
9227                 },
9228
9229                inputblock
9230
9231            ];
9232            
9233            if(this.indicatorpos == 'right'){
9234                 
9235                 cfg.cn = [
9236                     {
9237                         tag: 'label',
9238                        //cls : 'input-group-addon',
9239                         html : this.fieldLabel
9240
9241                     },
9242                     {
9243                         tag : 'i',
9244                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9245                         tooltip : 'This field is required'
9246                     },
9247
9248                    inputblock
9249
9250                ];
9251
9252             }
9253
9254         } else {
9255             
9256             cfg.cn = [
9257
9258                     inputblock
9259
9260             ];
9261                 
9262                 
9263         };
9264         
9265         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9266            cfg.cls += ' navbar-form';
9267         }
9268         
9269         if (this.parentType === 'NavGroup') {
9270            cfg.cls += ' navbar-form';
9271            cfg.tag = 'li';
9272         }
9273         
9274         return cfg;
9275         
9276     },
9277     /**
9278      * return the real input element.
9279      */
9280     inputEl: function ()
9281     {
9282         return this.el.select('input.form-control',true).first();
9283     },
9284     
9285     tooltipEl : function()
9286     {
9287         return this.inputEl();
9288     },
9289     
9290     indicatorEl : function()
9291     {
9292         var indicator = this.el.select('i.roo-required-indicator',true).first();
9293         
9294         if(!indicator){
9295             return false;
9296         }
9297         
9298         return indicator;
9299         
9300     },
9301     
9302     setDisabled : function(v)
9303     {
9304         var i  = this.inputEl().dom;
9305         if (!v) {
9306             i.removeAttribute('disabled');
9307             return;
9308             
9309         }
9310         i.setAttribute('disabled','true');
9311     },
9312     initEvents : function()
9313     {
9314           
9315         this.inputEl().on("keydown" , this.fireKey,  this);
9316         this.inputEl().on("focus", this.onFocus,  this);
9317         this.inputEl().on("blur", this.onBlur,  this);
9318         
9319         this.inputEl().relayEvent('keyup', this);
9320         
9321         this.indicator = this.indicatorEl();
9322         
9323         if(this.indicator){
9324             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9325         }
9326  
9327         // reference to original value for reset
9328         this.originalValue = this.getValue();
9329         //Roo.form.TextField.superclass.initEvents.call(this);
9330         if(this.validationEvent == 'keyup'){
9331             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9332             this.inputEl().on('keyup', this.filterValidation, this);
9333         }
9334         else if(this.validationEvent !== false){
9335             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9336         }
9337         
9338         if(this.selectOnFocus){
9339             this.on("focus", this.preFocus, this);
9340             
9341         }
9342         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9343             this.inputEl().on("keypress", this.filterKeys, this);
9344         } else {
9345             this.inputEl().relayEvent('keypress', this);
9346         }
9347        /* if(this.grow){
9348             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9349             this.el.on("click", this.autoSize,  this);
9350         }
9351         */
9352         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9353             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9354         }
9355         
9356         if (typeof(this.before) == 'object') {
9357             this.before.render(this.el.select('.roo-input-before',true).first());
9358         }
9359         if (typeof(this.after) == 'object') {
9360             this.after.render(this.el.select('.roo-input-after',true).first());
9361         }
9362         
9363         this.inputEl().on('change', this.onChange, this);
9364         
9365     },
9366     filterValidation : function(e){
9367         if(!e.isNavKeyPress()){
9368             this.validationTask.delay(this.validationDelay);
9369         }
9370     },
9371      /**
9372      * Validates the field value
9373      * @return {Boolean} True if the value is valid, else false
9374      */
9375     validate : function(){
9376         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9377         if(this.disabled || this.validateValue(this.getRawValue())){
9378             this.markValid();
9379             return true;
9380         }
9381         
9382         this.markInvalid();
9383         return false;
9384     },
9385     
9386     
9387     /**
9388      * Validates a value according to the field's validation rules and marks the field as invalid
9389      * if the validation fails
9390      * @param {Mixed} value The value to validate
9391      * @return {Boolean} True if the value is valid, else false
9392      */
9393     validateValue : function(value)
9394     {
9395         if(this.getVisibilityEl().hasClass('hidden')){
9396             return true;
9397         }
9398         
9399         if(value.length < 1)  { // if it's blank
9400             if(this.allowBlank){
9401                 return true;
9402             }
9403             return false;
9404         }
9405         
9406         if(value.length < this.minLength){
9407             return false;
9408         }
9409         if(value.length > this.maxLength){
9410             return false;
9411         }
9412         if(this.vtype){
9413             var vt = Roo.form.VTypes;
9414             if(!vt[this.vtype](value, this)){
9415                 return false;
9416             }
9417         }
9418         if(typeof this.validator == "function"){
9419             var msg = this.validator(value);
9420             if(msg !== true){
9421                 return false;
9422             }
9423             if (typeof(msg) == 'string') {
9424                 this.invalidText = msg;
9425             }
9426         }
9427         
9428         if(this.regex && !this.regex.test(value)){
9429             return false;
9430         }
9431         
9432         return true;
9433     },
9434     
9435      // private
9436     fireKey : function(e){
9437         //Roo.log('field ' + e.getKey());
9438         if(e.isNavKeyPress()){
9439             this.fireEvent("specialkey", this, e);
9440         }
9441     },
9442     focus : function (selectText){
9443         if(this.rendered){
9444             this.inputEl().focus();
9445             if(selectText === true){
9446                 this.inputEl().dom.select();
9447             }
9448         }
9449         return this;
9450     } ,
9451     
9452     onFocus : function(){
9453         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9454            // this.el.addClass(this.focusClass);
9455         }
9456         if(!this.hasFocus){
9457             this.hasFocus = true;
9458             this.startValue = this.getValue();
9459             this.fireEvent("focus", this);
9460         }
9461     },
9462     
9463     beforeBlur : Roo.emptyFn,
9464
9465     
9466     // private
9467     onBlur : function(){
9468         this.beforeBlur();
9469         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9470             //this.el.removeClass(this.focusClass);
9471         }
9472         this.hasFocus = false;
9473         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9474             this.validate();
9475         }
9476         var v = this.getValue();
9477         if(String(v) !== String(this.startValue)){
9478             this.fireEvent('change', this, v, this.startValue);
9479         }
9480         this.fireEvent("blur", this);
9481     },
9482     
9483     onChange : function(e)
9484     {
9485         var v = this.getValue();
9486         if(String(v) !== String(this.startValue)){
9487             this.fireEvent('change', this, v, this.startValue);
9488         }
9489         
9490     },
9491     
9492     /**
9493      * Resets the current field value to the originally loaded value and clears any validation messages
9494      */
9495     reset : function(){
9496         this.setValue(this.originalValue);
9497         this.validate();
9498     },
9499      /**
9500      * Returns the name of the field
9501      * @return {Mixed} name The name field
9502      */
9503     getName: function(){
9504         return this.name;
9505     },
9506      /**
9507      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9508      * @return {Mixed} value The field value
9509      */
9510     getValue : function(){
9511         
9512         var v = this.inputEl().getValue();
9513         
9514         return v;
9515     },
9516     /**
9517      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9518      * @return {Mixed} value The field value
9519      */
9520     getRawValue : function(){
9521         var v = this.inputEl().getValue();
9522         
9523         return v;
9524     },
9525     
9526     /**
9527      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9528      * @param {Mixed} value The value to set
9529      */
9530     setRawValue : function(v){
9531         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9532     },
9533     
9534     selectText : function(start, end){
9535         var v = this.getRawValue();
9536         if(v.length > 0){
9537             start = start === undefined ? 0 : start;
9538             end = end === undefined ? v.length : end;
9539             var d = this.inputEl().dom;
9540             if(d.setSelectionRange){
9541                 d.setSelectionRange(start, end);
9542             }else if(d.createTextRange){
9543                 var range = d.createTextRange();
9544                 range.moveStart("character", start);
9545                 range.moveEnd("character", v.length-end);
9546                 range.select();
9547             }
9548         }
9549     },
9550     
9551     /**
9552      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9553      * @param {Mixed} value The value to set
9554      */
9555     setValue : function(v){
9556         this.value = v;
9557         if(this.rendered){
9558             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9559             this.validate();
9560         }
9561     },
9562     
9563     /*
9564     processValue : function(value){
9565         if(this.stripCharsRe){
9566             var newValue = value.replace(this.stripCharsRe, '');
9567             if(newValue !== value){
9568                 this.setRawValue(newValue);
9569                 return newValue;
9570             }
9571         }
9572         return value;
9573     },
9574   */
9575     preFocus : function(){
9576         
9577         if(this.selectOnFocus){
9578             this.inputEl().dom.select();
9579         }
9580     },
9581     filterKeys : function(e){
9582         var k = e.getKey();
9583         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9584             return;
9585         }
9586         var c = e.getCharCode(), cc = String.fromCharCode(c);
9587         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9588             return;
9589         }
9590         if(!this.maskRe.test(cc)){
9591             e.stopEvent();
9592         }
9593     },
9594      /**
9595      * Clear any invalid styles/messages for this field
9596      */
9597     clearInvalid : function(){
9598         
9599         if(!this.el || this.preventMark){ // not rendered
9600             return;
9601         }
9602         
9603      
9604         this.el.removeClass(this.invalidClass);
9605         
9606         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9607             
9608             var feedback = this.el.select('.form-control-feedback', true).first();
9609             
9610             if(feedback){
9611                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9612             }
9613             
9614         }
9615         
9616         if(this.indicator){
9617             this.indicator.removeClass('visible');
9618             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9619         }
9620         
9621         this.fireEvent('valid', this);
9622     },
9623     
9624      /**
9625      * Mark this field as valid
9626      */
9627     markValid : function()
9628     {
9629         if(!this.el  || this.preventMark){ // not rendered...
9630             return;
9631         }
9632         
9633         this.el.removeClass([this.invalidClass, this.validClass]);
9634         
9635         var feedback = this.el.select('.form-control-feedback', true).first();
9636             
9637         if(feedback){
9638             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9639         }
9640         
9641         if(this.indicator){
9642             this.indicator.removeClass('visible');
9643             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9644         }
9645         
9646         if(this.disabled){
9647             return;
9648         }
9649         
9650         if(this.allowBlank && !this.getRawValue().length){
9651             return;
9652         }
9653         
9654         this.el.addClass(this.validClass);
9655         
9656         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9657             
9658             var feedback = this.el.select('.form-control-feedback', true).first();
9659             
9660             if(feedback){
9661                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9662                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9663             }
9664             
9665         }
9666         
9667         this.fireEvent('valid', this);
9668     },
9669     
9670      /**
9671      * Mark this field as invalid
9672      * @param {String} msg The validation message
9673      */
9674     markInvalid : function(msg)
9675     {
9676         if(!this.el  || this.preventMark){ // not rendered
9677             return;
9678         }
9679         
9680         this.el.removeClass([this.invalidClass, this.validClass]);
9681         
9682         var feedback = this.el.select('.form-control-feedback', true).first();
9683             
9684         if(feedback){
9685             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9686         }
9687
9688         if(this.disabled){
9689             return;
9690         }
9691         
9692         if(this.allowBlank && !this.getRawValue().length){
9693             return;
9694         }
9695         
9696         if(this.indicator){
9697             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9698             this.indicator.addClass('visible');
9699         }
9700         
9701         this.el.addClass(this.invalidClass);
9702         
9703         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9704             
9705             var feedback = this.el.select('.form-control-feedback', true).first();
9706             
9707             if(feedback){
9708                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9709                 
9710                 if(this.getValue().length || this.forceFeedback){
9711                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9712                 }
9713                 
9714             }
9715             
9716         }
9717         
9718         this.fireEvent('invalid', this, msg);
9719     },
9720     // private
9721     SafariOnKeyDown : function(event)
9722     {
9723         // this is a workaround for a password hang bug on chrome/ webkit.
9724         if (this.inputEl().dom.type != 'password') {
9725             return;
9726         }
9727         
9728         var isSelectAll = false;
9729         
9730         if(this.inputEl().dom.selectionEnd > 0){
9731             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9732         }
9733         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9734             event.preventDefault();
9735             this.setValue('');
9736             return;
9737         }
9738         
9739         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9740             
9741             event.preventDefault();
9742             // this is very hacky as keydown always get's upper case.
9743             //
9744             var cc = String.fromCharCode(event.getCharCode());
9745             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9746             
9747         }
9748     },
9749     adjustWidth : function(tag, w){
9750         tag = tag.toLowerCase();
9751         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9752             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9753                 if(tag == 'input'){
9754                     return w + 2;
9755                 }
9756                 if(tag == 'textarea'){
9757                     return w-2;
9758                 }
9759             }else if(Roo.isOpera){
9760                 if(tag == 'input'){
9761                     return w + 2;
9762                 }
9763                 if(tag == 'textarea'){
9764                     return w-2;
9765                 }
9766             }
9767         }
9768         return w;
9769     },
9770     
9771     setFieldLabel : function(v)
9772     {
9773         if(!this.rendered){
9774             return;
9775         }
9776         
9777         if(this.indicator){
9778             var ar = this.el.select('label > span',true);
9779             
9780             if (ar.elements.length) {
9781                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9782                 this.fieldLabel = v;
9783                 return;
9784             }
9785             
9786             var br = this.el.select('label',true);
9787             
9788             if(br.elements.length) {
9789                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9790                 this.fieldLabel = v;
9791                 return;
9792             }
9793             
9794             Roo.log('Cannot Found any of label > span || label in input');
9795             return;
9796         }
9797         
9798         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9799         this.fieldLabel = v;
9800         
9801         
9802     }
9803 });
9804
9805  
9806 /*
9807  * - LGPL
9808  *
9809  * Input
9810  * 
9811  */
9812
9813 /**
9814  * @class Roo.bootstrap.TextArea
9815  * @extends Roo.bootstrap.Input
9816  * Bootstrap TextArea class
9817  * @cfg {Number} cols Specifies the visible width of a text area
9818  * @cfg {Number} rows Specifies the visible number of lines in a text area
9819  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9820  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9821  * @cfg {string} html text
9822  * 
9823  * @constructor
9824  * Create a new TextArea
9825  * @param {Object} config The config object
9826  */
9827
9828 Roo.bootstrap.TextArea = function(config){
9829     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9830    
9831 };
9832
9833 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9834      
9835     cols : false,
9836     rows : 5,
9837     readOnly : false,
9838     warp : 'soft',
9839     resize : false,
9840     value: false,
9841     html: false,
9842     
9843     getAutoCreate : function(){
9844         
9845         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9846         
9847         var id = Roo.id();
9848         
9849         var cfg = {};
9850         
9851         if(this.inputType != 'hidden'){
9852             cfg.cls = 'form-group' //input-group
9853         }
9854         
9855         var input =  {
9856             tag: 'textarea',
9857             id : id,
9858             warp : this.warp,
9859             rows : this.rows,
9860             value : this.value || '',
9861             html: this.html || '',
9862             cls : 'form-control',
9863             placeholder : this.placeholder || '' 
9864             
9865         };
9866         
9867         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9868             input.maxLength = this.maxLength;
9869         }
9870         
9871         if(this.resize){
9872             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9873         }
9874         
9875         if(this.cols){
9876             input.cols = this.cols;
9877         }
9878         
9879         if (this.readOnly) {
9880             input.readonly = true;
9881         }
9882         
9883         if (this.name) {
9884             input.name = this.name;
9885         }
9886         
9887         if (this.size) {
9888             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9889         }
9890         
9891         var settings=this;
9892         ['xs','sm','md','lg'].map(function(size){
9893             if (settings[size]) {
9894                 cfg.cls += ' col-' + size + '-' + settings[size];
9895             }
9896         });
9897         
9898         var inputblock = input;
9899         
9900         if(this.hasFeedback && !this.allowBlank){
9901             
9902             var feedback = {
9903                 tag: 'span',
9904                 cls: 'glyphicon form-control-feedback'
9905             };
9906
9907             inputblock = {
9908                 cls : 'has-feedback',
9909                 cn :  [
9910                     input,
9911                     feedback
9912                 ] 
9913             };  
9914         }
9915         
9916         
9917         if (this.before || this.after) {
9918             
9919             inputblock = {
9920                 cls : 'input-group',
9921                 cn :  [] 
9922             };
9923             if (this.before) {
9924                 inputblock.cn.push({
9925                     tag :'span',
9926                     cls : 'input-group-addon',
9927                     html : this.before
9928                 });
9929             }
9930             
9931             inputblock.cn.push(input);
9932             
9933             if(this.hasFeedback && !this.allowBlank){
9934                 inputblock.cls += ' has-feedback';
9935                 inputblock.cn.push(feedback);
9936             }
9937             
9938             if (this.after) {
9939                 inputblock.cn.push({
9940                     tag :'span',
9941                     cls : 'input-group-addon',
9942                     html : this.after
9943                 });
9944             }
9945             
9946         }
9947         
9948         if (align ==='left' && this.fieldLabel.length) {
9949             cfg.cn = [
9950                 {
9951                     tag: 'label',
9952                     'for' :  id,
9953                     cls : 'control-label',
9954                     html : this.fieldLabel
9955                 },
9956                 {
9957                     cls : "",
9958                     cn: [
9959                         inputblock
9960                     ]
9961                 }
9962
9963             ];
9964             
9965             if(this.labelWidth > 12){
9966                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9967             }
9968
9969             if(this.labelWidth < 13 && this.labelmd == 0){
9970                 this.labelmd = this.labelWidth;
9971             }
9972
9973             if(this.labellg > 0){
9974                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9975                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9976             }
9977
9978             if(this.labelmd > 0){
9979                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9980                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9981             }
9982
9983             if(this.labelsm > 0){
9984                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9985                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9986             }
9987
9988             if(this.labelxs > 0){
9989                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9990                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9991             }
9992             
9993         } else if ( this.fieldLabel.length) {
9994             cfg.cn = [
9995
9996                {
9997                    tag: 'label',
9998                    //cls : 'input-group-addon',
9999                    html : this.fieldLabel
10000
10001                },
10002
10003                inputblock
10004
10005            ];
10006
10007         } else {
10008
10009             cfg.cn = [
10010
10011                 inputblock
10012
10013             ];
10014                 
10015         }
10016         
10017         if (this.disabled) {
10018             input.disabled=true;
10019         }
10020         
10021         return cfg;
10022         
10023     },
10024     /**
10025      * return the real textarea element.
10026      */
10027     inputEl: function ()
10028     {
10029         return this.el.select('textarea.form-control',true).first();
10030     },
10031     
10032     /**
10033      * Clear any invalid styles/messages for this field
10034      */
10035     clearInvalid : function()
10036     {
10037         
10038         if(!this.el || this.preventMark){ // not rendered
10039             return;
10040         }
10041         
10042         var label = this.el.select('label', true).first();
10043         var icon = this.el.select('i.fa-star', true).first();
10044         
10045         if(label && icon){
10046             icon.remove();
10047         }
10048         
10049         this.el.removeClass(this.invalidClass);
10050         
10051         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10052             
10053             var feedback = this.el.select('.form-control-feedback', true).first();
10054             
10055             if(feedback){
10056                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10057             }
10058             
10059         }
10060         
10061         this.fireEvent('valid', this);
10062     },
10063     
10064      /**
10065      * Mark this field as valid
10066      */
10067     markValid : function()
10068     {
10069         if(!this.el  || this.preventMark){ // not rendered
10070             return;
10071         }
10072         
10073         this.el.removeClass([this.invalidClass, this.validClass]);
10074         
10075         var feedback = this.el.select('.form-control-feedback', true).first();
10076             
10077         if(feedback){
10078             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10079         }
10080
10081         if(this.disabled || this.allowBlank){
10082             return;
10083         }
10084         
10085         var label = this.el.select('label', true).first();
10086         var icon = this.el.select('i.fa-star', true).first();
10087         
10088         if(label && icon){
10089             icon.remove();
10090         }
10091         
10092         this.el.addClass(this.validClass);
10093         
10094         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10095             
10096             var feedback = this.el.select('.form-control-feedback', true).first();
10097             
10098             if(feedback){
10099                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10100                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10101             }
10102             
10103         }
10104         
10105         this.fireEvent('valid', this);
10106     },
10107     
10108      /**
10109      * Mark this field as invalid
10110      * @param {String} msg The validation message
10111      */
10112     markInvalid : function(msg)
10113     {
10114         if(!this.el  || this.preventMark){ // not rendered
10115             return;
10116         }
10117         
10118         this.el.removeClass([this.invalidClass, this.validClass]);
10119         
10120         var feedback = this.el.select('.form-control-feedback', true).first();
10121             
10122         if(feedback){
10123             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10124         }
10125
10126         if(this.disabled || this.allowBlank){
10127             return;
10128         }
10129         
10130         var label = this.el.select('label', true).first();
10131         var icon = this.el.select('i.fa-star', true).first();
10132         
10133         if(!this.getValue().length && label && !icon){
10134             this.el.createChild({
10135                 tag : 'i',
10136                 cls : 'text-danger fa fa-lg fa-star',
10137                 tooltip : 'This field is required',
10138                 style : 'margin-right:5px;'
10139             }, label, true);
10140         }
10141
10142         this.el.addClass(this.invalidClass);
10143         
10144         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10145             
10146             var feedback = this.el.select('.form-control-feedback', true).first();
10147             
10148             if(feedback){
10149                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10150                 
10151                 if(this.getValue().length || this.forceFeedback){
10152                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10153                 }
10154                 
10155             }
10156             
10157         }
10158         
10159         this.fireEvent('invalid', this, msg);
10160     }
10161 });
10162
10163  
10164 /*
10165  * - LGPL
10166  *
10167  * trigger field - base class for combo..
10168  * 
10169  */
10170  
10171 /**
10172  * @class Roo.bootstrap.TriggerField
10173  * @extends Roo.bootstrap.Input
10174  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10175  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10176  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10177  * for which you can provide a custom implementation.  For example:
10178  * <pre><code>
10179 var trigger = new Roo.bootstrap.TriggerField();
10180 trigger.onTriggerClick = myTriggerFn;
10181 trigger.applyTo('my-field');
10182 </code></pre>
10183  *
10184  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10185  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10186  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10187  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10188  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10189
10190  * @constructor
10191  * Create a new TriggerField.
10192  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10193  * to the base TextField)
10194  */
10195 Roo.bootstrap.TriggerField = function(config){
10196     this.mimicing = false;
10197     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10198 };
10199
10200 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10201     /**
10202      * @cfg {String} triggerClass A CSS class to apply to the trigger
10203      */
10204      /**
10205      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10206      */
10207     hideTrigger:false,
10208
10209     /**
10210      * @cfg {Boolean} removable (true|false) special filter default false
10211      */
10212     removable : false,
10213     
10214     /** @cfg {Boolean} grow @hide */
10215     /** @cfg {Number} growMin @hide */
10216     /** @cfg {Number} growMax @hide */
10217
10218     /**
10219      * @hide 
10220      * @method
10221      */
10222     autoSize: Roo.emptyFn,
10223     // private
10224     monitorTab : true,
10225     // private
10226     deferHeight : true,
10227
10228     
10229     actionMode : 'wrap',
10230     
10231     caret : false,
10232     
10233     
10234     getAutoCreate : function(){
10235        
10236         var align = this.labelAlign || this.parentLabelAlign();
10237         
10238         var id = Roo.id();
10239         
10240         var cfg = {
10241             cls: 'form-group' //input-group
10242         };
10243         
10244         
10245         var input =  {
10246             tag: 'input',
10247             id : id,
10248             type : this.inputType,
10249             cls : 'form-control',
10250             autocomplete: 'new-password',
10251             placeholder : this.placeholder || '' 
10252             
10253         };
10254         if (this.name) {
10255             input.name = this.name;
10256         }
10257         if (this.size) {
10258             input.cls += ' input-' + this.size;
10259         }
10260         
10261         if (this.disabled) {
10262             input.disabled=true;
10263         }
10264         
10265         var inputblock = input;
10266         
10267         if(this.hasFeedback && !this.allowBlank){
10268             
10269             var feedback = {
10270                 tag: 'span',
10271                 cls: 'glyphicon form-control-feedback'
10272             };
10273             
10274             if(this.removable && !this.editable && !this.tickable){
10275                 inputblock = {
10276                     cls : 'has-feedback',
10277                     cn :  [
10278                         inputblock,
10279                         {
10280                             tag: 'button',
10281                             html : 'x',
10282                             cls : 'roo-combo-removable-btn close'
10283                         },
10284                         feedback
10285                     ] 
10286                 };
10287             } else {
10288                 inputblock = {
10289                     cls : 'has-feedback',
10290                     cn :  [
10291                         inputblock,
10292                         feedback
10293                     ] 
10294                 };
10295             }
10296
10297         } else {
10298             if(this.removable && !this.editable && !this.tickable){
10299                 inputblock = {
10300                     cls : 'roo-removable',
10301                     cn :  [
10302                         inputblock,
10303                         {
10304                             tag: 'button',
10305                             html : 'x',
10306                             cls : 'roo-combo-removable-btn close'
10307                         }
10308                     ] 
10309                 };
10310             }
10311         }
10312         
10313         if (this.before || this.after) {
10314             
10315             inputblock = {
10316                 cls : 'input-group',
10317                 cn :  [] 
10318             };
10319             if (this.before) {
10320                 inputblock.cn.push({
10321                     tag :'span',
10322                     cls : 'input-group-addon',
10323                     html : this.before
10324                 });
10325             }
10326             
10327             inputblock.cn.push(input);
10328             
10329             if(this.hasFeedback && !this.allowBlank){
10330                 inputblock.cls += ' has-feedback';
10331                 inputblock.cn.push(feedback);
10332             }
10333             
10334             if (this.after) {
10335                 inputblock.cn.push({
10336                     tag :'span',
10337                     cls : 'input-group-addon',
10338                     html : this.after
10339                 });
10340             }
10341             
10342         };
10343         
10344         var box = {
10345             tag: 'div',
10346             cn: [
10347                 {
10348                     tag: 'input',
10349                     type : 'hidden',
10350                     cls: 'form-hidden-field'
10351                 },
10352                 inputblock
10353             ]
10354             
10355         };
10356         
10357         if(this.multiple){
10358             box = {
10359                 tag: 'div',
10360                 cn: [
10361                     {
10362                         tag: 'input',
10363                         type : 'hidden',
10364                         cls: 'form-hidden-field'
10365                     },
10366                     {
10367                         tag: 'ul',
10368                         cls: 'roo-select2-choices',
10369                         cn:[
10370                             {
10371                                 tag: 'li',
10372                                 cls: 'roo-select2-search-field',
10373                                 cn: [
10374
10375                                     inputblock
10376                                 ]
10377                             }
10378                         ]
10379                     }
10380                 ]
10381             }
10382         };
10383         
10384         var combobox = {
10385             cls: 'roo-select2-container input-group',
10386             cn: [
10387                 box
10388 //                {
10389 //                    tag: 'ul',
10390 //                    cls: 'typeahead typeahead-long dropdown-menu',
10391 //                    style: 'display:none'
10392 //                }
10393             ]
10394         };
10395         
10396         if(!this.multiple && this.showToggleBtn){
10397             
10398             var caret = {
10399                         tag: 'span',
10400                         cls: 'caret'
10401              };
10402             if (this.caret != false) {
10403                 caret = {
10404                      tag: 'i',
10405                      cls: 'fa fa-' + this.caret
10406                 };
10407                 
10408             }
10409             
10410             combobox.cn.push({
10411                 tag :'span',
10412                 cls : 'input-group-addon btn dropdown-toggle',
10413                 cn : [
10414                     caret,
10415                     {
10416                         tag: 'span',
10417                         cls: 'combobox-clear',
10418                         cn  : [
10419                             {
10420                                 tag : 'i',
10421                                 cls: 'icon-remove'
10422                             }
10423                         ]
10424                     }
10425                 ]
10426
10427             })
10428         }
10429         
10430         if(this.multiple){
10431             combobox.cls += ' roo-select2-container-multi';
10432         }
10433         
10434         if (align ==='left' && this.fieldLabel.length) {
10435             
10436             cfg.cls += ' roo-form-group-label-left';
10437
10438             cfg.cn = [
10439                 {
10440                     tag : 'i',
10441                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10442                     tooltip : 'This field is required'
10443                 },
10444                 {
10445                     tag: 'label',
10446                     'for' :  id,
10447                     cls : 'control-label',
10448                     html : this.fieldLabel
10449
10450                 },
10451                 {
10452                     cls : "", 
10453                     cn: [
10454                         combobox
10455                     ]
10456                 }
10457
10458             ];
10459             
10460             var labelCfg = cfg.cn[1];
10461             var contentCfg = cfg.cn[2];
10462             
10463             if(this.indicatorpos == 'right'){
10464                 cfg.cn = [
10465                     {
10466                         tag: 'label',
10467                         'for' :  id,
10468                         cls : 'control-label',
10469                         cn : [
10470                             {
10471                                 tag : 'span',
10472                                 html : this.fieldLabel
10473                             },
10474                             {
10475                                 tag : 'i',
10476                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10477                                 tooltip : 'This field is required'
10478                             }
10479                         ]
10480                     },
10481                     {
10482                         cls : "", 
10483                         cn: [
10484                             combobox
10485                         ]
10486                     }
10487
10488                 ];
10489                 
10490                 labelCfg = cfg.cn[0];
10491                 contentCfg = cfg.cn[1];
10492             }
10493             
10494             if(this.labelWidth > 12){
10495                 labelCfg.style = "width: " + this.labelWidth + 'px';
10496             }
10497             
10498             if(this.labelWidth < 13 && this.labelmd == 0){
10499                 this.labelmd = this.labelWidth;
10500             }
10501             
10502             if(this.labellg > 0){
10503                 labelCfg.cls += ' col-lg-' + this.labellg;
10504                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10505             }
10506             
10507             if(this.labelmd > 0){
10508                 labelCfg.cls += ' col-md-' + this.labelmd;
10509                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10510             }
10511             
10512             if(this.labelsm > 0){
10513                 labelCfg.cls += ' col-sm-' + this.labelsm;
10514                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10515             }
10516             
10517             if(this.labelxs > 0){
10518                 labelCfg.cls += ' col-xs-' + this.labelxs;
10519                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10520             }
10521             
10522         } else if ( this.fieldLabel.length) {
10523 //                Roo.log(" label");
10524             cfg.cn = [
10525                 {
10526                    tag : 'i',
10527                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10528                    tooltip : 'This field is required'
10529                },
10530                {
10531                    tag: 'label',
10532                    //cls : 'input-group-addon',
10533                    html : this.fieldLabel
10534
10535                },
10536
10537                combobox
10538
10539             ];
10540             
10541             if(this.indicatorpos == 'right'){
10542                 
10543                 cfg.cn = [
10544                     {
10545                        tag: 'label',
10546                        cn : [
10547                            {
10548                                tag : 'span',
10549                                html : this.fieldLabel
10550                            },
10551                            {
10552                               tag : 'i',
10553                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10554                               tooltip : 'This field is required'
10555                            }
10556                        ]
10557
10558                     },
10559                     combobox
10560
10561                 ];
10562
10563             }
10564
10565         } else {
10566             
10567 //                Roo.log(" no label && no align");
10568                 cfg = combobox
10569                      
10570                 
10571         }
10572         
10573         var settings=this;
10574         ['xs','sm','md','lg'].map(function(size){
10575             if (settings[size]) {
10576                 cfg.cls += ' col-' + size + '-' + settings[size];
10577             }
10578         });
10579         
10580         return cfg;
10581         
10582     },
10583     
10584     
10585     
10586     // private
10587     onResize : function(w, h){
10588 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10589 //        if(typeof w == 'number'){
10590 //            var x = w - this.trigger.getWidth();
10591 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10592 //            this.trigger.setStyle('left', x+'px');
10593 //        }
10594     },
10595
10596     // private
10597     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10598
10599     // private
10600     getResizeEl : function(){
10601         return this.inputEl();
10602     },
10603
10604     // private
10605     getPositionEl : function(){
10606         return this.inputEl();
10607     },
10608
10609     // private
10610     alignErrorIcon : function(){
10611         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10612     },
10613
10614     // private
10615     initEvents : function(){
10616         
10617         this.createList();
10618         
10619         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10620         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10621         if(!this.multiple && this.showToggleBtn){
10622             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10623             if(this.hideTrigger){
10624                 this.trigger.setDisplayed(false);
10625             }
10626             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10627         }
10628         
10629         if(this.multiple){
10630             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10631         }
10632         
10633         if(this.removable && !this.editable && !this.tickable){
10634             var close = this.closeTriggerEl();
10635             
10636             if(close){
10637                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10638                 close.on('click', this.removeBtnClick, this, close);
10639             }
10640         }
10641         
10642         //this.trigger.addClassOnOver('x-form-trigger-over');
10643         //this.trigger.addClassOnClick('x-form-trigger-click');
10644         
10645         //if(!this.width){
10646         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10647         //}
10648     },
10649     
10650     closeTriggerEl : function()
10651     {
10652         var close = this.el.select('.roo-combo-removable-btn', true).first();
10653         return close ? close : false;
10654     },
10655     
10656     removeBtnClick : function(e, h, el)
10657     {
10658         e.preventDefault();
10659         
10660         if(this.fireEvent("remove", this) !== false){
10661             this.reset();
10662             this.fireEvent("afterremove", this)
10663         }
10664     },
10665     
10666     createList : function()
10667     {
10668         this.list = Roo.get(document.body).createChild({
10669             tag: 'ul',
10670             cls: 'typeahead typeahead-long dropdown-menu',
10671             style: 'display:none'
10672         });
10673         
10674         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10675         
10676     },
10677
10678     // private
10679     initTrigger : function(){
10680        
10681     },
10682
10683     // private
10684     onDestroy : function(){
10685         if(this.trigger){
10686             this.trigger.removeAllListeners();
10687           //  this.trigger.remove();
10688         }
10689         //if(this.wrap){
10690         //    this.wrap.remove();
10691         //}
10692         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10693     },
10694
10695     // private
10696     onFocus : function(){
10697         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10698         /*
10699         if(!this.mimicing){
10700             this.wrap.addClass('x-trigger-wrap-focus');
10701             this.mimicing = true;
10702             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10703             if(this.monitorTab){
10704                 this.el.on("keydown", this.checkTab, this);
10705             }
10706         }
10707         */
10708     },
10709
10710     // private
10711     checkTab : function(e){
10712         if(e.getKey() == e.TAB){
10713             this.triggerBlur();
10714         }
10715     },
10716
10717     // private
10718     onBlur : function(){
10719         // do nothing
10720     },
10721
10722     // private
10723     mimicBlur : function(e, t){
10724         /*
10725         if(!this.wrap.contains(t) && this.validateBlur()){
10726             this.triggerBlur();
10727         }
10728         */
10729     },
10730
10731     // private
10732     triggerBlur : function(){
10733         this.mimicing = false;
10734         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10735         if(this.monitorTab){
10736             this.el.un("keydown", this.checkTab, this);
10737         }
10738         //this.wrap.removeClass('x-trigger-wrap-focus');
10739         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10740     },
10741
10742     // private
10743     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10744     validateBlur : function(e, t){
10745         return true;
10746     },
10747
10748     // private
10749     onDisable : function(){
10750         this.inputEl().dom.disabled = true;
10751         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10752         //if(this.wrap){
10753         //    this.wrap.addClass('x-item-disabled');
10754         //}
10755     },
10756
10757     // private
10758     onEnable : function(){
10759         this.inputEl().dom.disabled = false;
10760         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10761         //if(this.wrap){
10762         //    this.el.removeClass('x-item-disabled');
10763         //}
10764     },
10765
10766     // private
10767     onShow : function(){
10768         var ae = this.getActionEl();
10769         
10770         if(ae){
10771             ae.dom.style.display = '';
10772             ae.dom.style.visibility = 'visible';
10773         }
10774     },
10775
10776     // private
10777     
10778     onHide : function(){
10779         var ae = this.getActionEl();
10780         ae.dom.style.display = 'none';
10781     },
10782
10783     /**
10784      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10785      * by an implementing function.
10786      * @method
10787      * @param {EventObject} e
10788      */
10789     onTriggerClick : Roo.emptyFn
10790 });
10791  /*
10792  * Based on:
10793  * Ext JS Library 1.1.1
10794  * Copyright(c) 2006-2007, Ext JS, LLC.
10795  *
10796  * Originally Released Under LGPL - original licence link has changed is not relivant.
10797  *
10798  * Fork - LGPL
10799  * <script type="text/javascript">
10800  */
10801
10802
10803 /**
10804  * @class Roo.data.SortTypes
10805  * @singleton
10806  * Defines the default sorting (casting?) comparison functions used when sorting data.
10807  */
10808 Roo.data.SortTypes = {
10809     /**
10810      * Default sort that does nothing
10811      * @param {Mixed} s The value being converted
10812      * @return {Mixed} The comparison value
10813      */
10814     none : function(s){
10815         return s;
10816     },
10817     
10818     /**
10819      * The regular expression used to strip tags
10820      * @type {RegExp}
10821      * @property
10822      */
10823     stripTagsRE : /<\/?[^>]+>/gi,
10824     
10825     /**
10826      * Strips all HTML tags to sort on text only
10827      * @param {Mixed} s The value being converted
10828      * @return {String} The comparison value
10829      */
10830     asText : function(s){
10831         return String(s).replace(this.stripTagsRE, "");
10832     },
10833     
10834     /**
10835      * Strips all HTML tags to sort on text only - Case insensitive
10836      * @param {Mixed} s The value being converted
10837      * @return {String} The comparison value
10838      */
10839     asUCText : function(s){
10840         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10841     },
10842     
10843     /**
10844      * Case insensitive string
10845      * @param {Mixed} s The value being converted
10846      * @return {String} The comparison value
10847      */
10848     asUCString : function(s) {
10849         return String(s).toUpperCase();
10850     },
10851     
10852     /**
10853      * Date sorting
10854      * @param {Mixed} s The value being converted
10855      * @return {Number} The comparison value
10856      */
10857     asDate : function(s) {
10858         if(!s){
10859             return 0;
10860         }
10861         if(s instanceof Date){
10862             return s.getTime();
10863         }
10864         return Date.parse(String(s));
10865     },
10866     
10867     /**
10868      * Float sorting
10869      * @param {Mixed} s The value being converted
10870      * @return {Float} The comparison value
10871      */
10872     asFloat : function(s) {
10873         var val = parseFloat(String(s).replace(/,/g, ""));
10874         if(isNaN(val)) {
10875             val = 0;
10876         }
10877         return val;
10878     },
10879     
10880     /**
10881      * Integer sorting
10882      * @param {Mixed} s The value being converted
10883      * @return {Number} The comparison value
10884      */
10885     asInt : function(s) {
10886         var val = parseInt(String(s).replace(/,/g, ""));
10887         if(isNaN(val)) {
10888             val = 0;
10889         }
10890         return val;
10891     }
10892 };/*
10893  * Based on:
10894  * Ext JS Library 1.1.1
10895  * Copyright(c) 2006-2007, Ext JS, LLC.
10896  *
10897  * Originally Released Under LGPL - original licence link has changed is not relivant.
10898  *
10899  * Fork - LGPL
10900  * <script type="text/javascript">
10901  */
10902
10903 /**
10904 * @class Roo.data.Record
10905  * Instances of this class encapsulate both record <em>definition</em> information, and record
10906  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10907  * to access Records cached in an {@link Roo.data.Store} object.<br>
10908  * <p>
10909  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10910  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10911  * objects.<br>
10912  * <p>
10913  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10914  * @constructor
10915  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10916  * {@link #create}. The parameters are the same.
10917  * @param {Array} data An associative Array of data values keyed by the field name.
10918  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10919  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10920  * not specified an integer id is generated.
10921  */
10922 Roo.data.Record = function(data, id){
10923     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10924     this.data = data;
10925 };
10926
10927 /**
10928  * Generate a constructor for a specific record layout.
10929  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10930  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10931  * Each field definition object may contain the following properties: <ul>
10932  * <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,
10933  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10934  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10935  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10936  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10937  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10938  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10939  * this may be omitted.</p></li>
10940  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10941  * <ul><li>auto (Default, implies no conversion)</li>
10942  * <li>string</li>
10943  * <li>int</li>
10944  * <li>float</li>
10945  * <li>boolean</li>
10946  * <li>date</li></ul></p></li>
10947  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10948  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10949  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10950  * by the Reader into an object that will be stored in the Record. It is passed the
10951  * following parameters:<ul>
10952  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10953  * </ul></p></li>
10954  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10955  * </ul>
10956  * <br>usage:<br><pre><code>
10957 var TopicRecord = Roo.data.Record.create(
10958     {name: 'title', mapping: 'topic_title'},
10959     {name: 'author', mapping: 'username'},
10960     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10961     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10962     {name: 'lastPoster', mapping: 'user2'},
10963     {name: 'excerpt', mapping: 'post_text'}
10964 );
10965
10966 var myNewRecord = new TopicRecord({
10967     title: 'Do my job please',
10968     author: 'noobie',
10969     totalPosts: 1,
10970     lastPost: new Date(),
10971     lastPoster: 'Animal',
10972     excerpt: 'No way dude!'
10973 });
10974 myStore.add(myNewRecord);
10975 </code></pre>
10976  * @method create
10977  * @static
10978  */
10979 Roo.data.Record.create = function(o){
10980     var f = function(){
10981         f.superclass.constructor.apply(this, arguments);
10982     };
10983     Roo.extend(f, Roo.data.Record);
10984     var p = f.prototype;
10985     p.fields = new Roo.util.MixedCollection(false, function(field){
10986         return field.name;
10987     });
10988     for(var i = 0, len = o.length; i < len; i++){
10989         p.fields.add(new Roo.data.Field(o[i]));
10990     }
10991     f.getField = function(name){
10992         return p.fields.get(name);  
10993     };
10994     return f;
10995 };
10996
10997 Roo.data.Record.AUTO_ID = 1000;
10998 Roo.data.Record.EDIT = 'edit';
10999 Roo.data.Record.REJECT = 'reject';
11000 Roo.data.Record.COMMIT = 'commit';
11001
11002 Roo.data.Record.prototype = {
11003     /**
11004      * Readonly flag - true if this record has been modified.
11005      * @type Boolean
11006      */
11007     dirty : false,
11008     editing : false,
11009     error: null,
11010     modified: null,
11011
11012     // private
11013     join : function(store){
11014         this.store = store;
11015     },
11016
11017     /**
11018      * Set the named field to the specified value.
11019      * @param {String} name The name of the field to set.
11020      * @param {Object} value The value to set the field to.
11021      */
11022     set : function(name, value){
11023         if(this.data[name] == value){
11024             return;
11025         }
11026         this.dirty = true;
11027         if(!this.modified){
11028             this.modified = {};
11029         }
11030         if(typeof this.modified[name] == 'undefined'){
11031             this.modified[name] = this.data[name];
11032         }
11033         this.data[name] = value;
11034         if(!this.editing && this.store){
11035             this.store.afterEdit(this);
11036         }       
11037     },
11038
11039     /**
11040      * Get the value of the named field.
11041      * @param {String} name The name of the field to get the value of.
11042      * @return {Object} The value of the field.
11043      */
11044     get : function(name){
11045         return this.data[name]; 
11046     },
11047
11048     // private
11049     beginEdit : function(){
11050         this.editing = true;
11051         this.modified = {}; 
11052     },
11053
11054     // private
11055     cancelEdit : function(){
11056         this.editing = false;
11057         delete this.modified;
11058     },
11059
11060     // private
11061     endEdit : function(){
11062         this.editing = false;
11063         if(this.dirty && this.store){
11064             this.store.afterEdit(this);
11065         }
11066     },
11067
11068     /**
11069      * Usually called by the {@link Roo.data.Store} which owns the Record.
11070      * Rejects all changes made to the Record since either creation, or the last commit operation.
11071      * Modified fields are reverted to their original values.
11072      * <p>
11073      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11074      * of reject operations.
11075      */
11076     reject : function(){
11077         var m = this.modified;
11078         for(var n in m){
11079             if(typeof m[n] != "function"){
11080                 this.data[n] = m[n];
11081             }
11082         }
11083         this.dirty = false;
11084         delete this.modified;
11085         this.editing = false;
11086         if(this.store){
11087             this.store.afterReject(this);
11088         }
11089     },
11090
11091     /**
11092      * Usually called by the {@link Roo.data.Store} which owns the Record.
11093      * Commits all changes made to the Record since either creation, or the last commit operation.
11094      * <p>
11095      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11096      * of commit operations.
11097      */
11098     commit : function(){
11099         this.dirty = false;
11100         delete this.modified;
11101         this.editing = false;
11102         if(this.store){
11103             this.store.afterCommit(this);
11104         }
11105     },
11106
11107     // private
11108     hasError : function(){
11109         return this.error != null;
11110     },
11111
11112     // private
11113     clearError : function(){
11114         this.error = null;
11115     },
11116
11117     /**
11118      * Creates a copy of this record.
11119      * @param {String} id (optional) A new record id if you don't want to use this record's id
11120      * @return {Record}
11121      */
11122     copy : function(newId) {
11123         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11124     }
11125 };/*
11126  * Based on:
11127  * Ext JS Library 1.1.1
11128  * Copyright(c) 2006-2007, Ext JS, LLC.
11129  *
11130  * Originally Released Under LGPL - original licence link has changed is not relivant.
11131  *
11132  * Fork - LGPL
11133  * <script type="text/javascript">
11134  */
11135
11136
11137
11138 /**
11139  * @class Roo.data.Store
11140  * @extends Roo.util.Observable
11141  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11142  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11143  * <p>
11144  * 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
11145  * has no knowledge of the format of the data returned by the Proxy.<br>
11146  * <p>
11147  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11148  * instances from the data object. These records are cached and made available through accessor functions.
11149  * @constructor
11150  * Creates a new Store.
11151  * @param {Object} config A config object containing the objects needed for the Store to access data,
11152  * and read the data into Records.
11153  */
11154 Roo.data.Store = function(config){
11155     this.data = new Roo.util.MixedCollection(false);
11156     this.data.getKey = function(o){
11157         return o.id;
11158     };
11159     this.baseParams = {};
11160     // private
11161     this.paramNames = {
11162         "start" : "start",
11163         "limit" : "limit",
11164         "sort" : "sort",
11165         "dir" : "dir",
11166         "multisort" : "_multisort"
11167     };
11168
11169     if(config && config.data){
11170         this.inlineData = config.data;
11171         delete config.data;
11172     }
11173
11174     Roo.apply(this, config);
11175     
11176     if(this.reader){ // reader passed
11177         this.reader = Roo.factory(this.reader, Roo.data);
11178         this.reader.xmodule = this.xmodule || false;
11179         if(!this.recordType){
11180             this.recordType = this.reader.recordType;
11181         }
11182         if(this.reader.onMetaChange){
11183             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11184         }
11185     }
11186
11187     if(this.recordType){
11188         this.fields = this.recordType.prototype.fields;
11189     }
11190     this.modified = [];
11191
11192     this.addEvents({
11193         /**
11194          * @event datachanged
11195          * Fires when the data cache has changed, and a widget which is using this Store
11196          * as a Record cache should refresh its view.
11197          * @param {Store} this
11198          */
11199         datachanged : true,
11200         /**
11201          * @event metachange
11202          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11203          * @param {Store} this
11204          * @param {Object} meta The JSON metadata
11205          */
11206         metachange : true,
11207         /**
11208          * @event add
11209          * Fires when Records have been added to the Store
11210          * @param {Store} this
11211          * @param {Roo.data.Record[]} records The array of Records added
11212          * @param {Number} index The index at which the record(s) were added
11213          */
11214         add : true,
11215         /**
11216          * @event remove
11217          * Fires when a Record has been removed from the Store
11218          * @param {Store} this
11219          * @param {Roo.data.Record} record The Record that was removed
11220          * @param {Number} index The index at which the record was removed
11221          */
11222         remove : true,
11223         /**
11224          * @event update
11225          * Fires when a Record has been updated
11226          * @param {Store} this
11227          * @param {Roo.data.Record} record The Record that was updated
11228          * @param {String} operation The update operation being performed.  Value may be one of:
11229          * <pre><code>
11230  Roo.data.Record.EDIT
11231  Roo.data.Record.REJECT
11232  Roo.data.Record.COMMIT
11233          * </code></pre>
11234          */
11235         update : true,
11236         /**
11237          * @event clear
11238          * Fires when the data cache has been cleared.
11239          * @param {Store} this
11240          */
11241         clear : true,
11242         /**
11243          * @event beforeload
11244          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11245          * the load action will be canceled.
11246          * @param {Store} this
11247          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11248          */
11249         beforeload : true,
11250         /**
11251          * @event beforeloadadd
11252          * Fires after a new set of Records has been loaded.
11253          * @param {Store} this
11254          * @param {Roo.data.Record[]} records The Records that were loaded
11255          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11256          */
11257         beforeloadadd : true,
11258         /**
11259          * @event load
11260          * Fires after a new set of Records has been loaded, before they are added to the store.
11261          * @param {Store} this
11262          * @param {Roo.data.Record[]} records The Records that were loaded
11263          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11264          * @params {Object} return from reader
11265          */
11266         load : true,
11267         /**
11268          * @event loadexception
11269          * Fires if an exception occurs in the Proxy during loading.
11270          * Called with the signature of the Proxy's "loadexception" event.
11271          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11272          * 
11273          * @param {Proxy} 
11274          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11275          * @param {Object} load options 
11276          * @param {Object} jsonData from your request (normally this contains the Exception)
11277          */
11278         loadexception : true
11279     });
11280     
11281     if(this.proxy){
11282         this.proxy = Roo.factory(this.proxy, Roo.data);
11283         this.proxy.xmodule = this.xmodule || false;
11284         this.relayEvents(this.proxy,  ["loadexception"]);
11285     }
11286     this.sortToggle = {};
11287     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11288
11289     Roo.data.Store.superclass.constructor.call(this);
11290
11291     if(this.inlineData){
11292         this.loadData(this.inlineData);
11293         delete this.inlineData;
11294     }
11295 };
11296
11297 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11298      /**
11299     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11300     * without a remote query - used by combo/forms at present.
11301     */
11302     
11303     /**
11304     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11305     */
11306     /**
11307     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11308     */
11309     /**
11310     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11311     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11312     */
11313     /**
11314     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11315     * on any HTTP request
11316     */
11317     /**
11318     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11319     */
11320     /**
11321     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11322     */
11323     multiSort: false,
11324     /**
11325     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11326     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11327     */
11328     remoteSort : false,
11329
11330     /**
11331     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11332      * loaded or when a record is removed. (defaults to false).
11333     */
11334     pruneModifiedRecords : false,
11335
11336     // private
11337     lastOptions : null,
11338
11339     /**
11340      * Add Records to the Store and fires the add event.
11341      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11342      */
11343     add : function(records){
11344         records = [].concat(records);
11345         for(var i = 0, len = records.length; i < len; i++){
11346             records[i].join(this);
11347         }
11348         var index = this.data.length;
11349         this.data.addAll(records);
11350         this.fireEvent("add", this, records, index);
11351     },
11352
11353     /**
11354      * Remove a Record from the Store and fires the remove event.
11355      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11356      */
11357     remove : function(record){
11358         var index = this.data.indexOf(record);
11359         this.data.removeAt(index);
11360  
11361         if(this.pruneModifiedRecords){
11362             this.modified.remove(record);
11363         }
11364         this.fireEvent("remove", this, record, index);
11365     },
11366
11367     /**
11368      * Remove all Records from the Store and fires the clear event.
11369      */
11370     removeAll : function(){
11371         this.data.clear();
11372         if(this.pruneModifiedRecords){
11373             this.modified = [];
11374         }
11375         this.fireEvent("clear", this);
11376     },
11377
11378     /**
11379      * Inserts Records to the Store at the given index and fires the add event.
11380      * @param {Number} index The start index at which to insert the passed Records.
11381      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11382      */
11383     insert : function(index, records){
11384         records = [].concat(records);
11385         for(var i = 0, len = records.length; i < len; i++){
11386             this.data.insert(index, records[i]);
11387             records[i].join(this);
11388         }
11389         this.fireEvent("add", this, records, index);
11390     },
11391
11392     /**
11393      * Get the index within the cache of the passed Record.
11394      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11395      * @return {Number} The index of the passed Record. Returns -1 if not found.
11396      */
11397     indexOf : function(record){
11398         return this.data.indexOf(record);
11399     },
11400
11401     /**
11402      * Get the index within the cache of the Record with the passed id.
11403      * @param {String} id The id of the Record to find.
11404      * @return {Number} The index of the Record. Returns -1 if not found.
11405      */
11406     indexOfId : function(id){
11407         return this.data.indexOfKey(id);
11408     },
11409
11410     /**
11411      * Get the Record with the specified id.
11412      * @param {String} id The id of the Record to find.
11413      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11414      */
11415     getById : function(id){
11416         return this.data.key(id);
11417     },
11418
11419     /**
11420      * Get the Record at the specified index.
11421      * @param {Number} index The index of the Record to find.
11422      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11423      */
11424     getAt : function(index){
11425         return this.data.itemAt(index);
11426     },
11427
11428     /**
11429      * Returns a range of Records between specified indices.
11430      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11431      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11432      * @return {Roo.data.Record[]} An array of Records
11433      */
11434     getRange : function(start, end){
11435         return this.data.getRange(start, end);
11436     },
11437
11438     // private
11439     storeOptions : function(o){
11440         o = Roo.apply({}, o);
11441         delete o.callback;
11442         delete o.scope;
11443         this.lastOptions = o;
11444     },
11445
11446     /**
11447      * Loads the Record cache from the configured Proxy using the configured Reader.
11448      * <p>
11449      * If using remote paging, then the first load call must specify the <em>start</em>
11450      * and <em>limit</em> properties in the options.params property to establish the initial
11451      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11452      * <p>
11453      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11454      * and this call will return before the new data has been loaded. Perform any post-processing
11455      * in a callback function, or in a "load" event handler.</strong>
11456      * <p>
11457      * @param {Object} options An object containing properties which control loading options:<ul>
11458      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11459      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11460      * passed the following arguments:<ul>
11461      * <li>r : Roo.data.Record[]</li>
11462      * <li>options: Options object from the load call</li>
11463      * <li>success: Boolean success indicator</li></ul></li>
11464      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11465      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11466      * </ul>
11467      */
11468     load : function(options){
11469         options = options || {};
11470         if(this.fireEvent("beforeload", this, options) !== false){
11471             this.storeOptions(options);
11472             var p = Roo.apply(options.params || {}, this.baseParams);
11473             // if meta was not loaded from remote source.. try requesting it.
11474             if (!this.reader.metaFromRemote) {
11475                 p._requestMeta = 1;
11476             }
11477             if(this.sortInfo && this.remoteSort){
11478                 var pn = this.paramNames;
11479                 p[pn["sort"]] = this.sortInfo.field;
11480                 p[pn["dir"]] = this.sortInfo.direction;
11481             }
11482             if (this.multiSort) {
11483                 var pn = this.paramNames;
11484                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11485             }
11486             
11487             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11488         }
11489     },
11490
11491     /**
11492      * Reloads the Record cache from the configured Proxy using the configured Reader and
11493      * the options from the last load operation performed.
11494      * @param {Object} options (optional) An object containing properties which may override the options
11495      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11496      * the most recently used options are reused).
11497      */
11498     reload : function(options){
11499         this.load(Roo.applyIf(options||{}, this.lastOptions));
11500     },
11501
11502     // private
11503     // Called as a callback by the Reader during a load operation.
11504     loadRecords : function(o, options, success){
11505         if(!o || success === false){
11506             if(success !== false){
11507                 this.fireEvent("load", this, [], options, o);
11508             }
11509             if(options.callback){
11510                 options.callback.call(options.scope || this, [], options, false);
11511             }
11512             return;
11513         }
11514         // if data returned failure - throw an exception.
11515         if (o.success === false) {
11516             // show a message if no listener is registered.
11517             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11518                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11519             }
11520             // loadmask wil be hooked into this..
11521             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11522             return;
11523         }
11524         var r = o.records, t = o.totalRecords || r.length;
11525         
11526         this.fireEvent("beforeloadadd", this, r, options, o);
11527         
11528         if(!options || options.add !== true){
11529             if(this.pruneModifiedRecords){
11530                 this.modified = [];
11531             }
11532             for(var i = 0, len = r.length; i < len; i++){
11533                 r[i].join(this);
11534             }
11535             if(this.snapshot){
11536                 this.data = this.snapshot;
11537                 delete this.snapshot;
11538             }
11539             this.data.clear();
11540             this.data.addAll(r);
11541             this.totalLength = t;
11542             this.applySort();
11543             this.fireEvent("datachanged", this);
11544         }else{
11545             this.totalLength = Math.max(t, this.data.length+r.length);
11546             this.add(r);
11547         }
11548         
11549         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11550                 
11551             var e = new Roo.data.Record({});
11552
11553             e.set(this.parent.displayField, this.parent.emptyTitle);
11554             e.set(this.parent.valueField, '');
11555
11556             this.insert(0, e);
11557         }
11558             
11559         this.fireEvent("load", this, r, options, o);
11560         if(options.callback){
11561             options.callback.call(options.scope || this, r, options, true);
11562         }
11563     },
11564
11565
11566     /**
11567      * Loads data from a passed data block. A Reader which understands the format of the data
11568      * must have been configured in the constructor.
11569      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11570      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11571      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11572      */
11573     loadData : function(o, append){
11574         var r = this.reader.readRecords(o);
11575         this.loadRecords(r, {add: append}, true);
11576     },
11577
11578     /**
11579      * Gets the number of cached records.
11580      * <p>
11581      * <em>If using paging, this may not be the total size of the dataset. If the data object
11582      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11583      * the data set size</em>
11584      */
11585     getCount : function(){
11586         return this.data.length || 0;
11587     },
11588
11589     /**
11590      * Gets the total number of records in the dataset as returned by the server.
11591      * <p>
11592      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11593      * the dataset size</em>
11594      */
11595     getTotalCount : function(){
11596         return this.totalLength || 0;
11597     },
11598
11599     /**
11600      * Returns the sort state of the Store as an object with two properties:
11601      * <pre><code>
11602  field {String} The name of the field by which the Records are sorted
11603  direction {String} The sort order, "ASC" or "DESC"
11604      * </code></pre>
11605      */
11606     getSortState : function(){
11607         return this.sortInfo;
11608     },
11609
11610     // private
11611     applySort : function(){
11612         if(this.sortInfo && !this.remoteSort){
11613             var s = this.sortInfo, f = s.field;
11614             var st = this.fields.get(f).sortType;
11615             var fn = function(r1, r2){
11616                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11617                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11618             };
11619             this.data.sort(s.direction, fn);
11620             if(this.snapshot && this.snapshot != this.data){
11621                 this.snapshot.sort(s.direction, fn);
11622             }
11623         }
11624     },
11625
11626     /**
11627      * Sets the default sort column and order to be used by the next load operation.
11628      * @param {String} fieldName The name of the field to sort by.
11629      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11630      */
11631     setDefaultSort : function(field, dir){
11632         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11633     },
11634
11635     /**
11636      * Sort the Records.
11637      * If remote sorting is used, the sort is performed on the server, and the cache is
11638      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
11643         var f = this.fields.get(fieldName);
11644         if(!dir){
11645             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11646             
11647             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11648                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11649             }else{
11650                 dir = f.sortDir;
11651             }
11652         }
11653         this.sortToggle[f.name] = dir;
11654         this.sortInfo = {field: f.name, direction: dir};
11655         if(!this.remoteSort){
11656             this.applySort();
11657             this.fireEvent("datachanged", this);
11658         }else{
11659             this.load(this.lastOptions);
11660         }
11661     },
11662
11663     /**
11664      * Calls the specified function for each of the Records in the cache.
11665      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11666      * Returning <em>false</em> aborts and exits the iteration.
11667      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11668      */
11669     each : function(fn, scope){
11670         this.data.each(fn, scope);
11671     },
11672
11673     /**
11674      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11675      * (e.g., during paging).
11676      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11677      */
11678     getModifiedRecords : function(){
11679         return this.modified;
11680     },
11681
11682     // private
11683     createFilterFn : function(property, value, anyMatch){
11684         if(!value.exec){ // not a regex
11685             value = String(value);
11686             if(value.length == 0){
11687                 return false;
11688             }
11689             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11690         }
11691         return function(r){
11692             return value.test(r.data[property]);
11693         };
11694     },
11695
11696     /**
11697      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11698      * @param {String} property A field on your records
11699      * @param {Number} start The record index to start at (defaults to 0)
11700      * @param {Number} end The last record index to include (defaults to length - 1)
11701      * @return {Number} The sum
11702      */
11703     sum : function(property, start, end){
11704         var rs = this.data.items, v = 0;
11705         start = start || 0;
11706         end = (end || end === 0) ? end : rs.length-1;
11707
11708         for(var i = start; i <= end; i++){
11709             v += (rs[i].data[property] || 0);
11710         }
11711         return v;
11712     },
11713
11714     /**
11715      * Filter the records by a specified property.
11716      * @param {String} field A field on your records
11717      * @param {String/RegExp} value Either a string that the field
11718      * should start with or a RegExp to test against the field
11719      * @param {Boolean} anyMatch True to match any part not just the beginning
11720      */
11721     filter : function(property, value, anyMatch){
11722         var fn = this.createFilterFn(property, value, anyMatch);
11723         return fn ? this.filterBy(fn) : this.clearFilter();
11724     },
11725
11726     /**
11727      * Filter by a function. The specified function will be called with each
11728      * record in this data source. If the function returns true the record is included,
11729      * otherwise it is filtered.
11730      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11731      * @param {Object} scope (optional) The scope of the function (defaults to this)
11732      */
11733     filterBy : function(fn, scope){
11734         this.snapshot = this.snapshot || this.data;
11735         this.data = this.queryBy(fn, scope||this);
11736         this.fireEvent("datachanged", this);
11737     },
11738
11739     /**
11740      * Query the records by a specified property.
11741      * @param {String} field A field on your records
11742      * @param {String/RegExp} value Either a string that the field
11743      * should start with or a RegExp to test against the field
11744      * @param {Boolean} anyMatch True to match any part not just the beginning
11745      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11746      */
11747     query : function(property, value, anyMatch){
11748         var fn = this.createFilterFn(property, value, anyMatch);
11749         return fn ? this.queryBy(fn) : this.data.clone();
11750     },
11751
11752     /**
11753      * Query by a function. The specified function will be called with each
11754      * record in this data source. If the function returns true the record is included
11755      * in the results.
11756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11757      * @param {Object} scope (optional) The scope of the function (defaults to this)
11758       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11759      **/
11760     queryBy : function(fn, scope){
11761         var data = this.snapshot || this.data;
11762         return data.filterBy(fn, scope||this);
11763     },
11764
11765     /**
11766      * Collects unique values for a particular dataIndex from this store.
11767      * @param {String} dataIndex The property to collect
11768      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11769      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11770      * @return {Array} An array of the unique values
11771      **/
11772     collect : function(dataIndex, allowNull, bypassFilter){
11773         var d = (bypassFilter === true && this.snapshot) ?
11774                 this.snapshot.items : this.data.items;
11775         var v, sv, r = [], l = {};
11776         for(var i = 0, len = d.length; i < len; i++){
11777             v = d[i].data[dataIndex];
11778             sv = String(v);
11779             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11780                 l[sv] = true;
11781                 r[r.length] = v;
11782             }
11783         }
11784         return r;
11785     },
11786
11787     /**
11788      * Revert to a view of the Record cache with no filtering applied.
11789      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11790      */
11791     clearFilter : function(suppressEvent){
11792         if(this.snapshot && this.snapshot != this.data){
11793             this.data = this.snapshot;
11794             delete this.snapshot;
11795             if(suppressEvent !== true){
11796                 this.fireEvent("datachanged", this);
11797             }
11798         }
11799     },
11800
11801     // private
11802     afterEdit : function(record){
11803         if(this.modified.indexOf(record) == -1){
11804             this.modified.push(record);
11805         }
11806         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11807     },
11808     
11809     // private
11810     afterReject : function(record){
11811         this.modified.remove(record);
11812         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11813     },
11814
11815     // private
11816     afterCommit : function(record){
11817         this.modified.remove(record);
11818         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11819     },
11820
11821     /**
11822      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11823      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11824      */
11825     commitChanges : function(){
11826         var m = this.modified.slice(0);
11827         this.modified = [];
11828         for(var i = 0, len = m.length; i < len; i++){
11829             m[i].commit();
11830         }
11831     },
11832
11833     /**
11834      * Cancel outstanding changes on all changed records.
11835      */
11836     rejectChanges : 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].reject();
11841         }
11842     },
11843
11844     onMetaChange : function(meta, rtype, o){
11845         this.recordType = rtype;
11846         this.fields = rtype.prototype.fields;
11847         delete this.snapshot;
11848         this.sortInfo = meta.sortInfo || this.sortInfo;
11849         this.modified = [];
11850         this.fireEvent('metachange', this, this.reader.meta);
11851     },
11852     
11853     moveIndex : function(data, type)
11854     {
11855         var index = this.indexOf(data);
11856         
11857         var newIndex = index + type;
11858         
11859         this.remove(data);
11860         
11861         this.insert(newIndex, data);
11862         
11863     }
11864 });/*
11865  * Based on:
11866  * Ext JS Library 1.1.1
11867  * Copyright(c) 2006-2007, Ext JS, LLC.
11868  *
11869  * Originally Released Under LGPL - original licence link has changed is not relivant.
11870  *
11871  * Fork - LGPL
11872  * <script type="text/javascript">
11873  */
11874
11875 /**
11876  * @class Roo.data.SimpleStore
11877  * @extends Roo.data.Store
11878  * Small helper class to make creating Stores from Array data easier.
11879  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11880  * @cfg {Array} fields An array of field definition objects, or field name strings.
11881  * @cfg {Array} data The multi-dimensional array of data
11882  * @constructor
11883  * @param {Object} config
11884  */
11885 Roo.data.SimpleStore = function(config){
11886     Roo.data.SimpleStore.superclass.constructor.call(this, {
11887         isLocal : true,
11888         reader: new Roo.data.ArrayReader({
11889                 id: config.id
11890             },
11891             Roo.data.Record.create(config.fields)
11892         ),
11893         proxy : new Roo.data.MemoryProxy(config.data)
11894     });
11895     this.load();
11896 };
11897 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11898  * Based on:
11899  * Ext JS Library 1.1.1
11900  * Copyright(c) 2006-2007, Ext JS, LLC.
11901  *
11902  * Originally Released Under LGPL - original licence link has changed is not relivant.
11903  *
11904  * Fork - LGPL
11905  * <script type="text/javascript">
11906  */
11907
11908 /**
11909 /**
11910  * @extends Roo.data.Store
11911  * @class Roo.data.JsonStore
11912  * Small helper class to make creating Stores for JSON data easier. <br/>
11913 <pre><code>
11914 var store = new Roo.data.JsonStore({
11915     url: 'get-images.php',
11916     root: 'images',
11917     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11918 });
11919 </code></pre>
11920  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11921  * JsonReader and HttpProxy (unless inline data is provided).</b>
11922  * @cfg {Array} fields An array of field definition objects, or field name strings.
11923  * @constructor
11924  * @param {Object} config
11925  */
11926 Roo.data.JsonStore = function(c){
11927     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11928         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11929         reader: new Roo.data.JsonReader(c, c.fields)
11930     }));
11931 };
11932 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11933  * Based on:
11934  * Ext JS Library 1.1.1
11935  * Copyright(c) 2006-2007, Ext JS, LLC.
11936  *
11937  * Originally Released Under LGPL - original licence link has changed is not relivant.
11938  *
11939  * Fork - LGPL
11940  * <script type="text/javascript">
11941  */
11942
11943  
11944 Roo.data.Field = function(config){
11945     if(typeof config == "string"){
11946         config = {name: config};
11947     }
11948     Roo.apply(this, config);
11949     
11950     if(!this.type){
11951         this.type = "auto";
11952     }
11953     
11954     var st = Roo.data.SortTypes;
11955     // named sortTypes are supported, here we look them up
11956     if(typeof this.sortType == "string"){
11957         this.sortType = st[this.sortType];
11958     }
11959     
11960     // set default sortType for strings and dates
11961     if(!this.sortType){
11962         switch(this.type){
11963             case "string":
11964                 this.sortType = st.asUCString;
11965                 break;
11966             case "date":
11967                 this.sortType = st.asDate;
11968                 break;
11969             default:
11970                 this.sortType = st.none;
11971         }
11972     }
11973
11974     // define once
11975     var stripRe = /[\$,%]/g;
11976
11977     // prebuilt conversion function for this field, instead of
11978     // switching every time we're reading a value
11979     if(!this.convert){
11980         var cv, dateFormat = this.dateFormat;
11981         switch(this.type){
11982             case "":
11983             case "auto":
11984             case undefined:
11985                 cv = function(v){ return v; };
11986                 break;
11987             case "string":
11988                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11989                 break;
11990             case "int":
11991                 cv = function(v){
11992                     return v !== undefined && v !== null && v !== '' ?
11993                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11994                     };
11995                 break;
11996             case "float":
11997                 cv = function(v){
11998                     return v !== undefined && v !== null && v !== '' ?
11999                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12000                     };
12001                 break;
12002             case "bool":
12003             case "boolean":
12004                 cv = function(v){ return v === true || v === "true" || v == 1; };
12005                 break;
12006             case "date":
12007                 cv = function(v){
12008                     if(!v){
12009                         return '';
12010                     }
12011                     if(v instanceof Date){
12012                         return v;
12013                     }
12014                     if(dateFormat){
12015                         if(dateFormat == "timestamp"){
12016                             return new Date(v*1000);
12017                         }
12018                         return Date.parseDate(v, dateFormat);
12019                     }
12020                     var parsed = Date.parse(v);
12021                     return parsed ? new Date(parsed) : null;
12022                 };
12023              break;
12024             
12025         }
12026         this.convert = cv;
12027     }
12028 };
12029
12030 Roo.data.Field.prototype = {
12031     dateFormat: null,
12032     defaultValue: "",
12033     mapping: null,
12034     sortType : null,
12035     sortDir : "ASC"
12036 };/*
12037  * Based on:
12038  * Ext JS Library 1.1.1
12039  * Copyright(c) 2006-2007, Ext JS, LLC.
12040  *
12041  * Originally Released Under LGPL - original licence link has changed is not relivant.
12042  *
12043  * Fork - LGPL
12044  * <script type="text/javascript">
12045  */
12046  
12047 // Base class for reading structured data from a data source.  This class is intended to be
12048 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12049
12050 /**
12051  * @class Roo.data.DataReader
12052  * Base class for reading structured data from a data source.  This class is intended to be
12053  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12054  */
12055
12056 Roo.data.DataReader = function(meta, recordType){
12057     
12058     this.meta = meta;
12059     
12060     this.recordType = recordType instanceof Array ? 
12061         Roo.data.Record.create(recordType) : recordType;
12062 };
12063
12064 Roo.data.DataReader.prototype = {
12065      /**
12066      * Create an empty record
12067      * @param {Object} data (optional) - overlay some values
12068      * @return {Roo.data.Record} record created.
12069      */
12070     newRow :  function(d) {
12071         var da =  {};
12072         this.recordType.prototype.fields.each(function(c) {
12073             switch( c.type) {
12074                 case 'int' : da[c.name] = 0; break;
12075                 case 'date' : da[c.name] = new Date(); break;
12076                 case 'float' : da[c.name] = 0.0; break;
12077                 case 'boolean' : da[c.name] = false; break;
12078                 default : da[c.name] = ""; break;
12079             }
12080             
12081         });
12082         return new this.recordType(Roo.apply(da, d));
12083     }
12084     
12085 };/*
12086  * Based on:
12087  * Ext JS Library 1.1.1
12088  * Copyright(c) 2006-2007, Ext JS, LLC.
12089  *
12090  * Originally Released Under LGPL - original licence link has changed is not relivant.
12091  *
12092  * Fork - LGPL
12093  * <script type="text/javascript">
12094  */
12095
12096 /**
12097  * @class Roo.data.DataProxy
12098  * @extends Roo.data.Observable
12099  * This class is an abstract base class for implementations which provide retrieval of
12100  * unformatted data objects.<br>
12101  * <p>
12102  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12103  * (of the appropriate type which knows how to parse the data object) to provide a block of
12104  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12105  * <p>
12106  * Custom implementations must implement the load method as described in
12107  * {@link Roo.data.HttpProxy#load}.
12108  */
12109 Roo.data.DataProxy = function(){
12110     this.addEvents({
12111         /**
12112          * @event beforeload
12113          * Fires before a network request is made to retrieve a data object.
12114          * @param {Object} This DataProxy object.
12115          * @param {Object} params The params parameter to the load function.
12116          */
12117         beforeload : true,
12118         /**
12119          * @event load
12120          * Fires before the load method's callback is called.
12121          * @param {Object} This DataProxy object.
12122          * @param {Object} o The data object.
12123          * @param {Object} arg The callback argument object passed to the load function.
12124          */
12125         load : true,
12126         /**
12127          * @event loadexception
12128          * Fires if an Exception occurs during data retrieval.
12129          * @param {Object} This DataProxy object.
12130          * @param {Object} o The data object.
12131          * @param {Object} arg The callback argument object passed to the load function.
12132          * @param {Object} e The Exception.
12133          */
12134         loadexception : true
12135     });
12136     Roo.data.DataProxy.superclass.constructor.call(this);
12137 };
12138
12139 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12140
12141     /**
12142      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12143      */
12144 /*
12145  * Based on:
12146  * Ext JS Library 1.1.1
12147  * Copyright(c) 2006-2007, Ext JS, LLC.
12148  *
12149  * Originally Released Under LGPL - original licence link has changed is not relivant.
12150  *
12151  * Fork - LGPL
12152  * <script type="text/javascript">
12153  */
12154 /**
12155  * @class Roo.data.MemoryProxy
12156  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12157  * to the Reader when its load method is called.
12158  * @constructor
12159  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12160  */
12161 Roo.data.MemoryProxy = function(data){
12162     if (data.data) {
12163         data = data.data;
12164     }
12165     Roo.data.MemoryProxy.superclass.constructor.call(this);
12166     this.data = data;
12167 };
12168
12169 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12170     
12171     /**
12172      * Load data from the requested source (in this case an in-memory
12173      * data object passed to the constructor), read the data object into
12174      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12175      * process that block using the passed callback.
12176      * @param {Object} params This parameter is not used by the MemoryProxy class.
12177      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12178      * object into a block of Roo.data.Records.
12179      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12180      * The function must be passed <ul>
12181      * <li>The Record block object</li>
12182      * <li>The "arg" argument from the load function</li>
12183      * <li>A boolean success indicator</li>
12184      * </ul>
12185      * @param {Object} scope The scope in which to call the callback
12186      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12187      */
12188     load : function(params, reader, callback, scope, arg){
12189         params = params || {};
12190         var result;
12191         try {
12192             result = reader.readRecords(this.data);
12193         }catch(e){
12194             this.fireEvent("loadexception", this, arg, null, e);
12195             callback.call(scope, null, arg, false);
12196             return;
12197         }
12198         callback.call(scope, result, arg, true);
12199     },
12200     
12201     // private
12202     update : function(params, records){
12203         
12204     }
12205 });/*
12206  * Based on:
12207  * Ext JS Library 1.1.1
12208  * Copyright(c) 2006-2007, Ext JS, LLC.
12209  *
12210  * Originally Released Under LGPL - original licence link has changed is not relivant.
12211  *
12212  * Fork - LGPL
12213  * <script type="text/javascript">
12214  */
12215 /**
12216  * @class Roo.data.HttpProxy
12217  * @extends Roo.data.DataProxy
12218  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12219  * configured to reference a certain URL.<br><br>
12220  * <p>
12221  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12222  * from which the running page was served.<br><br>
12223  * <p>
12224  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12225  * <p>
12226  * Be aware that to enable the browser to parse an XML document, the server must set
12227  * the Content-Type header in the HTTP response to "text/xml".
12228  * @constructor
12229  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12230  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12231  * will be used to make the request.
12232  */
12233 Roo.data.HttpProxy = function(conn){
12234     Roo.data.HttpProxy.superclass.constructor.call(this);
12235     // is conn a conn config or a real conn?
12236     this.conn = conn;
12237     this.useAjax = !conn || !conn.events;
12238   
12239 };
12240
12241 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12242     // thse are take from connection...
12243     
12244     /**
12245      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12246      */
12247     /**
12248      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12249      * extra parameters to each request made by this object. (defaults to undefined)
12250      */
12251     /**
12252      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12253      *  to each request made by this object. (defaults to undefined)
12254      */
12255     /**
12256      * @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)
12257      */
12258     /**
12259      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12260      */
12261      /**
12262      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12263      * @type Boolean
12264      */
12265   
12266
12267     /**
12268      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12269      * @type Boolean
12270      */
12271     /**
12272      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12273      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12274      * a finer-grained basis than the DataProxy events.
12275      */
12276     getConnection : function(){
12277         return this.useAjax ? Roo.Ajax : this.conn;
12278     },
12279
12280     /**
12281      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12282      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12283      * process that block using the passed callback.
12284      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12285      * for the request to the remote server.
12286      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12287      * object into a block of Roo.data.Records.
12288      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12289      * The function must be passed <ul>
12290      * <li>The Record block object</li>
12291      * <li>The "arg" argument from the load function</li>
12292      * <li>A boolean success indicator</li>
12293      * </ul>
12294      * @param {Object} scope The scope in which to call the callback
12295      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12296      */
12297     load : function(params, reader, callback, scope, arg){
12298         if(this.fireEvent("beforeload", this, params) !== false){
12299             var  o = {
12300                 params : params || {},
12301                 request: {
12302                     callback : callback,
12303                     scope : scope,
12304                     arg : arg
12305                 },
12306                 reader: reader,
12307                 callback : this.loadResponse,
12308                 scope: this
12309             };
12310             if(this.useAjax){
12311                 Roo.applyIf(o, this.conn);
12312                 if(this.activeRequest){
12313                     Roo.Ajax.abort(this.activeRequest);
12314                 }
12315                 this.activeRequest = Roo.Ajax.request(o);
12316             }else{
12317                 this.conn.request(o);
12318             }
12319         }else{
12320             callback.call(scope||this, null, arg, false);
12321         }
12322     },
12323
12324     // private
12325     loadResponse : function(o, success, response){
12326         delete this.activeRequest;
12327         if(!success){
12328             this.fireEvent("loadexception", this, o, response);
12329             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12330             return;
12331         }
12332         var result;
12333         try {
12334             result = o.reader.read(response);
12335         }catch(e){
12336             this.fireEvent("loadexception", this, o, response, e);
12337             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12338             return;
12339         }
12340         
12341         this.fireEvent("load", this, o, o.request.arg);
12342         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12343     },
12344
12345     // private
12346     update : function(dataSet){
12347
12348     },
12349
12350     // private
12351     updateResponse : function(dataSet){
12352
12353     }
12354 });/*
12355  * Based on:
12356  * Ext JS Library 1.1.1
12357  * Copyright(c) 2006-2007, Ext JS, LLC.
12358  *
12359  * Originally Released Under LGPL - original licence link has changed is not relivant.
12360  *
12361  * Fork - LGPL
12362  * <script type="text/javascript">
12363  */
12364
12365 /**
12366  * @class Roo.data.ScriptTagProxy
12367  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12368  * other than the originating domain of the running page.<br><br>
12369  * <p>
12370  * <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
12371  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12372  * <p>
12373  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12374  * source code that is used as the source inside a &lt;script> tag.<br><br>
12375  * <p>
12376  * In order for the browser to process the returned data, the server must wrap the data object
12377  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12378  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12379  * depending on whether the callback name was passed:
12380  * <p>
12381  * <pre><code>
12382 boolean scriptTag = false;
12383 String cb = request.getParameter("callback");
12384 if (cb != null) {
12385     scriptTag = true;
12386     response.setContentType("text/javascript");
12387 } else {
12388     response.setContentType("application/x-json");
12389 }
12390 Writer out = response.getWriter();
12391 if (scriptTag) {
12392     out.write(cb + "(");
12393 }
12394 out.print(dataBlock.toJsonString());
12395 if (scriptTag) {
12396     out.write(");");
12397 }
12398 </pre></code>
12399  *
12400  * @constructor
12401  * @param {Object} config A configuration object.
12402  */
12403 Roo.data.ScriptTagProxy = function(config){
12404     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12405     Roo.apply(this, config);
12406     this.head = document.getElementsByTagName("head")[0];
12407 };
12408
12409 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12410
12411 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12412     /**
12413      * @cfg {String} url The URL from which to request the data object.
12414      */
12415     /**
12416      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12417      */
12418     timeout : 30000,
12419     /**
12420      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12421      * the server the name of the callback function set up by the load call to process the returned data object.
12422      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12423      * javascript output which calls this named function passing the data object as its only parameter.
12424      */
12425     callbackParam : "callback",
12426     /**
12427      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12428      * name to the request.
12429      */
12430     nocache : true,
12431
12432     /**
12433      * Load data from the configured URL, read the data object into
12434      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12435      * process that block using the passed callback.
12436      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12437      * for the request to the remote server.
12438      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12439      * object into a block of Roo.data.Records.
12440      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12441      * The function must be passed <ul>
12442      * <li>The Record block object</li>
12443      * <li>The "arg" argument from the load function</li>
12444      * <li>A boolean success indicator</li>
12445      * </ul>
12446      * @param {Object} scope The scope in which to call the callback
12447      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12448      */
12449     load : function(params, reader, callback, scope, arg){
12450         if(this.fireEvent("beforeload", this, params) !== false){
12451
12452             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12453
12454             var url = this.url;
12455             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12456             if(this.nocache){
12457                 url += "&_dc=" + (new Date().getTime());
12458             }
12459             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12460             var trans = {
12461                 id : transId,
12462                 cb : "stcCallback"+transId,
12463                 scriptId : "stcScript"+transId,
12464                 params : params,
12465                 arg : arg,
12466                 url : url,
12467                 callback : callback,
12468                 scope : scope,
12469                 reader : reader
12470             };
12471             var conn = this;
12472
12473             window[trans.cb] = function(o){
12474                 conn.handleResponse(o, trans);
12475             };
12476
12477             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12478
12479             if(this.autoAbort !== false){
12480                 this.abort();
12481             }
12482
12483             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12484
12485             var script = document.createElement("script");
12486             script.setAttribute("src", url);
12487             script.setAttribute("type", "text/javascript");
12488             script.setAttribute("id", trans.scriptId);
12489             this.head.appendChild(script);
12490
12491             this.trans = trans;
12492         }else{
12493             callback.call(scope||this, null, arg, false);
12494         }
12495     },
12496
12497     // private
12498     isLoading : function(){
12499         return this.trans ? true : false;
12500     },
12501
12502     /**
12503      * Abort the current server request.
12504      */
12505     abort : function(){
12506         if(this.isLoading()){
12507             this.destroyTrans(this.trans);
12508         }
12509     },
12510
12511     // private
12512     destroyTrans : function(trans, isLoaded){
12513         this.head.removeChild(document.getElementById(trans.scriptId));
12514         clearTimeout(trans.timeoutId);
12515         if(isLoaded){
12516             window[trans.cb] = undefined;
12517             try{
12518                 delete window[trans.cb];
12519             }catch(e){}
12520         }else{
12521             // if hasn't been loaded, wait for load to remove it to prevent script error
12522             window[trans.cb] = function(){
12523                 window[trans.cb] = undefined;
12524                 try{
12525                     delete window[trans.cb];
12526                 }catch(e){}
12527             };
12528         }
12529     },
12530
12531     // private
12532     handleResponse : function(o, trans){
12533         this.trans = false;
12534         this.destroyTrans(trans, true);
12535         var result;
12536         try {
12537             result = trans.reader.readRecords(o);
12538         }catch(e){
12539             this.fireEvent("loadexception", this, o, trans.arg, e);
12540             trans.callback.call(trans.scope||window, null, trans.arg, false);
12541             return;
12542         }
12543         this.fireEvent("load", this, o, trans.arg);
12544         trans.callback.call(trans.scope||window, result, trans.arg, true);
12545     },
12546
12547     // private
12548     handleFailure : function(trans){
12549         this.trans = false;
12550         this.destroyTrans(trans, false);
12551         this.fireEvent("loadexception", this, null, trans.arg);
12552         trans.callback.call(trans.scope||window, null, trans.arg, false);
12553     }
12554 });/*
12555  * Based on:
12556  * Ext JS Library 1.1.1
12557  * Copyright(c) 2006-2007, Ext JS, LLC.
12558  *
12559  * Originally Released Under LGPL - original licence link has changed is not relivant.
12560  *
12561  * Fork - LGPL
12562  * <script type="text/javascript">
12563  */
12564
12565 /**
12566  * @class Roo.data.JsonReader
12567  * @extends Roo.data.DataReader
12568  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12569  * based on mappings in a provided Roo.data.Record constructor.
12570  * 
12571  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12572  * in the reply previously. 
12573  * 
12574  * <p>
12575  * Example code:
12576  * <pre><code>
12577 var RecordDef = Roo.data.Record.create([
12578     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12579     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12580 ]);
12581 var myReader = new Roo.data.JsonReader({
12582     totalProperty: "results",    // The property which contains the total dataset size (optional)
12583     root: "rows",                // The property which contains an Array of row objects
12584     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12585 }, RecordDef);
12586 </code></pre>
12587  * <p>
12588  * This would consume a JSON file like this:
12589  * <pre><code>
12590 { 'results': 2, 'rows': [
12591     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12592     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12593 }
12594 </code></pre>
12595  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12596  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12597  * paged from the remote server.
12598  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12599  * @cfg {String} root name of the property which contains the Array of row objects.
12600  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12601  * @cfg {Array} fields Array of field definition objects
12602  * @constructor
12603  * Create a new JsonReader
12604  * @param {Object} meta Metadata configuration options
12605  * @param {Object} recordType Either an Array of field definition objects,
12606  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12607  */
12608 Roo.data.JsonReader = function(meta, recordType){
12609     
12610     meta = meta || {};
12611     // set some defaults:
12612     Roo.applyIf(meta, {
12613         totalProperty: 'total',
12614         successProperty : 'success',
12615         root : 'data',
12616         id : 'id'
12617     });
12618     
12619     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12620 };
12621 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12622     
12623     /**
12624      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12625      * Used by Store query builder to append _requestMeta to params.
12626      * 
12627      */
12628     metaFromRemote : false,
12629     /**
12630      * This method is only used by a DataProxy which has retrieved data from a remote server.
12631      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12632      * @return {Object} data A data block which is used by an Roo.data.Store object as
12633      * a cache of Roo.data.Records.
12634      */
12635     read : function(response){
12636         var json = response.responseText;
12637        
12638         var o = /* eval:var:o */ eval("("+json+")");
12639         if(!o) {
12640             throw {message: "JsonReader.read: Json object not found"};
12641         }
12642         
12643         if(o.metaData){
12644             
12645             delete this.ef;
12646             this.metaFromRemote = true;
12647             this.meta = o.metaData;
12648             this.recordType = Roo.data.Record.create(o.metaData.fields);
12649             this.onMetaChange(this.meta, this.recordType, o);
12650         }
12651         return this.readRecords(o);
12652     },
12653
12654     // private function a store will implement
12655     onMetaChange : function(meta, recordType, o){
12656
12657     },
12658
12659     /**
12660          * @ignore
12661          */
12662     simpleAccess: function(obj, subsc) {
12663         return obj[subsc];
12664     },
12665
12666         /**
12667          * @ignore
12668          */
12669     getJsonAccessor: function(){
12670         var re = /[\[\.]/;
12671         return function(expr) {
12672             try {
12673                 return(re.test(expr))
12674                     ? new Function("obj", "return obj." + expr)
12675                     : function(obj){
12676                         return obj[expr];
12677                     };
12678             } catch(e){}
12679             return Roo.emptyFn;
12680         };
12681     }(),
12682
12683     /**
12684      * Create a data block containing Roo.data.Records from an XML document.
12685      * @param {Object} o An object which contains an Array of row objects in the property specified
12686      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12687      * which contains the total size of the dataset.
12688      * @return {Object} data A data block which is used by an Roo.data.Store object as
12689      * a cache of Roo.data.Records.
12690      */
12691     readRecords : function(o){
12692         /**
12693          * After any data loads, the raw JSON data is available for further custom processing.
12694          * @type Object
12695          */
12696         this.o = o;
12697         var s = this.meta, Record = this.recordType,
12698             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12699
12700 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12701         if (!this.ef) {
12702             if(s.totalProperty) {
12703                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12704                 }
12705                 if(s.successProperty) {
12706                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12707                 }
12708                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12709                 if (s.id) {
12710                         var g = this.getJsonAccessor(s.id);
12711                         this.getId = function(rec) {
12712                                 var r = g(rec);  
12713                                 return (r === undefined || r === "") ? null : r;
12714                         };
12715                 } else {
12716                         this.getId = function(){return null;};
12717                 }
12718             this.ef = [];
12719             for(var jj = 0; jj < fl; jj++){
12720                 f = fi[jj];
12721                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12722                 this.ef[jj] = this.getJsonAccessor(map);
12723             }
12724         }
12725
12726         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12727         if(s.totalProperty){
12728             var vt = parseInt(this.getTotal(o), 10);
12729             if(!isNaN(vt)){
12730                 totalRecords = vt;
12731             }
12732         }
12733         if(s.successProperty){
12734             var vs = this.getSuccess(o);
12735             if(vs === false || vs === 'false'){
12736                 success = false;
12737             }
12738         }
12739         var records = [];
12740         for(var i = 0; i < c; i++){
12741                 var n = root[i];
12742             var values = {};
12743             var id = this.getId(n);
12744             for(var j = 0; j < fl; j++){
12745                 f = fi[j];
12746             var v = this.ef[j](n);
12747             if (!f.convert) {
12748                 Roo.log('missing convert for ' + f.name);
12749                 Roo.log(f);
12750                 continue;
12751             }
12752             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12753             }
12754             var record = new Record(values, id);
12755             record.json = n;
12756             records[i] = record;
12757         }
12758         return {
12759             raw : o,
12760             success : success,
12761             records : records,
12762             totalRecords : totalRecords
12763         };
12764     }
12765 });/*
12766  * Based on:
12767  * Ext JS Library 1.1.1
12768  * Copyright(c) 2006-2007, Ext JS, LLC.
12769  *
12770  * Originally Released Under LGPL - original licence link has changed is not relivant.
12771  *
12772  * Fork - LGPL
12773  * <script type="text/javascript">
12774  */
12775
12776 /**
12777  * @class Roo.data.ArrayReader
12778  * @extends Roo.data.DataReader
12779  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12780  * Each element of that Array represents a row of data fields. The
12781  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12782  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12783  * <p>
12784  * Example code:.
12785  * <pre><code>
12786 var RecordDef = Roo.data.Record.create([
12787     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12788     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12789 ]);
12790 var myReader = new Roo.data.ArrayReader({
12791     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12792 }, RecordDef);
12793 </code></pre>
12794  * <p>
12795  * This would consume an Array like this:
12796  * <pre><code>
12797 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12798   </code></pre>
12799  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12800  * @constructor
12801  * Create a new JsonReader
12802  * @param {Object} meta Metadata configuration options.
12803  * @param {Object} recordType Either an Array of field definition objects
12804  * as specified to {@link Roo.data.Record#create},
12805  * or an {@link Roo.data.Record} object
12806  * created using {@link Roo.data.Record#create}.
12807  */
12808 Roo.data.ArrayReader = function(meta, recordType){
12809     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12810 };
12811
12812 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12813     /**
12814      * Create a data block containing Roo.data.Records from an XML document.
12815      * @param {Object} o An Array of row objects which represents the dataset.
12816      * @return {Object} data A data block which is used by an Roo.data.Store object as
12817      * a cache of Roo.data.Records.
12818      */
12819     readRecords : function(o){
12820         var sid = this.meta ? this.meta.id : null;
12821         var recordType = this.recordType, fields = recordType.prototype.fields;
12822         var records = [];
12823         var root = o;
12824             for(var i = 0; i < root.length; i++){
12825                     var n = root[i];
12826                 var values = {};
12827                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12828                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12829                 var f = fields.items[j];
12830                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12831                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12832                 v = f.convert(v);
12833                 values[f.name] = v;
12834             }
12835                 var record = new recordType(values, id);
12836                 record.json = n;
12837                 records[records.length] = record;
12838             }
12839             return {
12840                 records : records,
12841                 totalRecords : records.length
12842             };
12843     }
12844 });/*
12845  * - LGPL
12846  * * 
12847  */
12848
12849 /**
12850  * @class Roo.bootstrap.ComboBox
12851  * @extends Roo.bootstrap.TriggerField
12852  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12853  * @cfg {Boolean} append (true|false) default false
12854  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12855  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12856  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12857  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12858  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12859  * @cfg {Boolean} animate default true
12860  * @cfg {Boolean} emptyResultText only for touch device
12861  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12862  * @cfg {String} emptyTitle default ''
12863  * @constructor
12864  * Create a new ComboBox.
12865  * @param {Object} config Configuration options
12866  */
12867 Roo.bootstrap.ComboBox = function(config){
12868     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12869     this.addEvents({
12870         /**
12871          * @event expand
12872          * Fires when the dropdown list is expanded
12873         * @param {Roo.bootstrap.ComboBox} combo This combo box
12874         */
12875         'expand' : true,
12876         /**
12877          * @event collapse
12878          * Fires when the dropdown list is collapsed
12879         * @param {Roo.bootstrap.ComboBox} combo This combo box
12880         */
12881         'collapse' : true,
12882         /**
12883          * @event beforeselect
12884          * Fires before a list item is selected. Return false to cancel the selection.
12885         * @param {Roo.bootstrap.ComboBox} combo This combo box
12886         * @param {Roo.data.Record} record The data record returned from the underlying store
12887         * @param {Number} index The index of the selected item in the dropdown list
12888         */
12889         'beforeselect' : true,
12890         /**
12891          * @event select
12892          * Fires when a list item is selected
12893         * @param {Roo.bootstrap.ComboBox} combo This combo box
12894         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12895         * @param {Number} index The index of the selected item in the dropdown list
12896         */
12897         'select' : true,
12898         /**
12899          * @event beforequery
12900          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12901          * The event object passed has these properties:
12902         * @param {Roo.bootstrap.ComboBox} combo This combo box
12903         * @param {String} query The query
12904         * @param {Boolean} forceAll true to force "all" query
12905         * @param {Boolean} cancel true to cancel the query
12906         * @param {Object} e The query event object
12907         */
12908         'beforequery': true,
12909          /**
12910          * @event add
12911          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12912         * @param {Roo.bootstrap.ComboBox} combo This combo box
12913         */
12914         'add' : true,
12915         /**
12916          * @event edit
12917          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12918         * @param {Roo.bootstrap.ComboBox} combo This combo box
12919         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12920         */
12921         'edit' : true,
12922         /**
12923          * @event remove
12924          * Fires when the remove value from the combobox array
12925         * @param {Roo.bootstrap.ComboBox} combo This combo box
12926         */
12927         'remove' : true,
12928         /**
12929          * @event afterremove
12930          * Fires when the remove value from the combobox array
12931         * @param {Roo.bootstrap.ComboBox} combo This combo box
12932         */
12933         'afterremove' : true,
12934         /**
12935          * @event specialfilter
12936          * Fires when specialfilter
12937             * @param {Roo.bootstrap.ComboBox} combo This combo box
12938             */
12939         'specialfilter' : true,
12940         /**
12941          * @event tick
12942          * Fires when tick the element
12943             * @param {Roo.bootstrap.ComboBox} combo This combo box
12944             */
12945         'tick' : true,
12946         /**
12947          * @event touchviewdisplay
12948          * Fires when touch view require special display (default is using displayField)
12949             * @param {Roo.bootstrap.ComboBox} combo This combo box
12950             * @param {Object} cfg set html .
12951             */
12952         'touchviewdisplay' : true
12953         
12954     });
12955     
12956     this.item = [];
12957     this.tickItems = [];
12958     
12959     this.selectedIndex = -1;
12960     if(this.mode == 'local'){
12961         if(config.queryDelay === undefined){
12962             this.queryDelay = 10;
12963         }
12964         if(config.minChars === undefined){
12965             this.minChars = 0;
12966         }
12967     }
12968 };
12969
12970 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12971      
12972     /**
12973      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12974      * rendering into an Roo.Editor, defaults to false)
12975      */
12976     /**
12977      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12978      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12979      */
12980     /**
12981      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12982      */
12983     /**
12984      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12985      * the dropdown list (defaults to undefined, with no header element)
12986      */
12987
12988      /**
12989      * @cfg {String/Roo.Template} tpl The template to use to render the output
12990      */
12991      
12992      /**
12993      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12994      */
12995     listWidth: undefined,
12996     /**
12997      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12998      * mode = 'remote' or 'text' if mode = 'local')
12999      */
13000     displayField: undefined,
13001     
13002     /**
13003      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13004      * mode = 'remote' or 'value' if mode = 'local'). 
13005      * Note: use of a valueField requires the user make a selection
13006      * in order for a value to be mapped.
13007      */
13008     valueField: undefined,
13009     /**
13010      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13011      */
13012     modalTitle : '',
13013     
13014     /**
13015      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13016      * field's data value (defaults to the underlying DOM element's name)
13017      */
13018     hiddenName: undefined,
13019     /**
13020      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13021      */
13022     listClass: '',
13023     /**
13024      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13025      */
13026     selectedClass: 'active',
13027     
13028     /**
13029      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13030      */
13031     shadow:'sides',
13032     /**
13033      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13034      * anchor positions (defaults to 'tl-bl')
13035      */
13036     listAlign: 'tl-bl?',
13037     /**
13038      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13039      */
13040     maxHeight: 300,
13041     /**
13042      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13043      * query specified by the allQuery config option (defaults to 'query')
13044      */
13045     triggerAction: 'query',
13046     /**
13047      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13048      * (defaults to 4, does not apply if editable = false)
13049      */
13050     minChars : 4,
13051     /**
13052      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13053      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13054      */
13055     typeAhead: false,
13056     /**
13057      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13058      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13059      */
13060     queryDelay: 500,
13061     /**
13062      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13063      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13064      */
13065     pageSize: 0,
13066     /**
13067      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13068      * when editable = true (defaults to false)
13069      */
13070     selectOnFocus:false,
13071     /**
13072      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13073      */
13074     queryParam: 'query',
13075     /**
13076      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13077      * when mode = 'remote' (defaults to 'Loading...')
13078      */
13079     loadingText: 'Loading...',
13080     /**
13081      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13082      */
13083     resizable: false,
13084     /**
13085      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13086      */
13087     handleHeight : 8,
13088     /**
13089      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13090      * traditional select (defaults to true)
13091      */
13092     editable: true,
13093     /**
13094      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13095      */
13096     allQuery: '',
13097     /**
13098      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13099      */
13100     mode: 'remote',
13101     /**
13102      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13103      * listWidth has a higher value)
13104      */
13105     minListWidth : 70,
13106     /**
13107      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13108      * allow the user to set arbitrary text into the field (defaults to false)
13109      */
13110     forceSelection:false,
13111     /**
13112      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13113      * if typeAhead = true (defaults to 250)
13114      */
13115     typeAheadDelay : 250,
13116     /**
13117      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13118      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13119      */
13120     valueNotFoundText : undefined,
13121     /**
13122      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13123      */
13124     blockFocus : false,
13125     
13126     /**
13127      * @cfg {Boolean} disableClear Disable showing of clear button.
13128      */
13129     disableClear : false,
13130     /**
13131      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13132      */
13133     alwaysQuery : false,
13134     
13135     /**
13136      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13137      */
13138     multiple : false,
13139     
13140     /**
13141      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13142      */
13143     invalidClass : "has-warning",
13144     
13145     /**
13146      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13147      */
13148     validClass : "has-success",
13149     
13150     /**
13151      * @cfg {Boolean} specialFilter (true|false) special filter default false
13152      */
13153     specialFilter : false,
13154     
13155     /**
13156      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13157      */
13158     mobileTouchView : true,
13159     
13160     /**
13161      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13162      */
13163     useNativeIOS : false,
13164     
13165     /**
13166      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13167      */
13168     mobile_restrict_height : false,
13169     
13170     ios_options : false,
13171     
13172     //private
13173     addicon : false,
13174     editicon: false,
13175     
13176     page: 0,
13177     hasQuery: false,
13178     append: false,
13179     loadNext: false,
13180     autoFocus : true,
13181     tickable : false,
13182     btnPosition : 'right',
13183     triggerList : true,
13184     showToggleBtn : true,
13185     animate : true,
13186     emptyResultText: 'Empty',
13187     triggerText : 'Select',
13188     emptyTitle : '',
13189     
13190     // element that contains real text value.. (when hidden is used..)
13191     
13192     getAutoCreate : function()
13193     {   
13194         var cfg = false;
13195         //render
13196         /*
13197          * Render classic select for iso
13198          */
13199         
13200         if(Roo.isIOS && this.useNativeIOS){
13201             cfg = this.getAutoCreateNativeIOS();
13202             return cfg;
13203         }
13204         
13205         /*
13206          * Touch Devices
13207          */
13208         
13209         if(Roo.isTouch && this.mobileTouchView){
13210             cfg = this.getAutoCreateTouchView();
13211             return cfg;;
13212         }
13213         
13214         /*
13215          *  Normal ComboBox
13216          */
13217         if(!this.tickable){
13218             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13219             return cfg;
13220         }
13221         
13222         /*
13223          *  ComboBox with tickable selections
13224          */
13225              
13226         var align = this.labelAlign || this.parentLabelAlign();
13227         
13228         cfg = {
13229             cls : 'form-group roo-combobox-tickable' //input-group
13230         };
13231         
13232         var btn_text_select = '';
13233         var btn_text_done = '';
13234         var btn_text_cancel = '';
13235         
13236         if (this.btn_text_show) {
13237             btn_text_select = 'Select';
13238             btn_text_done = 'Done';
13239             btn_text_cancel = 'Cancel'; 
13240         }
13241         
13242         var buttons = {
13243             tag : 'div',
13244             cls : 'tickable-buttons',
13245             cn : [
13246                 {
13247                     tag : 'button',
13248                     type : 'button',
13249                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13250                     //html : this.triggerText
13251                     html: btn_text_select
13252                 },
13253                 {
13254                     tag : 'button',
13255                     type : 'button',
13256                     name : 'ok',
13257                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13258                     //html : 'Done'
13259                     html: btn_text_done
13260                 },
13261                 {
13262                     tag : 'button',
13263                     type : 'button',
13264                     name : 'cancel',
13265                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13266                     //html : 'Cancel'
13267                     html: btn_text_cancel
13268                 }
13269             ]
13270         };
13271         
13272         if(this.editable){
13273             buttons.cn.unshift({
13274                 tag: 'input',
13275                 cls: 'roo-select2-search-field-input'
13276             });
13277         }
13278         
13279         var _this = this;
13280         
13281         Roo.each(buttons.cn, function(c){
13282             if (_this.size) {
13283                 c.cls += ' btn-' + _this.size;
13284             }
13285
13286             if (_this.disabled) {
13287                 c.disabled = true;
13288             }
13289         });
13290         
13291         var box = {
13292             tag: 'div',
13293             cn: [
13294                 {
13295                     tag: 'input',
13296                     type : 'hidden',
13297                     cls: 'form-hidden-field'
13298                 },
13299                 {
13300                     tag: 'ul',
13301                     cls: 'roo-select2-choices',
13302                     cn:[
13303                         {
13304                             tag: 'li',
13305                             cls: 'roo-select2-search-field',
13306                             cn: [
13307                                 buttons
13308                             ]
13309                         }
13310                     ]
13311                 }
13312             ]
13313         };
13314         
13315         var combobox = {
13316             cls: 'roo-select2-container input-group roo-select2-container-multi',
13317             cn: [
13318                 box
13319 //                {
13320 //                    tag: 'ul',
13321 //                    cls: 'typeahead typeahead-long dropdown-menu',
13322 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13323 //                }
13324             ]
13325         };
13326         
13327         if(this.hasFeedback && !this.allowBlank){
13328             
13329             var feedback = {
13330                 tag: 'span',
13331                 cls: 'glyphicon form-control-feedback'
13332             };
13333
13334             combobox.cn.push(feedback);
13335         }
13336         
13337         
13338         if (align ==='left' && this.fieldLabel.length) {
13339             
13340             cfg.cls += ' roo-form-group-label-left';
13341             
13342             cfg.cn = [
13343                 {
13344                     tag : 'i',
13345                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13346                     tooltip : 'This field is required'
13347                 },
13348                 {
13349                     tag: 'label',
13350                     'for' :  id,
13351                     cls : 'control-label',
13352                     html : this.fieldLabel
13353
13354                 },
13355                 {
13356                     cls : "", 
13357                     cn: [
13358                         combobox
13359                     ]
13360                 }
13361
13362             ];
13363             
13364             var labelCfg = cfg.cn[1];
13365             var contentCfg = cfg.cn[2];
13366             
13367
13368             if(this.indicatorpos == 'right'){
13369                 
13370                 cfg.cn = [
13371                     {
13372                         tag: 'label',
13373                         'for' :  id,
13374                         cls : 'control-label',
13375                         cn : [
13376                             {
13377                                 tag : 'span',
13378                                 html : this.fieldLabel
13379                             },
13380                             {
13381                                 tag : 'i',
13382                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13383                                 tooltip : 'This field is required'
13384                             }
13385                         ]
13386                     },
13387                     {
13388                         cls : "",
13389                         cn: [
13390                             combobox
13391                         ]
13392                     }
13393
13394                 ];
13395                 
13396                 
13397                 
13398                 labelCfg = cfg.cn[0];
13399                 contentCfg = cfg.cn[1];
13400             
13401             }
13402             
13403             if(this.labelWidth > 12){
13404                 labelCfg.style = "width: " + this.labelWidth + 'px';
13405             }
13406             
13407             if(this.labelWidth < 13 && this.labelmd == 0){
13408                 this.labelmd = this.labelWidth;
13409             }
13410             
13411             if(this.labellg > 0){
13412                 labelCfg.cls += ' col-lg-' + this.labellg;
13413                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13414             }
13415             
13416             if(this.labelmd > 0){
13417                 labelCfg.cls += ' col-md-' + this.labelmd;
13418                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13419             }
13420             
13421             if(this.labelsm > 0){
13422                 labelCfg.cls += ' col-sm-' + this.labelsm;
13423                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13424             }
13425             
13426             if(this.labelxs > 0){
13427                 labelCfg.cls += ' col-xs-' + this.labelxs;
13428                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13429             }
13430                 
13431                 
13432         } else if ( this.fieldLabel.length) {
13433 //                Roo.log(" label");
13434                  cfg.cn = [
13435                     {
13436                         tag : 'i',
13437                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13438                         tooltip : 'This field is required'
13439                     },
13440                     {
13441                         tag: 'label',
13442                         //cls : 'input-group-addon',
13443                         html : this.fieldLabel
13444                     },
13445                     combobox
13446                 ];
13447                 
13448                 if(this.indicatorpos == 'right'){
13449                     cfg.cn = [
13450                         {
13451                             tag: 'label',
13452                             //cls : 'input-group-addon',
13453                             html : this.fieldLabel
13454                         },
13455                         {
13456                             tag : 'i',
13457                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13458                             tooltip : 'This field is required'
13459                         },
13460                         combobox
13461                     ];
13462                     
13463                 }
13464
13465         } else {
13466             
13467 //                Roo.log(" no label && no align");
13468                 cfg = combobox
13469                      
13470                 
13471         }
13472          
13473         var settings=this;
13474         ['xs','sm','md','lg'].map(function(size){
13475             if (settings[size]) {
13476                 cfg.cls += ' col-' + size + '-' + settings[size];
13477             }
13478         });
13479         
13480         return cfg;
13481         
13482     },
13483     
13484     _initEventsCalled : false,
13485     
13486     // private
13487     initEvents: function()
13488     {   
13489         if (this._initEventsCalled) { // as we call render... prevent looping...
13490             return;
13491         }
13492         this._initEventsCalled = true;
13493         
13494         if (!this.store) {
13495             throw "can not find store for combo";
13496         }
13497         
13498         this.indicator = this.indicatorEl();
13499         
13500         this.store = Roo.factory(this.store, Roo.data);
13501         this.store.parent = this;
13502         
13503         // if we are building from html. then this element is so complex, that we can not really
13504         // use the rendered HTML.
13505         // so we have to trash and replace the previous code.
13506         if (Roo.XComponent.build_from_html) {
13507             // remove this element....
13508             var e = this.el.dom, k=0;
13509             while (e ) { e = e.previousSibling;  ++k;}
13510
13511             this.el.remove();
13512             
13513             this.el=false;
13514             this.rendered = false;
13515             
13516             this.render(this.parent().getChildContainer(true), k);
13517         }
13518         
13519         if(Roo.isIOS && this.useNativeIOS){
13520             this.initIOSView();
13521             return;
13522         }
13523         
13524         /*
13525          * Touch Devices
13526          */
13527         
13528         if(Roo.isTouch && this.mobileTouchView){
13529             this.initTouchView();
13530             return;
13531         }
13532         
13533         if(this.tickable){
13534             this.initTickableEvents();
13535             return;
13536         }
13537         
13538         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13539         
13540         if(this.hiddenName){
13541             
13542             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13543             
13544             this.hiddenField.dom.value =
13545                 this.hiddenValue !== undefined ? this.hiddenValue :
13546                 this.value !== undefined ? this.value : '';
13547
13548             // prevent input submission
13549             this.el.dom.removeAttribute('name');
13550             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13551              
13552              
13553         }
13554         //if(Roo.isGecko){
13555         //    this.el.dom.setAttribute('autocomplete', 'off');
13556         //}
13557         
13558         var cls = 'x-combo-list';
13559         
13560         //this.list = new Roo.Layer({
13561         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13562         //});
13563         
13564         var _this = this;
13565         
13566         (function(){
13567             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13568             _this.list.setWidth(lw);
13569         }).defer(100);
13570         
13571         this.list.on('mouseover', this.onViewOver, this);
13572         this.list.on('mousemove', this.onViewMove, this);
13573         this.list.on('scroll', this.onViewScroll, this);
13574         
13575         /*
13576         this.list.swallowEvent('mousewheel');
13577         this.assetHeight = 0;
13578
13579         if(this.title){
13580             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13581             this.assetHeight += this.header.getHeight();
13582         }
13583
13584         this.innerList = this.list.createChild({cls:cls+'-inner'});
13585         this.innerList.on('mouseover', this.onViewOver, this);
13586         this.innerList.on('mousemove', this.onViewMove, this);
13587         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13588         
13589         if(this.allowBlank && !this.pageSize && !this.disableClear){
13590             this.footer = this.list.createChild({cls:cls+'-ft'});
13591             this.pageTb = new Roo.Toolbar(this.footer);
13592            
13593         }
13594         if(this.pageSize){
13595             this.footer = this.list.createChild({cls:cls+'-ft'});
13596             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13597                     {pageSize: this.pageSize});
13598             
13599         }
13600         
13601         if (this.pageTb && this.allowBlank && !this.disableClear) {
13602             var _this = this;
13603             this.pageTb.add(new Roo.Toolbar.Fill(), {
13604                 cls: 'x-btn-icon x-btn-clear',
13605                 text: '&#160;',
13606                 handler: function()
13607                 {
13608                     _this.collapse();
13609                     _this.clearValue();
13610                     _this.onSelect(false, -1);
13611                 }
13612             });
13613         }
13614         if (this.footer) {
13615             this.assetHeight += this.footer.getHeight();
13616         }
13617         */
13618             
13619         if(!this.tpl){
13620             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13621         }
13622
13623         this.view = new Roo.View(this.list, this.tpl, {
13624             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13625         });
13626         //this.view.wrapEl.setDisplayed(false);
13627         this.view.on('click', this.onViewClick, this);
13628         
13629         
13630         this.store.on('beforeload', this.onBeforeLoad, this);
13631         this.store.on('load', this.onLoad, this);
13632         this.store.on('loadexception', this.onLoadException, this);
13633         /*
13634         if(this.resizable){
13635             this.resizer = new Roo.Resizable(this.list,  {
13636                pinned:true, handles:'se'
13637             });
13638             this.resizer.on('resize', function(r, w, h){
13639                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13640                 this.listWidth = w;
13641                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13642                 this.restrictHeight();
13643             }, this);
13644             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13645         }
13646         */
13647         if(!this.editable){
13648             this.editable = true;
13649             this.setEditable(false);
13650         }
13651         
13652         /*
13653         
13654         if (typeof(this.events.add.listeners) != 'undefined') {
13655             
13656             this.addicon = this.wrap.createChild(
13657                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13658        
13659             this.addicon.on('click', function(e) {
13660                 this.fireEvent('add', this);
13661             }, this);
13662         }
13663         if (typeof(this.events.edit.listeners) != 'undefined') {
13664             
13665             this.editicon = this.wrap.createChild(
13666                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13667             if (this.addicon) {
13668                 this.editicon.setStyle('margin-left', '40px');
13669             }
13670             this.editicon.on('click', function(e) {
13671                 
13672                 // we fire even  if inothing is selected..
13673                 this.fireEvent('edit', this, this.lastData );
13674                 
13675             }, this);
13676         }
13677         */
13678         
13679         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13680             "up" : function(e){
13681                 this.inKeyMode = true;
13682                 this.selectPrev();
13683             },
13684
13685             "down" : function(e){
13686                 if(!this.isExpanded()){
13687                     this.onTriggerClick();
13688                 }else{
13689                     this.inKeyMode = true;
13690                     this.selectNext();
13691                 }
13692             },
13693
13694             "enter" : function(e){
13695 //                this.onViewClick();
13696                 //return true;
13697                 this.collapse();
13698                 
13699                 if(this.fireEvent("specialkey", this, e)){
13700                     this.onViewClick(false);
13701                 }
13702                 
13703                 return true;
13704             },
13705
13706             "esc" : function(e){
13707                 this.collapse();
13708             },
13709
13710             "tab" : function(e){
13711                 this.collapse();
13712                 
13713                 if(this.fireEvent("specialkey", this, e)){
13714                     this.onViewClick(false);
13715                 }
13716                 
13717                 return true;
13718             },
13719
13720             scope : this,
13721
13722             doRelay : function(foo, bar, hname){
13723                 if(hname == 'down' || this.scope.isExpanded()){
13724                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13725                 }
13726                 return true;
13727             },
13728
13729             forceKeyDown: true
13730         });
13731         
13732         
13733         this.queryDelay = Math.max(this.queryDelay || 10,
13734                 this.mode == 'local' ? 10 : 250);
13735         
13736         
13737         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13738         
13739         if(this.typeAhead){
13740             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13741         }
13742         if(this.editable !== false){
13743             this.inputEl().on("keyup", this.onKeyUp, this);
13744         }
13745         if(this.forceSelection){
13746             this.inputEl().on('blur', this.doForce, this);
13747         }
13748         
13749         if(this.multiple){
13750             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13751             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13752         }
13753     },
13754     
13755     initTickableEvents: function()
13756     {   
13757         this.createList();
13758         
13759         if(this.hiddenName){
13760             
13761             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13762             
13763             this.hiddenField.dom.value =
13764                 this.hiddenValue !== undefined ? this.hiddenValue :
13765                 this.value !== undefined ? this.value : '';
13766
13767             // prevent input submission
13768             this.el.dom.removeAttribute('name');
13769             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13770              
13771              
13772         }
13773         
13774 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13775         
13776         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13777         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13778         if(this.triggerList){
13779             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13780         }
13781          
13782         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13783         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13784         
13785         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13786         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13787         
13788         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13789         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13790         
13791         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13792         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13793         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13794         
13795         this.okBtn.hide();
13796         this.cancelBtn.hide();
13797         
13798         var _this = this;
13799         
13800         (function(){
13801             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13802             _this.list.setWidth(lw);
13803         }).defer(100);
13804         
13805         this.list.on('mouseover', this.onViewOver, this);
13806         this.list.on('mousemove', this.onViewMove, this);
13807         
13808         this.list.on('scroll', this.onViewScroll, this);
13809         
13810         if(!this.tpl){
13811             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13812                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13813         }
13814
13815         this.view = new Roo.View(this.list, this.tpl, {
13816             singleSelect:true,
13817             tickable:true,
13818             parent:this,
13819             store: this.store,
13820             selectedClass: this.selectedClass
13821         });
13822         
13823         //this.view.wrapEl.setDisplayed(false);
13824         this.view.on('click', this.onViewClick, this);
13825         
13826         
13827         
13828         this.store.on('beforeload', this.onBeforeLoad, this);
13829         this.store.on('load', this.onLoad, this);
13830         this.store.on('loadexception', this.onLoadException, this);
13831         
13832         if(this.editable){
13833             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13834                 "up" : function(e){
13835                     this.inKeyMode = true;
13836                     this.selectPrev();
13837                 },
13838
13839                 "down" : function(e){
13840                     this.inKeyMode = true;
13841                     this.selectNext();
13842                 },
13843
13844                 "enter" : function(e){
13845                     if(this.fireEvent("specialkey", this, e)){
13846                         this.onViewClick(false);
13847                     }
13848                     
13849                     return true;
13850                 },
13851
13852                 "esc" : function(e){
13853                     this.onTickableFooterButtonClick(e, false, false);
13854                 },
13855
13856                 "tab" : function(e){
13857                     this.fireEvent("specialkey", this, e);
13858                     
13859                     this.onTickableFooterButtonClick(e, false, false);
13860                     
13861                     return true;
13862                 },
13863
13864                 scope : this,
13865
13866                 doRelay : function(e, fn, key){
13867                     if(this.scope.isExpanded()){
13868                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13869                     }
13870                     return true;
13871                 },
13872
13873                 forceKeyDown: true
13874             });
13875         }
13876         
13877         this.queryDelay = Math.max(this.queryDelay || 10,
13878                 this.mode == 'local' ? 10 : 250);
13879         
13880         
13881         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13882         
13883         if(this.typeAhead){
13884             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13885         }
13886         
13887         if(this.editable !== false){
13888             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13889         }
13890         
13891         this.indicator = this.indicatorEl();
13892         
13893         if(this.indicator){
13894             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13895             this.indicator.hide();
13896         }
13897         
13898     },
13899
13900     onDestroy : function(){
13901         if(this.view){
13902             this.view.setStore(null);
13903             this.view.el.removeAllListeners();
13904             this.view.el.remove();
13905             this.view.purgeListeners();
13906         }
13907         if(this.list){
13908             this.list.dom.innerHTML  = '';
13909         }
13910         
13911         if(this.store){
13912             this.store.un('beforeload', this.onBeforeLoad, this);
13913             this.store.un('load', this.onLoad, this);
13914             this.store.un('loadexception', this.onLoadException, this);
13915         }
13916         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13917     },
13918
13919     // private
13920     fireKey : function(e){
13921         if(e.isNavKeyPress() && !this.list.isVisible()){
13922             this.fireEvent("specialkey", this, e);
13923         }
13924     },
13925
13926     // private
13927     onResize: function(w, h){
13928 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13929 //        
13930 //        if(typeof w != 'number'){
13931 //            // we do not handle it!?!?
13932 //            return;
13933 //        }
13934 //        var tw = this.trigger.getWidth();
13935 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13936 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13937 //        var x = w - tw;
13938 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13939 //            
13940 //        //this.trigger.setStyle('left', x+'px');
13941 //        
13942 //        if(this.list && this.listWidth === undefined){
13943 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13944 //            this.list.setWidth(lw);
13945 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13946 //        }
13947         
13948     
13949         
13950     },
13951
13952     /**
13953      * Allow or prevent the user from directly editing the field text.  If false is passed,
13954      * the user will only be able to select from the items defined in the dropdown list.  This method
13955      * is the runtime equivalent of setting the 'editable' config option at config time.
13956      * @param {Boolean} value True to allow the user to directly edit the field text
13957      */
13958     setEditable : function(value){
13959         if(value == this.editable){
13960             return;
13961         }
13962         this.editable = value;
13963         if(!value){
13964             this.inputEl().dom.setAttribute('readOnly', true);
13965             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13966             this.inputEl().addClass('x-combo-noedit');
13967         }else{
13968             this.inputEl().dom.setAttribute('readOnly', false);
13969             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13970             this.inputEl().removeClass('x-combo-noedit');
13971         }
13972     },
13973
13974     // private
13975     
13976     onBeforeLoad : function(combo,opts){
13977         if(!this.hasFocus){
13978             return;
13979         }
13980          if (!opts.add) {
13981             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13982          }
13983         this.restrictHeight();
13984         this.selectedIndex = -1;
13985     },
13986
13987     // private
13988     onLoad : function(){
13989         
13990         this.hasQuery = false;
13991         
13992         if(!this.hasFocus){
13993             return;
13994         }
13995         
13996         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13997             this.loading.hide();
13998         }
13999         
14000         if(this.store.getCount() > 0){
14001             
14002             this.expand();
14003             this.restrictHeight();
14004             if(this.lastQuery == this.allQuery){
14005                 if(this.editable && !this.tickable){
14006                     this.inputEl().dom.select();
14007                 }
14008                 
14009                 if(
14010                     !this.selectByValue(this.value, true) &&
14011                     this.autoFocus && 
14012                     (
14013                         !this.store.lastOptions ||
14014                         typeof(this.store.lastOptions.add) == 'undefined' || 
14015                         this.store.lastOptions.add != true
14016                     )
14017                 ){
14018                     this.select(0, true);
14019                 }
14020             }else{
14021                 if(this.autoFocus){
14022                     this.selectNext();
14023                 }
14024                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14025                     this.taTask.delay(this.typeAheadDelay);
14026                 }
14027             }
14028         }else{
14029             this.onEmptyResults();
14030         }
14031         
14032         //this.el.focus();
14033     },
14034     // private
14035     onLoadException : function()
14036     {
14037         this.hasQuery = false;
14038         
14039         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14040             this.loading.hide();
14041         }
14042         
14043         if(this.tickable && this.editable){
14044             return;
14045         }
14046         
14047         this.collapse();
14048         // only causes errors at present
14049         //Roo.log(this.store.reader.jsonData);
14050         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14051             // fixme
14052             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14053         //}
14054         
14055         
14056     },
14057     // private
14058     onTypeAhead : function(){
14059         if(this.store.getCount() > 0){
14060             var r = this.store.getAt(0);
14061             var newValue = r.data[this.displayField];
14062             var len = newValue.length;
14063             var selStart = this.getRawValue().length;
14064             
14065             if(selStart != len){
14066                 this.setRawValue(newValue);
14067                 this.selectText(selStart, newValue.length);
14068             }
14069         }
14070     },
14071
14072     // private
14073     onSelect : function(record, index){
14074         
14075         if(this.fireEvent('beforeselect', this, record, index) !== false){
14076         
14077             this.setFromData(index > -1 ? record.data : false);
14078             
14079             this.collapse();
14080             this.fireEvent('select', this, record, index);
14081         }
14082     },
14083
14084     /**
14085      * Returns the currently selected field value or empty string if no value is set.
14086      * @return {String} value The selected value
14087      */
14088     getValue : function()
14089     {
14090         if(Roo.isIOS && this.useNativeIOS){
14091             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14092         }
14093         
14094         if(this.multiple){
14095             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14096         }
14097         
14098         if(this.valueField){
14099             return typeof this.value != 'undefined' ? this.value : '';
14100         }else{
14101             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14102         }
14103     },
14104     
14105     getRawValue : function()
14106     {
14107         if(Roo.isIOS && this.useNativeIOS){
14108             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14109         }
14110         
14111         var v = this.inputEl().getValue();
14112         
14113         return v;
14114     },
14115
14116     /**
14117      * Clears any text/value currently set in the field
14118      */
14119     clearValue : function(){
14120         
14121         if(this.hiddenField){
14122             this.hiddenField.dom.value = '';
14123         }
14124         this.value = '';
14125         this.setRawValue('');
14126         this.lastSelectionText = '';
14127         this.lastData = false;
14128         
14129         var close = this.closeTriggerEl();
14130         
14131         if(close){
14132             close.hide();
14133         }
14134         
14135         this.validate();
14136         
14137     },
14138
14139     /**
14140      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14141      * will be displayed in the field.  If the value does not match the data value of an existing item,
14142      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14143      * Otherwise the field will be blank (although the value will still be set).
14144      * @param {String} value The value to match
14145      */
14146     setValue : function(v)
14147     {
14148         if(Roo.isIOS && this.useNativeIOS){
14149             this.setIOSValue(v);
14150             return;
14151         }
14152         
14153         if(this.multiple){
14154             this.syncValue();
14155             return;
14156         }
14157         
14158         var text = v;
14159         if(this.valueField){
14160             var r = this.findRecord(this.valueField, v);
14161             if(r){
14162                 text = r.data[this.displayField];
14163             }else if(this.valueNotFoundText !== undefined){
14164                 text = this.valueNotFoundText;
14165             }
14166         }
14167         this.lastSelectionText = text;
14168         if(this.hiddenField){
14169             this.hiddenField.dom.value = v;
14170         }
14171         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14172         this.value = v;
14173         
14174         var close = this.closeTriggerEl();
14175         
14176         if(close){
14177             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14178         }
14179         
14180         this.validate();
14181     },
14182     /**
14183      * @property {Object} the last set data for the element
14184      */
14185     
14186     lastData : false,
14187     /**
14188      * Sets the value of the field based on a object which is related to the record format for the store.
14189      * @param {Object} value the value to set as. or false on reset?
14190      */
14191     setFromData : function(o){
14192         
14193         if(this.multiple){
14194             this.addItem(o);
14195             return;
14196         }
14197             
14198         var dv = ''; // display value
14199         var vv = ''; // value value..
14200         this.lastData = o;
14201         if (this.displayField) {
14202             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14203         } else {
14204             // this is an error condition!!!
14205             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14206         }
14207         
14208         if(this.valueField){
14209             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14210         }
14211         
14212         var close = this.closeTriggerEl();
14213         
14214         if(close){
14215             if(dv.length || vv * 1 > 0){
14216                 close.show() ;
14217                 this.blockFocus=true;
14218             } else {
14219                 close.hide();
14220             }             
14221         }
14222         
14223         if(this.hiddenField){
14224             this.hiddenField.dom.value = vv;
14225             
14226             this.lastSelectionText = dv;
14227             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14228             this.value = vv;
14229             return;
14230         }
14231         // no hidden field.. - we store the value in 'value', but still display
14232         // display field!!!!
14233         this.lastSelectionText = dv;
14234         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14235         this.value = vv;
14236         
14237         
14238         
14239     },
14240     // private
14241     reset : function(){
14242         // overridden so that last data is reset..
14243         
14244         if(this.multiple){
14245             this.clearItem();
14246             return;
14247         }
14248         
14249         this.setValue(this.originalValue);
14250         //this.clearInvalid();
14251         this.lastData = false;
14252         if (this.view) {
14253             this.view.clearSelections();
14254         }
14255         
14256         this.validate();
14257     },
14258     // private
14259     findRecord : function(prop, value){
14260         var record;
14261         if(this.store.getCount() > 0){
14262             this.store.each(function(r){
14263                 if(r.data[prop] == value){
14264                     record = r;
14265                     return false;
14266                 }
14267                 return true;
14268             });
14269         }
14270         return record;
14271     },
14272     
14273     getName: function()
14274     {
14275         // returns hidden if it's set..
14276         if (!this.rendered) {return ''};
14277         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14278         
14279     },
14280     // private
14281     onViewMove : function(e, t){
14282         this.inKeyMode = false;
14283     },
14284
14285     // private
14286     onViewOver : function(e, t){
14287         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14288             return;
14289         }
14290         var item = this.view.findItemFromChild(t);
14291         
14292         if(item){
14293             var index = this.view.indexOf(item);
14294             this.select(index, false);
14295         }
14296     },
14297
14298     // private
14299     onViewClick : function(view, doFocus, el, e)
14300     {
14301         var index = this.view.getSelectedIndexes()[0];
14302         
14303         var r = this.store.getAt(index);
14304         
14305         if(this.tickable){
14306             
14307             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14308                 return;
14309             }
14310             
14311             var rm = false;
14312             var _this = this;
14313             
14314             Roo.each(this.tickItems, function(v,k){
14315                 
14316                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14317                     Roo.log(v);
14318                     _this.tickItems.splice(k, 1);
14319                     
14320                     if(typeof(e) == 'undefined' && view == false){
14321                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14322                     }
14323                     
14324                     rm = true;
14325                     return;
14326                 }
14327             });
14328             
14329             if(rm){
14330                 return;
14331             }
14332             
14333             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14334                 this.tickItems.push(r.data);
14335             }
14336             
14337             if(typeof(e) == 'undefined' && view == false){
14338                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14339             }
14340                     
14341             return;
14342         }
14343         
14344         if(r){
14345             this.onSelect(r, index);
14346         }
14347         if(doFocus !== false && !this.blockFocus){
14348             this.inputEl().focus();
14349         }
14350     },
14351
14352     // private
14353     restrictHeight : function(){
14354         //this.innerList.dom.style.height = '';
14355         //var inner = this.innerList.dom;
14356         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14357         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14358         //this.list.beginUpdate();
14359         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14360         this.list.alignTo(this.inputEl(), this.listAlign);
14361         this.list.alignTo(this.inputEl(), this.listAlign);
14362         //this.list.endUpdate();
14363     },
14364
14365     // private
14366     onEmptyResults : function(){
14367         
14368         if(this.tickable && this.editable){
14369             this.hasFocus = false;
14370             this.restrictHeight();
14371             return;
14372         }
14373         
14374         this.collapse();
14375     },
14376
14377     /**
14378      * Returns true if the dropdown list is expanded, else false.
14379      */
14380     isExpanded : function(){
14381         return this.list.isVisible();
14382     },
14383
14384     /**
14385      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14386      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14387      * @param {String} value The data value of the item to select
14388      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14389      * selected item if it is not currently in view (defaults to true)
14390      * @return {Boolean} True if the value matched an item in the list, else false
14391      */
14392     selectByValue : function(v, scrollIntoView){
14393         if(v !== undefined && v !== null){
14394             var r = this.findRecord(this.valueField || this.displayField, v);
14395             if(r){
14396                 this.select(this.store.indexOf(r), scrollIntoView);
14397                 return true;
14398             }
14399         }
14400         return false;
14401     },
14402
14403     /**
14404      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14405      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14406      * @param {Number} index The zero-based index of the list item to select
14407      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14408      * selected item if it is not currently in view (defaults to true)
14409      */
14410     select : function(index, scrollIntoView){
14411         this.selectedIndex = index;
14412         this.view.select(index);
14413         if(scrollIntoView !== false){
14414             var el = this.view.getNode(index);
14415             /*
14416              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14417              */
14418             if(el){
14419                 this.list.scrollChildIntoView(el, false);
14420             }
14421         }
14422     },
14423
14424     // private
14425     selectNext : function(){
14426         var ct = this.store.getCount();
14427         if(ct > 0){
14428             if(this.selectedIndex == -1){
14429                 this.select(0);
14430             }else if(this.selectedIndex < ct-1){
14431                 this.select(this.selectedIndex+1);
14432             }
14433         }
14434     },
14435
14436     // private
14437     selectPrev : function(){
14438         var ct = this.store.getCount();
14439         if(ct > 0){
14440             if(this.selectedIndex == -1){
14441                 this.select(0);
14442             }else if(this.selectedIndex != 0){
14443                 this.select(this.selectedIndex-1);
14444             }
14445         }
14446     },
14447
14448     // private
14449     onKeyUp : function(e){
14450         if(this.editable !== false && !e.isSpecialKey()){
14451             this.lastKey = e.getKey();
14452             this.dqTask.delay(this.queryDelay);
14453         }
14454     },
14455
14456     // private
14457     validateBlur : function(){
14458         return !this.list || !this.list.isVisible();   
14459     },
14460
14461     // private
14462     initQuery : function(){
14463         
14464         var v = this.getRawValue();
14465         
14466         if(this.tickable && this.editable){
14467             v = this.tickableInputEl().getValue();
14468         }
14469         
14470         this.doQuery(v);
14471     },
14472
14473     // private
14474     doForce : function(){
14475         if(this.inputEl().dom.value.length > 0){
14476             this.inputEl().dom.value =
14477                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14478              
14479         }
14480     },
14481
14482     /**
14483      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14484      * query allowing the query action to be canceled if needed.
14485      * @param {String} query The SQL query to execute
14486      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14487      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14488      * saved in the current store (defaults to false)
14489      */
14490     doQuery : function(q, forceAll){
14491         
14492         if(q === undefined || q === null){
14493             q = '';
14494         }
14495         var qe = {
14496             query: q,
14497             forceAll: forceAll,
14498             combo: this,
14499             cancel:false
14500         };
14501         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14502             return false;
14503         }
14504         q = qe.query;
14505         
14506         forceAll = qe.forceAll;
14507         if(forceAll === true || (q.length >= this.minChars)){
14508             
14509             this.hasQuery = true;
14510             
14511             if(this.lastQuery != q || this.alwaysQuery){
14512                 this.lastQuery = q;
14513                 if(this.mode == 'local'){
14514                     this.selectedIndex = -1;
14515                     if(forceAll){
14516                         this.store.clearFilter();
14517                     }else{
14518                         
14519                         if(this.specialFilter){
14520                             this.fireEvent('specialfilter', this);
14521                             this.onLoad();
14522                             return;
14523                         }
14524                         
14525                         this.store.filter(this.displayField, q);
14526                     }
14527                     
14528                     this.store.fireEvent("datachanged", this.store);
14529                     
14530                     this.onLoad();
14531                     
14532                     
14533                 }else{
14534                     
14535                     this.store.baseParams[this.queryParam] = q;
14536                     
14537                     var options = {params : this.getParams(q)};
14538                     
14539                     if(this.loadNext){
14540                         options.add = true;
14541                         options.params.start = this.page * this.pageSize;
14542                     }
14543                     
14544                     this.store.load(options);
14545                     
14546                     /*
14547                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14548                      *  we should expand the list on onLoad
14549                      *  so command out it
14550                      */
14551 //                    this.expand();
14552                 }
14553             }else{
14554                 this.selectedIndex = -1;
14555                 this.onLoad();   
14556             }
14557         }
14558         
14559         this.loadNext = false;
14560     },
14561     
14562     // private
14563     getParams : function(q){
14564         var p = {};
14565         //p[this.queryParam] = q;
14566         
14567         if(this.pageSize){
14568             p.start = 0;
14569             p.limit = this.pageSize;
14570         }
14571         return p;
14572     },
14573
14574     /**
14575      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14576      */
14577     collapse : function(){
14578         if(!this.isExpanded()){
14579             return;
14580         }
14581         
14582         this.list.hide();
14583         
14584         this.hasFocus = false;
14585         
14586         if(this.tickable){
14587             this.okBtn.hide();
14588             this.cancelBtn.hide();
14589             this.trigger.show();
14590             
14591             if(this.editable){
14592                 this.tickableInputEl().dom.value = '';
14593                 this.tickableInputEl().blur();
14594             }
14595             
14596         }
14597         
14598         Roo.get(document).un('mousedown', this.collapseIf, this);
14599         Roo.get(document).un('mousewheel', this.collapseIf, this);
14600         if (!this.editable) {
14601             Roo.get(document).un('keydown', this.listKeyPress, this);
14602         }
14603         this.fireEvent('collapse', this);
14604         
14605         this.validate();
14606     },
14607
14608     // private
14609     collapseIf : function(e){
14610         var in_combo  = e.within(this.el);
14611         var in_list =  e.within(this.list);
14612         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14613         
14614         if (in_combo || in_list || is_list) {
14615             //e.stopPropagation();
14616             return;
14617         }
14618         
14619         if(this.tickable){
14620             this.onTickableFooterButtonClick(e, false, false);
14621         }
14622
14623         this.collapse();
14624         
14625     },
14626
14627     /**
14628      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14629      */
14630     expand : function(){
14631        
14632         if(this.isExpanded() || !this.hasFocus){
14633             return;
14634         }
14635         
14636         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14637         this.list.setWidth(lw);
14638         
14639         Roo.log('expand');
14640         
14641         this.list.show();
14642         
14643         this.restrictHeight();
14644         
14645         if(this.tickable){
14646             
14647             this.tickItems = Roo.apply([], this.item);
14648             
14649             this.okBtn.show();
14650             this.cancelBtn.show();
14651             this.trigger.hide();
14652             
14653             if(this.editable){
14654                 this.tickableInputEl().focus();
14655             }
14656             
14657         }
14658         
14659         Roo.get(document).on('mousedown', this.collapseIf, this);
14660         Roo.get(document).on('mousewheel', this.collapseIf, this);
14661         if (!this.editable) {
14662             Roo.get(document).on('keydown', this.listKeyPress, this);
14663         }
14664         
14665         this.fireEvent('expand', this);
14666     },
14667
14668     // private
14669     // Implements the default empty TriggerField.onTriggerClick function
14670     onTriggerClick : function(e)
14671     {
14672         Roo.log('trigger click');
14673         
14674         if(this.disabled || !this.triggerList){
14675             return;
14676         }
14677         
14678         this.page = 0;
14679         this.loadNext = false;
14680         
14681         if(this.isExpanded()){
14682             this.collapse();
14683             if (!this.blockFocus) {
14684                 this.inputEl().focus();
14685             }
14686             
14687         }else {
14688             this.hasFocus = true;
14689             if(this.triggerAction == 'all') {
14690                 this.doQuery(this.allQuery, true);
14691             } else {
14692                 this.doQuery(this.getRawValue());
14693             }
14694             if (!this.blockFocus) {
14695                 this.inputEl().focus();
14696             }
14697         }
14698     },
14699     
14700     onTickableTriggerClick : function(e)
14701     {
14702         if(this.disabled){
14703             return;
14704         }
14705         
14706         this.page = 0;
14707         this.loadNext = false;
14708         this.hasFocus = true;
14709         
14710         if(this.triggerAction == 'all') {
14711             this.doQuery(this.allQuery, true);
14712         } else {
14713             this.doQuery(this.getRawValue());
14714         }
14715     },
14716     
14717     onSearchFieldClick : function(e)
14718     {
14719         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14720             this.onTickableFooterButtonClick(e, false, false);
14721             return;
14722         }
14723         
14724         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14725             return;
14726         }
14727         
14728         this.page = 0;
14729         this.loadNext = false;
14730         this.hasFocus = true;
14731         
14732         if(this.triggerAction == 'all') {
14733             this.doQuery(this.allQuery, true);
14734         } else {
14735             this.doQuery(this.getRawValue());
14736         }
14737     },
14738     
14739     listKeyPress : function(e)
14740     {
14741         //Roo.log('listkeypress');
14742         // scroll to first matching element based on key pres..
14743         if (e.isSpecialKey()) {
14744             return false;
14745         }
14746         var k = String.fromCharCode(e.getKey()).toUpperCase();
14747         //Roo.log(k);
14748         var match  = false;
14749         var csel = this.view.getSelectedNodes();
14750         var cselitem = false;
14751         if (csel.length) {
14752             var ix = this.view.indexOf(csel[0]);
14753             cselitem  = this.store.getAt(ix);
14754             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14755                 cselitem = false;
14756             }
14757             
14758         }
14759         
14760         this.store.each(function(v) { 
14761             if (cselitem) {
14762                 // start at existing selection.
14763                 if (cselitem.id == v.id) {
14764                     cselitem = false;
14765                 }
14766                 return true;
14767             }
14768                 
14769             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14770                 match = this.store.indexOf(v);
14771                 return false;
14772             }
14773             return true;
14774         }, this);
14775         
14776         if (match === false) {
14777             return true; // no more action?
14778         }
14779         // scroll to?
14780         this.view.select(match);
14781         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14782         sn.scrollIntoView(sn.dom.parentNode, false);
14783     },
14784     
14785     onViewScroll : function(e, t){
14786         
14787         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){
14788             return;
14789         }
14790         
14791         this.hasQuery = true;
14792         
14793         this.loading = this.list.select('.loading', true).first();
14794         
14795         if(this.loading === null){
14796             this.list.createChild({
14797                 tag: 'div',
14798                 cls: 'loading roo-select2-more-results roo-select2-active',
14799                 html: 'Loading more results...'
14800             });
14801             
14802             this.loading = this.list.select('.loading', true).first();
14803             
14804             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14805             
14806             this.loading.hide();
14807         }
14808         
14809         this.loading.show();
14810         
14811         var _combo = this;
14812         
14813         this.page++;
14814         this.loadNext = true;
14815         
14816         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14817         
14818         return;
14819     },
14820     
14821     addItem : function(o)
14822     {   
14823         var dv = ''; // display value
14824         
14825         if (this.displayField) {
14826             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14827         } else {
14828             // this is an error condition!!!
14829             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14830         }
14831         
14832         if(!dv.length){
14833             return;
14834         }
14835         
14836         var choice = this.choices.createChild({
14837             tag: 'li',
14838             cls: 'roo-select2-search-choice',
14839             cn: [
14840                 {
14841                     tag: 'div',
14842                     html: dv
14843                 },
14844                 {
14845                     tag: 'a',
14846                     href: '#',
14847                     cls: 'roo-select2-search-choice-close fa fa-times',
14848                     tabindex: '-1'
14849                 }
14850             ]
14851             
14852         }, this.searchField);
14853         
14854         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14855         
14856         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14857         
14858         this.item.push(o);
14859         
14860         this.lastData = o;
14861         
14862         this.syncValue();
14863         
14864         this.inputEl().dom.value = '';
14865         
14866         this.validate();
14867     },
14868     
14869     onRemoveItem : function(e, _self, o)
14870     {
14871         e.preventDefault();
14872         
14873         this.lastItem = Roo.apply([], this.item);
14874         
14875         var index = this.item.indexOf(o.data) * 1;
14876         
14877         if( index < 0){
14878             Roo.log('not this item?!');
14879             return;
14880         }
14881         
14882         this.item.splice(index, 1);
14883         o.item.remove();
14884         
14885         this.syncValue();
14886         
14887         this.fireEvent('remove', this, e);
14888         
14889         this.validate();
14890         
14891     },
14892     
14893     syncValue : function()
14894     {
14895         if(!this.item.length){
14896             this.clearValue();
14897             return;
14898         }
14899             
14900         var value = [];
14901         var _this = this;
14902         Roo.each(this.item, function(i){
14903             if(_this.valueField){
14904                 value.push(i[_this.valueField]);
14905                 return;
14906             }
14907
14908             value.push(i);
14909         });
14910
14911         this.value = value.join(',');
14912
14913         if(this.hiddenField){
14914             this.hiddenField.dom.value = this.value;
14915         }
14916         
14917         this.store.fireEvent("datachanged", this.store);
14918         
14919         this.validate();
14920     },
14921     
14922     clearItem : function()
14923     {
14924         if(!this.multiple){
14925             return;
14926         }
14927         
14928         this.item = [];
14929         
14930         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14931            c.remove();
14932         });
14933         
14934         this.syncValue();
14935         
14936         this.validate();
14937         
14938         if(this.tickable && !Roo.isTouch){
14939             this.view.refresh();
14940         }
14941     },
14942     
14943     inputEl: function ()
14944     {
14945         if(Roo.isIOS && this.useNativeIOS){
14946             return this.el.select('select.roo-ios-select', true).first();
14947         }
14948         
14949         if(Roo.isTouch && this.mobileTouchView){
14950             return this.el.select('input.form-control',true).first();
14951         }
14952         
14953         if(this.tickable){
14954             return this.searchField;
14955         }
14956         
14957         return this.el.select('input.form-control',true).first();
14958     },
14959     
14960     onTickableFooterButtonClick : function(e, btn, el)
14961     {
14962         e.preventDefault();
14963         
14964         this.lastItem = Roo.apply([], this.item);
14965         
14966         if(btn && btn.name == 'cancel'){
14967             this.tickItems = Roo.apply([], this.item);
14968             this.collapse();
14969             return;
14970         }
14971         
14972         this.clearItem();
14973         
14974         var _this = this;
14975         
14976         Roo.each(this.tickItems, function(o){
14977             _this.addItem(o);
14978         });
14979         
14980         this.collapse();
14981         
14982     },
14983     
14984     validate : function()
14985     {
14986         if(this.getVisibilityEl().hasClass('hidden')){
14987             return true;
14988         }
14989         
14990         var v = this.getRawValue();
14991         
14992         if(this.multiple){
14993             v = this.getValue();
14994         }
14995         
14996         if(this.disabled || this.allowBlank || v.length){
14997             this.markValid();
14998             return true;
14999         }
15000         
15001         this.markInvalid();
15002         return false;
15003     },
15004     
15005     tickableInputEl : function()
15006     {
15007         if(!this.tickable || !this.editable){
15008             return this.inputEl();
15009         }
15010         
15011         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15012     },
15013     
15014     
15015     getAutoCreateTouchView : function()
15016     {
15017         var id = Roo.id();
15018         
15019         var cfg = {
15020             cls: 'form-group' //input-group
15021         };
15022         
15023         var input =  {
15024             tag: 'input',
15025             id : id,
15026             type : this.inputType,
15027             cls : 'form-control x-combo-noedit',
15028             autocomplete: 'new-password',
15029             placeholder : this.placeholder || '',
15030             readonly : true
15031         };
15032         
15033         if (this.name) {
15034             input.name = this.name;
15035         }
15036         
15037         if (this.size) {
15038             input.cls += ' input-' + this.size;
15039         }
15040         
15041         if (this.disabled) {
15042             input.disabled = true;
15043         }
15044         
15045         var inputblock = {
15046             cls : '',
15047             cn : [
15048                 input
15049             ]
15050         };
15051         
15052         if(this.before){
15053             inputblock.cls += ' input-group';
15054             
15055             inputblock.cn.unshift({
15056                 tag :'span',
15057                 cls : 'input-group-addon',
15058                 html : this.before
15059             });
15060         }
15061         
15062         if(this.removable && !this.multiple){
15063             inputblock.cls += ' roo-removable';
15064             
15065             inputblock.cn.push({
15066                 tag: 'button',
15067                 html : 'x',
15068                 cls : 'roo-combo-removable-btn close'
15069             });
15070         }
15071
15072         if(this.hasFeedback && !this.allowBlank){
15073             
15074             inputblock.cls += ' has-feedback';
15075             
15076             inputblock.cn.push({
15077                 tag: 'span',
15078                 cls: 'glyphicon form-control-feedback'
15079             });
15080             
15081         }
15082         
15083         if (this.after) {
15084             
15085             inputblock.cls += (this.before) ? '' : ' input-group';
15086             
15087             inputblock.cn.push({
15088                 tag :'span',
15089                 cls : 'input-group-addon',
15090                 html : this.after
15091             });
15092         }
15093
15094         var box = {
15095             tag: 'div',
15096             cn: [
15097                 {
15098                     tag: 'input',
15099                     type : 'hidden',
15100                     cls: 'form-hidden-field'
15101                 },
15102                 inputblock
15103             ]
15104             
15105         };
15106         
15107         if(this.multiple){
15108             box = {
15109                 tag: 'div',
15110                 cn: [
15111                     {
15112                         tag: 'input',
15113                         type : 'hidden',
15114                         cls: 'form-hidden-field'
15115                     },
15116                     {
15117                         tag: 'ul',
15118                         cls: 'roo-select2-choices',
15119                         cn:[
15120                             {
15121                                 tag: 'li',
15122                                 cls: 'roo-select2-search-field',
15123                                 cn: [
15124
15125                                     inputblock
15126                                 ]
15127                             }
15128                         ]
15129                     }
15130                 ]
15131             }
15132         };
15133         
15134         var combobox = {
15135             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15136             cn: [
15137                 box
15138             ]
15139         };
15140         
15141         if(!this.multiple && this.showToggleBtn){
15142             
15143             var caret = {
15144                         tag: 'span',
15145                         cls: 'caret'
15146             };
15147             
15148             if (this.caret != false) {
15149                 caret = {
15150                      tag: 'i',
15151                      cls: 'fa fa-' + this.caret
15152                 };
15153                 
15154             }
15155             
15156             combobox.cn.push({
15157                 tag :'span',
15158                 cls : 'input-group-addon btn dropdown-toggle',
15159                 cn : [
15160                     caret,
15161                     {
15162                         tag: 'span',
15163                         cls: 'combobox-clear',
15164                         cn  : [
15165                             {
15166                                 tag : 'i',
15167                                 cls: 'icon-remove'
15168                             }
15169                         ]
15170                     }
15171                 ]
15172
15173             })
15174         }
15175         
15176         if(this.multiple){
15177             combobox.cls += ' roo-select2-container-multi';
15178         }
15179         
15180         var align = this.labelAlign || this.parentLabelAlign();
15181         
15182         if (align ==='left' && this.fieldLabel.length) {
15183
15184             cfg.cn = [
15185                 {
15186                    tag : 'i',
15187                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15188                    tooltip : 'This field is required'
15189                 },
15190                 {
15191                     tag: 'label',
15192                     cls : 'control-label',
15193                     html : this.fieldLabel
15194
15195                 },
15196                 {
15197                     cls : '', 
15198                     cn: [
15199                         combobox
15200                     ]
15201                 }
15202             ];
15203             
15204             var labelCfg = cfg.cn[1];
15205             var contentCfg = cfg.cn[2];
15206             
15207
15208             if(this.indicatorpos == 'right'){
15209                 cfg.cn = [
15210                     {
15211                         tag: 'label',
15212                         'for' :  id,
15213                         cls : 'control-label',
15214                         cn : [
15215                             {
15216                                 tag : 'span',
15217                                 html : this.fieldLabel
15218                             },
15219                             {
15220                                 tag : 'i',
15221                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15222                                 tooltip : 'This field is required'
15223                             }
15224                         ]
15225                     },
15226                     {
15227                         cls : "",
15228                         cn: [
15229                             combobox
15230                         ]
15231                     }
15232
15233                 ];
15234                 
15235                 labelCfg = cfg.cn[0];
15236                 contentCfg = cfg.cn[1];
15237             }
15238             
15239            
15240             
15241             if(this.labelWidth > 12){
15242                 labelCfg.style = "width: " + this.labelWidth + 'px';
15243             }
15244             
15245             if(this.labelWidth < 13 && this.labelmd == 0){
15246                 this.labelmd = this.labelWidth;
15247             }
15248             
15249             if(this.labellg > 0){
15250                 labelCfg.cls += ' col-lg-' + this.labellg;
15251                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15252             }
15253             
15254             if(this.labelmd > 0){
15255                 labelCfg.cls += ' col-md-' + this.labelmd;
15256                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15257             }
15258             
15259             if(this.labelsm > 0){
15260                 labelCfg.cls += ' col-sm-' + this.labelsm;
15261                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15262             }
15263             
15264             if(this.labelxs > 0){
15265                 labelCfg.cls += ' col-xs-' + this.labelxs;
15266                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15267             }
15268                 
15269                 
15270         } else if ( this.fieldLabel.length) {
15271             cfg.cn = [
15272                 {
15273                    tag : 'i',
15274                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15275                    tooltip : 'This field is required'
15276                 },
15277                 {
15278                     tag: 'label',
15279                     cls : 'control-label',
15280                     html : this.fieldLabel
15281
15282                 },
15283                 {
15284                     cls : '', 
15285                     cn: [
15286                         combobox
15287                     ]
15288                 }
15289             ];
15290             
15291             if(this.indicatorpos == 'right'){
15292                 cfg.cn = [
15293                     {
15294                         tag: 'label',
15295                         cls : 'control-label',
15296                         html : this.fieldLabel,
15297                         cn : [
15298                             {
15299                                tag : 'i',
15300                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15301                                tooltip : 'This field is required'
15302                             }
15303                         ]
15304                     },
15305                     {
15306                         cls : '', 
15307                         cn: [
15308                             combobox
15309                         ]
15310                     }
15311                 ];
15312             }
15313         } else {
15314             cfg.cn = combobox;    
15315         }
15316         
15317         
15318         var settings = this;
15319         
15320         ['xs','sm','md','lg'].map(function(size){
15321             if (settings[size]) {
15322                 cfg.cls += ' col-' + size + '-' + settings[size];
15323             }
15324         });
15325         
15326         return cfg;
15327     },
15328     
15329     initTouchView : function()
15330     {
15331         this.renderTouchView();
15332         
15333         this.touchViewEl.on('scroll', function(){
15334             this.el.dom.scrollTop = 0;
15335         }, this);
15336         
15337         this.originalValue = this.getValue();
15338         
15339         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15340         
15341         this.inputEl().on("click", this.showTouchView, this);
15342         if (this.triggerEl) {
15343             this.triggerEl.on("click", this.showTouchView, this);
15344         }
15345         
15346         
15347         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15348         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15349         
15350         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15351         
15352         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15353         this.store.on('load', this.onTouchViewLoad, this);
15354         this.store.on('loadexception', this.onTouchViewLoadException, this);
15355         
15356         if(this.hiddenName){
15357             
15358             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15359             
15360             this.hiddenField.dom.value =
15361                 this.hiddenValue !== undefined ? this.hiddenValue :
15362                 this.value !== undefined ? this.value : '';
15363         
15364             this.el.dom.removeAttribute('name');
15365             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15366         }
15367         
15368         if(this.multiple){
15369             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15370             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15371         }
15372         
15373         if(this.removable && !this.multiple){
15374             var close = this.closeTriggerEl();
15375             if(close){
15376                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15377                 close.on('click', this.removeBtnClick, this, close);
15378             }
15379         }
15380         /*
15381          * fix the bug in Safari iOS8
15382          */
15383         this.inputEl().on("focus", function(e){
15384             document.activeElement.blur();
15385         }, this);
15386         
15387         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15388         
15389         return;
15390         
15391         
15392     },
15393     
15394     renderTouchView : function()
15395     {
15396         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15397         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15398         
15399         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15400         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15401         
15402         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15403         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15404         this.touchViewBodyEl.setStyle('overflow', 'auto');
15405         
15406         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15407         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15408         
15409         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15410         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15411         
15412     },
15413     
15414     showTouchView : function()
15415     {
15416         if(this.disabled){
15417             return;
15418         }
15419         
15420         this.touchViewHeaderEl.hide();
15421
15422         if(this.modalTitle.length){
15423             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15424             this.touchViewHeaderEl.show();
15425         }
15426
15427         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15428         this.touchViewEl.show();
15429
15430         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15431         
15432         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15433         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15434
15435         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15436
15437         if(this.modalTitle.length){
15438             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15439         }
15440         
15441         this.touchViewBodyEl.setHeight(bodyHeight);
15442
15443         if(this.animate){
15444             var _this = this;
15445             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15446         }else{
15447             this.touchViewEl.addClass('in');
15448         }
15449         
15450         if(this._touchViewMask){
15451             Roo.get(document.body).addClass("x-body-masked");
15452             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15453             this._touchViewMask.setStyle('z-index', 10000);
15454             this._touchViewMask.addClass('show');
15455         }
15456         
15457         this.doTouchViewQuery();
15458         
15459     },
15460     
15461     hideTouchView : function()
15462     {
15463         this.touchViewEl.removeClass('in');
15464
15465         if(this.animate){
15466             var _this = this;
15467             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15468         }else{
15469             this.touchViewEl.setStyle('display', 'none');
15470         }
15471         
15472         if(this._touchViewMask){
15473             this._touchViewMask.removeClass('show');
15474             Roo.get(document.body).removeClass("x-body-masked");
15475         }
15476     },
15477     
15478     setTouchViewValue : function()
15479     {
15480         if(this.multiple){
15481             this.clearItem();
15482         
15483             var _this = this;
15484
15485             Roo.each(this.tickItems, function(o){
15486                 this.addItem(o);
15487             }, this);
15488         }
15489         
15490         this.hideTouchView();
15491     },
15492     
15493     doTouchViewQuery : function()
15494     {
15495         var qe = {
15496             query: '',
15497             forceAll: true,
15498             combo: this,
15499             cancel:false
15500         };
15501         
15502         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15503             return false;
15504         }
15505         
15506         if(!this.alwaysQuery || this.mode == 'local'){
15507             this.onTouchViewLoad();
15508             return;
15509         }
15510         
15511         this.store.load();
15512     },
15513     
15514     onTouchViewBeforeLoad : function(combo,opts)
15515     {
15516         return;
15517     },
15518
15519     // private
15520     onTouchViewLoad : function()
15521     {
15522         if(this.store.getCount() < 1){
15523             this.onTouchViewEmptyResults();
15524             return;
15525         }
15526         
15527         this.clearTouchView();
15528         
15529         var rawValue = this.getRawValue();
15530         
15531         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15532         
15533         this.tickItems = [];
15534         
15535         this.store.data.each(function(d, rowIndex){
15536             var row = this.touchViewListGroup.createChild(template);
15537             
15538             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15539                 row.addClass(d.data.cls);
15540             }
15541             
15542             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15543                 var cfg = {
15544                     data : d.data,
15545                     html : d.data[this.displayField]
15546                 };
15547                 
15548                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15549                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15550                 }
15551             }
15552             row.removeClass('selected');
15553             if(!this.multiple && this.valueField &&
15554                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15555             {
15556                 // radio buttons..
15557                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15558                 row.addClass('selected');
15559             }
15560             
15561             if(this.multiple && this.valueField &&
15562                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15563             {
15564                 
15565                 // checkboxes...
15566                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15567                 this.tickItems.push(d.data);
15568             }
15569             
15570             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15571             
15572         }, this);
15573         
15574         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15575         
15576         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15577
15578         if(this.modalTitle.length){
15579             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15580         }
15581
15582         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15583         
15584         if(this.mobile_restrict_height && listHeight < bodyHeight){
15585             this.touchViewBodyEl.setHeight(listHeight);
15586         }
15587         
15588         var _this = this;
15589         
15590         if(firstChecked && listHeight > bodyHeight){
15591             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15592         }
15593         
15594     },
15595     
15596     onTouchViewLoadException : function()
15597     {
15598         this.hideTouchView();
15599     },
15600     
15601     onTouchViewEmptyResults : function()
15602     {
15603         this.clearTouchView();
15604         
15605         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15606         
15607         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15608         
15609     },
15610     
15611     clearTouchView : function()
15612     {
15613         this.touchViewListGroup.dom.innerHTML = '';
15614     },
15615     
15616     onTouchViewClick : function(e, el, o)
15617     {
15618         e.preventDefault();
15619         
15620         var row = o.row;
15621         var rowIndex = o.rowIndex;
15622         
15623         var r = this.store.getAt(rowIndex);
15624         
15625         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15626             
15627             if(!this.multiple){
15628                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15629                     c.dom.removeAttribute('checked');
15630                 }, this);
15631
15632                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15633
15634                 this.setFromData(r.data);
15635
15636                 var close = this.closeTriggerEl();
15637
15638                 if(close){
15639                     close.show();
15640                 }
15641
15642                 this.hideTouchView();
15643
15644                 this.fireEvent('select', this, r, rowIndex);
15645
15646                 return;
15647             }
15648
15649             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15650                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15651                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15652                 return;
15653             }
15654
15655             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15656             this.addItem(r.data);
15657             this.tickItems.push(r.data);
15658         }
15659     },
15660     
15661     getAutoCreateNativeIOS : function()
15662     {
15663         var cfg = {
15664             cls: 'form-group' //input-group,
15665         };
15666         
15667         var combobox =  {
15668             tag: 'select',
15669             cls : 'roo-ios-select'
15670         };
15671         
15672         if (this.name) {
15673             combobox.name = this.name;
15674         }
15675         
15676         if (this.disabled) {
15677             combobox.disabled = true;
15678         }
15679         
15680         var settings = this;
15681         
15682         ['xs','sm','md','lg'].map(function(size){
15683             if (settings[size]) {
15684                 cfg.cls += ' col-' + size + '-' + settings[size];
15685             }
15686         });
15687         
15688         cfg.cn = combobox;
15689         
15690         return cfg;
15691         
15692     },
15693     
15694     initIOSView : function()
15695     {
15696         this.store.on('load', this.onIOSViewLoad, this);
15697         
15698         return;
15699     },
15700     
15701     onIOSViewLoad : function()
15702     {
15703         if(this.store.getCount() < 1){
15704             return;
15705         }
15706         
15707         this.clearIOSView();
15708         
15709         if(this.allowBlank) {
15710             
15711             var default_text = '-- SELECT --';
15712             
15713             if(this.placeholder.length){
15714                 default_text = this.placeholder;
15715             }
15716             
15717             if(this.emptyTitle.length){
15718                 default_text += ' - ' + this.emptyTitle + ' -';
15719             }
15720             
15721             var opt = this.inputEl().createChild({
15722                 tag: 'option',
15723                 value : 0,
15724                 html : default_text
15725             });
15726             
15727             var o = {};
15728             o[this.valueField] = 0;
15729             o[this.displayField] = default_text;
15730             
15731             this.ios_options.push({
15732                 data : o,
15733                 el : opt
15734             });
15735             
15736         }
15737         
15738         this.store.data.each(function(d, rowIndex){
15739             
15740             var html = '';
15741             
15742             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15743                 html = d.data[this.displayField];
15744             }
15745             
15746             var value = '';
15747             
15748             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15749                 value = d.data[this.valueField];
15750             }
15751             
15752             var option = {
15753                 tag: 'option',
15754                 value : value,
15755                 html : html
15756             };
15757             
15758             if(this.value == d.data[this.valueField]){
15759                 option['selected'] = true;
15760             }
15761             
15762             var opt = this.inputEl().createChild(option);
15763             
15764             this.ios_options.push({
15765                 data : d.data,
15766                 el : opt
15767             });
15768             
15769         }, this);
15770         
15771         this.inputEl().on('change', function(){
15772            this.fireEvent('select', this);
15773         }, this);
15774         
15775     },
15776     
15777     clearIOSView: function()
15778     {
15779         this.inputEl().dom.innerHTML = '';
15780         
15781         this.ios_options = [];
15782     },
15783     
15784     setIOSValue: function(v)
15785     {
15786         this.value = v;
15787         
15788         if(!this.ios_options){
15789             return;
15790         }
15791         
15792         Roo.each(this.ios_options, function(opts){
15793            
15794            opts.el.dom.removeAttribute('selected');
15795            
15796            if(opts.data[this.valueField] != v){
15797                return;
15798            }
15799            
15800            opts.el.dom.setAttribute('selected', true);
15801            
15802         }, this);
15803     }
15804
15805     /** 
15806     * @cfg {Boolean} grow 
15807     * @hide 
15808     */
15809     /** 
15810     * @cfg {Number} growMin 
15811     * @hide 
15812     */
15813     /** 
15814     * @cfg {Number} growMax 
15815     * @hide 
15816     */
15817     /**
15818      * @hide
15819      * @method autoSize
15820      */
15821 });
15822
15823 Roo.apply(Roo.bootstrap.ComboBox,  {
15824     
15825     header : {
15826         tag: 'div',
15827         cls: 'modal-header',
15828         cn: [
15829             {
15830                 tag: 'h4',
15831                 cls: 'modal-title'
15832             }
15833         ]
15834     },
15835     
15836     body : {
15837         tag: 'div',
15838         cls: 'modal-body',
15839         cn: [
15840             {
15841                 tag: 'ul',
15842                 cls: 'list-group'
15843             }
15844         ]
15845     },
15846     
15847     listItemRadio : {
15848         tag: 'li',
15849         cls: 'list-group-item',
15850         cn: [
15851             {
15852                 tag: 'span',
15853                 cls: 'roo-combobox-list-group-item-value'
15854             },
15855             {
15856                 tag: 'div',
15857                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15858                 cn: [
15859                     {
15860                         tag: 'input',
15861                         type: 'radio'
15862                     },
15863                     {
15864                         tag: 'label'
15865                     }
15866                 ]
15867             }
15868         ]
15869     },
15870     
15871     listItemCheckbox : {
15872         tag: 'li',
15873         cls: 'list-group-item',
15874         cn: [
15875             {
15876                 tag: 'span',
15877                 cls: 'roo-combobox-list-group-item-value'
15878             },
15879             {
15880                 tag: 'div',
15881                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15882                 cn: [
15883                     {
15884                         tag: 'input',
15885                         type: 'checkbox'
15886                     },
15887                     {
15888                         tag: 'label'
15889                     }
15890                 ]
15891             }
15892         ]
15893     },
15894     
15895     emptyResult : {
15896         tag: 'div',
15897         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15898     },
15899     
15900     footer : {
15901         tag: 'div',
15902         cls: 'modal-footer',
15903         cn: [
15904             {
15905                 tag: 'div',
15906                 cls: 'row',
15907                 cn: [
15908                     {
15909                         tag: 'div',
15910                         cls: 'col-xs-6 text-left',
15911                         cn: {
15912                             tag: 'button',
15913                             cls: 'btn btn-danger roo-touch-view-cancel',
15914                             html: 'Cancel'
15915                         }
15916                     },
15917                     {
15918                         tag: 'div',
15919                         cls: 'col-xs-6 text-right',
15920                         cn: {
15921                             tag: 'button',
15922                             cls: 'btn btn-success roo-touch-view-ok',
15923                             html: 'OK'
15924                         }
15925                     }
15926                 ]
15927             }
15928         ]
15929         
15930     }
15931 });
15932
15933 Roo.apply(Roo.bootstrap.ComboBox,  {
15934     
15935     touchViewTemplate : {
15936         tag: 'div',
15937         cls: 'modal fade roo-combobox-touch-view',
15938         cn: [
15939             {
15940                 tag: 'div',
15941                 cls: 'modal-dialog',
15942                 style : 'position:fixed', // we have to fix position....
15943                 cn: [
15944                     {
15945                         tag: 'div',
15946                         cls: 'modal-content',
15947                         cn: [
15948                             Roo.bootstrap.ComboBox.header,
15949                             Roo.bootstrap.ComboBox.body,
15950                             Roo.bootstrap.ComboBox.footer
15951                         ]
15952                     }
15953                 ]
15954             }
15955         ]
15956     }
15957 });/*
15958  * Based on:
15959  * Ext JS Library 1.1.1
15960  * Copyright(c) 2006-2007, Ext JS, LLC.
15961  *
15962  * Originally Released Under LGPL - original licence link has changed is not relivant.
15963  *
15964  * Fork - LGPL
15965  * <script type="text/javascript">
15966  */
15967
15968 /**
15969  * @class Roo.View
15970  * @extends Roo.util.Observable
15971  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15972  * This class also supports single and multi selection modes. <br>
15973  * Create a data model bound view:
15974  <pre><code>
15975  var store = new Roo.data.Store(...);
15976
15977  var view = new Roo.View({
15978     el : "my-element",
15979     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15980  
15981     singleSelect: true,
15982     selectedClass: "ydataview-selected",
15983     store: store
15984  });
15985
15986  // listen for node click?
15987  view.on("click", function(vw, index, node, e){
15988  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15989  });
15990
15991  // load XML data
15992  dataModel.load("foobar.xml");
15993  </code></pre>
15994  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15995  * <br><br>
15996  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15997  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15998  * 
15999  * Note: old style constructor is still suported (container, template, config)
16000  * 
16001  * @constructor
16002  * Create a new View
16003  * @param {Object} config The config object
16004  * 
16005  */
16006 Roo.View = function(config, depreciated_tpl, depreciated_config){
16007     
16008     this.parent = false;
16009     
16010     if (typeof(depreciated_tpl) == 'undefined') {
16011         // new way.. - universal constructor.
16012         Roo.apply(this, config);
16013         this.el  = Roo.get(this.el);
16014     } else {
16015         // old format..
16016         this.el  = Roo.get(config);
16017         this.tpl = depreciated_tpl;
16018         Roo.apply(this, depreciated_config);
16019     }
16020     this.wrapEl  = this.el.wrap().wrap();
16021     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16022     
16023     
16024     if(typeof(this.tpl) == "string"){
16025         this.tpl = new Roo.Template(this.tpl);
16026     } else {
16027         // support xtype ctors..
16028         this.tpl = new Roo.factory(this.tpl, Roo);
16029     }
16030     
16031     
16032     this.tpl.compile();
16033     
16034     /** @private */
16035     this.addEvents({
16036         /**
16037          * @event beforeclick
16038          * Fires before a click is processed. Returns false to cancel the default action.
16039          * @param {Roo.View} this
16040          * @param {Number} index The index of the target node
16041          * @param {HTMLElement} node The target node
16042          * @param {Roo.EventObject} e The raw event object
16043          */
16044             "beforeclick" : true,
16045         /**
16046          * @event click
16047          * Fires when a template node is clicked.
16048          * @param {Roo.View} this
16049          * @param {Number} index The index of the target node
16050          * @param {HTMLElement} node The target node
16051          * @param {Roo.EventObject} e The raw event object
16052          */
16053             "click" : true,
16054         /**
16055          * @event dblclick
16056          * Fires when a template node is double clicked.
16057          * @param {Roo.View} this
16058          * @param {Number} index The index of the target node
16059          * @param {HTMLElement} node The target node
16060          * @param {Roo.EventObject} e The raw event object
16061          */
16062             "dblclick" : true,
16063         /**
16064          * @event contextmenu
16065          * Fires when a template node is right clicked.
16066          * @param {Roo.View} this
16067          * @param {Number} index The index of the target node
16068          * @param {HTMLElement} node The target node
16069          * @param {Roo.EventObject} e The raw event object
16070          */
16071             "contextmenu" : true,
16072         /**
16073          * @event selectionchange
16074          * Fires when the selected nodes change.
16075          * @param {Roo.View} this
16076          * @param {Array} selections Array of the selected nodes
16077          */
16078             "selectionchange" : true,
16079     
16080         /**
16081          * @event beforeselect
16082          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16083          * @param {Roo.View} this
16084          * @param {HTMLElement} node The node to be selected
16085          * @param {Array} selections Array of currently selected nodes
16086          */
16087             "beforeselect" : true,
16088         /**
16089          * @event preparedata
16090          * Fires on every row to render, to allow you to change the data.
16091          * @param {Roo.View} this
16092          * @param {Object} data to be rendered (change this)
16093          */
16094           "preparedata" : true
16095           
16096           
16097         });
16098
16099
16100
16101     this.el.on({
16102         "click": this.onClick,
16103         "dblclick": this.onDblClick,
16104         "contextmenu": this.onContextMenu,
16105         scope:this
16106     });
16107
16108     this.selections = [];
16109     this.nodes = [];
16110     this.cmp = new Roo.CompositeElementLite([]);
16111     if(this.store){
16112         this.store = Roo.factory(this.store, Roo.data);
16113         this.setStore(this.store, true);
16114     }
16115     
16116     if ( this.footer && this.footer.xtype) {
16117            
16118          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16119         
16120         this.footer.dataSource = this.store;
16121         this.footer.container = fctr;
16122         this.footer = Roo.factory(this.footer, Roo);
16123         fctr.insertFirst(this.el);
16124         
16125         // this is a bit insane - as the paging toolbar seems to detach the el..
16126 //        dom.parentNode.parentNode.parentNode
16127          // they get detached?
16128     }
16129     
16130     
16131     Roo.View.superclass.constructor.call(this);
16132     
16133     
16134 };
16135
16136 Roo.extend(Roo.View, Roo.util.Observable, {
16137     
16138      /**
16139      * @cfg {Roo.data.Store} store Data store to load data from.
16140      */
16141     store : false,
16142     
16143     /**
16144      * @cfg {String|Roo.Element} el The container element.
16145      */
16146     el : '',
16147     
16148     /**
16149      * @cfg {String|Roo.Template} tpl The template used by this View 
16150      */
16151     tpl : false,
16152     /**
16153      * @cfg {String} dataName the named area of the template to use as the data area
16154      *                          Works with domtemplates roo-name="name"
16155      */
16156     dataName: false,
16157     /**
16158      * @cfg {String} selectedClass The css class to add to selected nodes
16159      */
16160     selectedClass : "x-view-selected",
16161      /**
16162      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16163      */
16164     emptyText : "",
16165     
16166     /**
16167      * @cfg {String} text to display on mask (default Loading)
16168      */
16169     mask : false,
16170     /**
16171      * @cfg {Boolean} multiSelect Allow multiple selection
16172      */
16173     multiSelect : false,
16174     /**
16175      * @cfg {Boolean} singleSelect Allow single selection
16176      */
16177     singleSelect:  false,
16178     
16179     /**
16180      * @cfg {Boolean} toggleSelect - selecting 
16181      */
16182     toggleSelect : false,
16183     
16184     /**
16185      * @cfg {Boolean} tickable - selecting 
16186      */
16187     tickable : false,
16188     
16189     /**
16190      * Returns the element this view is bound to.
16191      * @return {Roo.Element}
16192      */
16193     getEl : function(){
16194         return this.wrapEl;
16195     },
16196     
16197     
16198
16199     /**
16200      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16201      */
16202     refresh : function(){
16203         //Roo.log('refresh');
16204         var t = this.tpl;
16205         
16206         // if we are using something like 'domtemplate', then
16207         // the what gets used is:
16208         // t.applySubtemplate(NAME, data, wrapping data..)
16209         // the outer template then get' applied with
16210         //     the store 'extra data'
16211         // and the body get's added to the
16212         //      roo-name="data" node?
16213         //      <span class='roo-tpl-{name}'></span> ?????
16214         
16215         
16216         
16217         this.clearSelections();
16218         this.el.update("");
16219         var html = [];
16220         var records = this.store.getRange();
16221         if(records.length < 1) {
16222             
16223             // is this valid??  = should it render a template??
16224             
16225             this.el.update(this.emptyText);
16226             return;
16227         }
16228         var el = this.el;
16229         if (this.dataName) {
16230             this.el.update(t.apply(this.store.meta)); //????
16231             el = this.el.child('.roo-tpl-' + this.dataName);
16232         }
16233         
16234         for(var i = 0, len = records.length; i < len; i++){
16235             var data = this.prepareData(records[i].data, i, records[i]);
16236             this.fireEvent("preparedata", this, data, i, records[i]);
16237             
16238             var d = Roo.apply({}, data);
16239             
16240             if(this.tickable){
16241                 Roo.apply(d, {'roo-id' : Roo.id()});
16242                 
16243                 var _this = this;
16244             
16245                 Roo.each(this.parent.item, function(item){
16246                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16247                         return;
16248                     }
16249                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16250                 });
16251             }
16252             
16253             html[html.length] = Roo.util.Format.trim(
16254                 this.dataName ?
16255                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16256                     t.apply(d)
16257             );
16258         }
16259         
16260         
16261         
16262         el.update(html.join(""));
16263         this.nodes = el.dom.childNodes;
16264         this.updateIndexes(0);
16265     },
16266     
16267
16268     /**
16269      * Function to override to reformat the data that is sent to
16270      * the template for each node.
16271      * DEPRICATED - use the preparedata event handler.
16272      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16273      * a JSON object for an UpdateManager bound view).
16274      */
16275     prepareData : function(data, index, record)
16276     {
16277         this.fireEvent("preparedata", this, data, index, record);
16278         return data;
16279     },
16280
16281     onUpdate : function(ds, record){
16282         // Roo.log('on update');   
16283         this.clearSelections();
16284         var index = this.store.indexOf(record);
16285         var n = this.nodes[index];
16286         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16287         n.parentNode.removeChild(n);
16288         this.updateIndexes(index, index);
16289     },
16290
16291     
16292     
16293 // --------- FIXME     
16294     onAdd : function(ds, records, index)
16295     {
16296         //Roo.log(['on Add', ds, records, index] );        
16297         this.clearSelections();
16298         if(this.nodes.length == 0){
16299             this.refresh();
16300             return;
16301         }
16302         var n = this.nodes[index];
16303         for(var i = 0, len = records.length; i < len; i++){
16304             var d = this.prepareData(records[i].data, i, records[i]);
16305             if(n){
16306                 this.tpl.insertBefore(n, d);
16307             }else{
16308                 
16309                 this.tpl.append(this.el, d);
16310             }
16311         }
16312         this.updateIndexes(index);
16313     },
16314
16315     onRemove : function(ds, record, index){
16316        // Roo.log('onRemove');
16317         this.clearSelections();
16318         var el = this.dataName  ?
16319             this.el.child('.roo-tpl-' + this.dataName) :
16320             this.el; 
16321         
16322         el.dom.removeChild(this.nodes[index]);
16323         this.updateIndexes(index);
16324     },
16325
16326     /**
16327      * Refresh an individual node.
16328      * @param {Number} index
16329      */
16330     refreshNode : function(index){
16331         this.onUpdate(this.store, this.store.getAt(index));
16332     },
16333
16334     updateIndexes : function(startIndex, endIndex){
16335         var ns = this.nodes;
16336         startIndex = startIndex || 0;
16337         endIndex = endIndex || ns.length - 1;
16338         for(var i = startIndex; i <= endIndex; i++){
16339             ns[i].nodeIndex = i;
16340         }
16341     },
16342
16343     /**
16344      * Changes the data store this view uses and refresh the view.
16345      * @param {Store} store
16346      */
16347     setStore : function(store, initial){
16348         if(!initial && this.store){
16349             this.store.un("datachanged", this.refresh);
16350             this.store.un("add", this.onAdd);
16351             this.store.un("remove", this.onRemove);
16352             this.store.un("update", this.onUpdate);
16353             this.store.un("clear", this.refresh);
16354             this.store.un("beforeload", this.onBeforeLoad);
16355             this.store.un("load", this.onLoad);
16356             this.store.un("loadexception", this.onLoad);
16357         }
16358         if(store){
16359           
16360             store.on("datachanged", this.refresh, this);
16361             store.on("add", this.onAdd, this);
16362             store.on("remove", this.onRemove, this);
16363             store.on("update", this.onUpdate, this);
16364             store.on("clear", this.refresh, this);
16365             store.on("beforeload", this.onBeforeLoad, this);
16366             store.on("load", this.onLoad, this);
16367             store.on("loadexception", this.onLoad, this);
16368         }
16369         
16370         if(store){
16371             this.refresh();
16372         }
16373     },
16374     /**
16375      * onbeforeLoad - masks the loading area.
16376      *
16377      */
16378     onBeforeLoad : function(store,opts)
16379     {
16380          //Roo.log('onBeforeLoad');   
16381         if (!opts.add) {
16382             this.el.update("");
16383         }
16384         this.el.mask(this.mask ? this.mask : "Loading" ); 
16385     },
16386     onLoad : function ()
16387     {
16388         this.el.unmask();
16389     },
16390     
16391
16392     /**
16393      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16394      * @param {HTMLElement} node
16395      * @return {HTMLElement} The template node
16396      */
16397     findItemFromChild : function(node){
16398         var el = this.dataName  ?
16399             this.el.child('.roo-tpl-' + this.dataName,true) :
16400             this.el.dom; 
16401         
16402         if(!node || node.parentNode == el){
16403                     return node;
16404             }
16405             var p = node.parentNode;
16406             while(p && p != el){
16407             if(p.parentNode == el){
16408                 return p;
16409             }
16410             p = p.parentNode;
16411         }
16412             return null;
16413     },
16414
16415     /** @ignore */
16416     onClick : function(e){
16417         var item = this.findItemFromChild(e.getTarget());
16418         if(item){
16419             var index = this.indexOf(item);
16420             if(this.onItemClick(item, index, e) !== false){
16421                 this.fireEvent("click", this, index, item, e);
16422             }
16423         }else{
16424             this.clearSelections();
16425         }
16426     },
16427
16428     /** @ignore */
16429     onContextMenu : function(e){
16430         var item = this.findItemFromChild(e.getTarget());
16431         if(item){
16432             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16433         }
16434     },
16435
16436     /** @ignore */
16437     onDblClick : function(e){
16438         var item = this.findItemFromChild(e.getTarget());
16439         if(item){
16440             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16441         }
16442     },
16443
16444     onItemClick : function(item, index, e)
16445     {
16446         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16447             return false;
16448         }
16449         if (this.toggleSelect) {
16450             var m = this.isSelected(item) ? 'unselect' : 'select';
16451             //Roo.log(m);
16452             var _t = this;
16453             _t[m](item, true, false);
16454             return true;
16455         }
16456         if(this.multiSelect || this.singleSelect){
16457             if(this.multiSelect && e.shiftKey && this.lastSelection){
16458                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16459             }else{
16460                 this.select(item, this.multiSelect && e.ctrlKey);
16461                 this.lastSelection = item;
16462             }
16463             
16464             if(!this.tickable){
16465                 e.preventDefault();
16466             }
16467             
16468         }
16469         return true;
16470     },
16471
16472     /**
16473      * Get the number of selected nodes.
16474      * @return {Number}
16475      */
16476     getSelectionCount : function(){
16477         return this.selections.length;
16478     },
16479
16480     /**
16481      * Get the currently selected nodes.
16482      * @return {Array} An array of HTMLElements
16483      */
16484     getSelectedNodes : function(){
16485         return this.selections;
16486     },
16487
16488     /**
16489      * Get the indexes of the selected nodes.
16490      * @return {Array}
16491      */
16492     getSelectedIndexes : function(){
16493         var indexes = [], s = this.selections;
16494         for(var i = 0, len = s.length; i < len; i++){
16495             indexes.push(s[i].nodeIndex);
16496         }
16497         return indexes;
16498     },
16499
16500     /**
16501      * Clear all selections
16502      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16503      */
16504     clearSelections : function(suppressEvent){
16505         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16506             this.cmp.elements = this.selections;
16507             this.cmp.removeClass(this.selectedClass);
16508             this.selections = [];
16509             if(!suppressEvent){
16510                 this.fireEvent("selectionchange", this, this.selections);
16511             }
16512         }
16513     },
16514
16515     /**
16516      * Returns true if the passed node is selected
16517      * @param {HTMLElement/Number} node The node or node index
16518      * @return {Boolean}
16519      */
16520     isSelected : function(node){
16521         var s = this.selections;
16522         if(s.length < 1){
16523             return false;
16524         }
16525         node = this.getNode(node);
16526         return s.indexOf(node) !== -1;
16527     },
16528
16529     /**
16530      * Selects nodes.
16531      * @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
16532      * @param {Boolean} keepExisting (optional) true to keep existing selections
16533      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16534      */
16535     select : function(nodeInfo, keepExisting, suppressEvent){
16536         if(nodeInfo instanceof Array){
16537             if(!keepExisting){
16538                 this.clearSelections(true);
16539             }
16540             for(var i = 0, len = nodeInfo.length; i < len; i++){
16541                 this.select(nodeInfo[i], true, true);
16542             }
16543             return;
16544         } 
16545         var node = this.getNode(nodeInfo);
16546         if(!node || this.isSelected(node)){
16547             return; // already selected.
16548         }
16549         if(!keepExisting){
16550             this.clearSelections(true);
16551         }
16552         
16553         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16554             Roo.fly(node).addClass(this.selectedClass);
16555             this.selections.push(node);
16556             if(!suppressEvent){
16557                 this.fireEvent("selectionchange", this, this.selections);
16558             }
16559         }
16560         
16561         
16562     },
16563       /**
16564      * Unselects nodes.
16565      * @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
16566      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16567      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16568      */
16569     unselect : function(nodeInfo, keepExisting, suppressEvent)
16570     {
16571         if(nodeInfo instanceof Array){
16572             Roo.each(this.selections, function(s) {
16573                 this.unselect(s, nodeInfo);
16574             }, this);
16575             return;
16576         }
16577         var node = this.getNode(nodeInfo);
16578         if(!node || !this.isSelected(node)){
16579             //Roo.log("not selected");
16580             return; // not selected.
16581         }
16582         // fireevent???
16583         var ns = [];
16584         Roo.each(this.selections, function(s) {
16585             if (s == node ) {
16586                 Roo.fly(node).removeClass(this.selectedClass);
16587
16588                 return;
16589             }
16590             ns.push(s);
16591         },this);
16592         
16593         this.selections= ns;
16594         this.fireEvent("selectionchange", this, this.selections);
16595     },
16596
16597     /**
16598      * Gets a template node.
16599      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16600      * @return {HTMLElement} The node or null if it wasn't found
16601      */
16602     getNode : function(nodeInfo){
16603         if(typeof nodeInfo == "string"){
16604             return document.getElementById(nodeInfo);
16605         }else if(typeof nodeInfo == "number"){
16606             return this.nodes[nodeInfo];
16607         }
16608         return nodeInfo;
16609     },
16610
16611     /**
16612      * Gets a range template nodes.
16613      * @param {Number} startIndex
16614      * @param {Number} endIndex
16615      * @return {Array} An array of nodes
16616      */
16617     getNodes : function(start, end){
16618         var ns = this.nodes;
16619         start = start || 0;
16620         end = typeof end == "undefined" ? ns.length - 1 : end;
16621         var nodes = [];
16622         if(start <= end){
16623             for(var i = start; i <= end; i++){
16624                 nodes.push(ns[i]);
16625             }
16626         } else{
16627             for(var i = start; i >= end; i--){
16628                 nodes.push(ns[i]);
16629             }
16630         }
16631         return nodes;
16632     },
16633
16634     /**
16635      * Finds the index of the passed node
16636      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16637      * @return {Number} The index of the node or -1
16638      */
16639     indexOf : function(node){
16640         node = this.getNode(node);
16641         if(typeof node.nodeIndex == "number"){
16642             return node.nodeIndex;
16643         }
16644         var ns = this.nodes;
16645         for(var i = 0, len = ns.length; i < len; i++){
16646             if(ns[i] == node){
16647                 return i;
16648             }
16649         }
16650         return -1;
16651     }
16652 });
16653 /*
16654  * - LGPL
16655  *
16656  * based on jquery fullcalendar
16657  * 
16658  */
16659
16660 Roo.bootstrap = Roo.bootstrap || {};
16661 /**
16662  * @class Roo.bootstrap.Calendar
16663  * @extends Roo.bootstrap.Component
16664  * Bootstrap Calendar class
16665  * @cfg {Boolean} loadMask (true|false) default false
16666  * @cfg {Object} header generate the user specific header of the calendar, default false
16667
16668  * @constructor
16669  * Create a new Container
16670  * @param {Object} config The config object
16671  */
16672
16673
16674
16675 Roo.bootstrap.Calendar = function(config){
16676     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16677      this.addEvents({
16678         /**
16679              * @event select
16680              * Fires when a date is selected
16681              * @param {DatePicker} this
16682              * @param {Date} date The selected date
16683              */
16684         'select': true,
16685         /**
16686              * @event monthchange
16687              * Fires when the displayed month changes 
16688              * @param {DatePicker} this
16689              * @param {Date} date The selected month
16690              */
16691         'monthchange': true,
16692         /**
16693              * @event evententer
16694              * Fires when mouse over an event
16695              * @param {Calendar} this
16696              * @param {event} Event
16697              */
16698         'evententer': true,
16699         /**
16700              * @event eventleave
16701              * Fires when the mouse leaves an
16702              * @param {Calendar} this
16703              * @param {event}
16704              */
16705         'eventleave': true,
16706         /**
16707              * @event eventclick
16708              * Fires when the mouse click an
16709              * @param {Calendar} this
16710              * @param {event}
16711              */
16712         'eventclick': true
16713         
16714     });
16715
16716 };
16717
16718 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16719     
16720      /**
16721      * @cfg {Number} startDay
16722      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16723      */
16724     startDay : 0,
16725     
16726     loadMask : false,
16727     
16728     header : false,
16729       
16730     getAutoCreate : function(){
16731         
16732         
16733         var fc_button = function(name, corner, style, content ) {
16734             return Roo.apply({},{
16735                 tag : 'span',
16736                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16737                          (corner.length ?
16738                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16739                             ''
16740                         ),
16741                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16742                 unselectable: 'on'
16743             });
16744         };
16745         
16746         var header = {};
16747         
16748         if(!this.header){
16749             header = {
16750                 tag : 'table',
16751                 cls : 'fc-header',
16752                 style : 'width:100%',
16753                 cn : [
16754                     {
16755                         tag: 'tr',
16756                         cn : [
16757                             {
16758                                 tag : 'td',
16759                                 cls : 'fc-header-left',
16760                                 cn : [
16761                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16762                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16763                                     { tag: 'span', cls: 'fc-header-space' },
16764                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16765
16766
16767                                 ]
16768                             },
16769
16770                             {
16771                                 tag : 'td',
16772                                 cls : 'fc-header-center',
16773                                 cn : [
16774                                     {
16775                                         tag: 'span',
16776                                         cls: 'fc-header-title',
16777                                         cn : {
16778                                             tag: 'H2',
16779                                             html : 'month / year'
16780                                         }
16781                                     }
16782
16783                                 ]
16784                             },
16785                             {
16786                                 tag : 'td',
16787                                 cls : 'fc-header-right',
16788                                 cn : [
16789                               /*      fc_button('month', 'left', '', 'month' ),
16790                                     fc_button('week', '', '', 'week' ),
16791                                     fc_button('day', 'right', '', 'day' )
16792                                 */    
16793
16794                                 ]
16795                             }
16796
16797                         ]
16798                     }
16799                 ]
16800             };
16801         }
16802         
16803         header = this.header;
16804         
16805        
16806         var cal_heads = function() {
16807             var ret = [];
16808             // fixme - handle this.
16809             
16810             for (var i =0; i < Date.dayNames.length; i++) {
16811                 var d = Date.dayNames[i];
16812                 ret.push({
16813                     tag: 'th',
16814                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16815                     html : d.substring(0,3)
16816                 });
16817                 
16818             }
16819             ret[0].cls += ' fc-first';
16820             ret[6].cls += ' fc-last';
16821             return ret;
16822         };
16823         var cal_cell = function(n) {
16824             return  {
16825                 tag: 'td',
16826                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16827                 cn : [
16828                     {
16829                         cn : [
16830                             {
16831                                 cls: 'fc-day-number',
16832                                 html: 'D'
16833                             },
16834                             {
16835                                 cls: 'fc-day-content',
16836                              
16837                                 cn : [
16838                                      {
16839                                         style: 'position: relative;' // height: 17px;
16840                                     }
16841                                 ]
16842                             }
16843                             
16844                             
16845                         ]
16846                     }
16847                 ]
16848                 
16849             }
16850         };
16851         var cal_rows = function() {
16852             
16853             var ret = [];
16854             for (var r = 0; r < 6; r++) {
16855                 var row= {
16856                     tag : 'tr',
16857                     cls : 'fc-week',
16858                     cn : []
16859                 };
16860                 
16861                 for (var i =0; i < Date.dayNames.length; i++) {
16862                     var d = Date.dayNames[i];
16863                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16864
16865                 }
16866                 row.cn[0].cls+=' fc-first';
16867                 row.cn[0].cn[0].style = 'min-height:90px';
16868                 row.cn[6].cls+=' fc-last';
16869                 ret.push(row);
16870                 
16871             }
16872             ret[0].cls += ' fc-first';
16873             ret[4].cls += ' fc-prev-last';
16874             ret[5].cls += ' fc-last';
16875             return ret;
16876             
16877         };
16878         
16879         var cal_table = {
16880             tag: 'table',
16881             cls: 'fc-border-separate',
16882             style : 'width:100%',
16883             cellspacing  : 0,
16884             cn : [
16885                 { 
16886                     tag: 'thead',
16887                     cn : [
16888                         { 
16889                             tag: 'tr',
16890                             cls : 'fc-first fc-last',
16891                             cn : cal_heads()
16892                         }
16893                     ]
16894                 },
16895                 { 
16896                     tag: 'tbody',
16897                     cn : cal_rows()
16898                 }
16899                   
16900             ]
16901         };
16902          
16903          var cfg = {
16904             cls : 'fc fc-ltr',
16905             cn : [
16906                 header,
16907                 {
16908                     cls : 'fc-content',
16909                     style : "position: relative;",
16910                     cn : [
16911                         {
16912                             cls : 'fc-view fc-view-month fc-grid',
16913                             style : 'position: relative',
16914                             unselectable : 'on',
16915                             cn : [
16916                                 {
16917                                     cls : 'fc-event-container',
16918                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16919                                 },
16920                                 cal_table
16921                             ]
16922                         }
16923                     ]
16924     
16925                 }
16926            ] 
16927             
16928         };
16929         
16930          
16931         
16932         return cfg;
16933     },
16934     
16935     
16936     initEvents : function()
16937     {
16938         if(!this.store){
16939             throw "can not find store for calendar";
16940         }
16941         
16942         var mark = {
16943             tag: "div",
16944             cls:"x-dlg-mask",
16945             style: "text-align:center",
16946             cn: [
16947                 {
16948                     tag: "div",
16949                     style: "background-color:white;width:50%;margin:250 auto",
16950                     cn: [
16951                         {
16952                             tag: "img",
16953                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16954                         },
16955                         {
16956                             tag: "span",
16957                             html: "Loading"
16958                         }
16959                         
16960                     ]
16961                 }
16962             ]
16963         };
16964         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16965         
16966         var size = this.el.select('.fc-content', true).first().getSize();
16967         this.maskEl.setSize(size.width, size.height);
16968         this.maskEl.enableDisplayMode("block");
16969         if(!this.loadMask){
16970             this.maskEl.hide();
16971         }
16972         
16973         this.store = Roo.factory(this.store, Roo.data);
16974         this.store.on('load', this.onLoad, this);
16975         this.store.on('beforeload', this.onBeforeLoad, this);
16976         
16977         this.resize();
16978         
16979         this.cells = this.el.select('.fc-day',true);
16980         //Roo.log(this.cells);
16981         this.textNodes = this.el.query('.fc-day-number');
16982         this.cells.addClassOnOver('fc-state-hover');
16983         
16984         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16985         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16986         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16987         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16988         
16989         this.on('monthchange', this.onMonthChange, this);
16990         
16991         this.update(new Date().clearTime());
16992     },
16993     
16994     resize : function() {
16995         var sz  = this.el.getSize();
16996         
16997         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16998         this.el.select('.fc-day-content div',true).setHeight(34);
16999     },
17000     
17001     
17002     // private
17003     showPrevMonth : function(e){
17004         this.update(this.activeDate.add("mo", -1));
17005     },
17006     showToday : function(e){
17007         this.update(new Date().clearTime());
17008     },
17009     // private
17010     showNextMonth : function(e){
17011         this.update(this.activeDate.add("mo", 1));
17012     },
17013
17014     // private
17015     showPrevYear : function(){
17016         this.update(this.activeDate.add("y", -1));
17017     },
17018
17019     // private
17020     showNextYear : function(){
17021         this.update(this.activeDate.add("y", 1));
17022     },
17023
17024     
17025    // private
17026     update : function(date)
17027     {
17028         var vd = this.activeDate;
17029         this.activeDate = date;
17030 //        if(vd && this.el){
17031 //            var t = date.getTime();
17032 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17033 //                Roo.log('using add remove');
17034 //                
17035 //                this.fireEvent('monthchange', this, date);
17036 //                
17037 //                this.cells.removeClass("fc-state-highlight");
17038 //                this.cells.each(function(c){
17039 //                   if(c.dateValue == t){
17040 //                       c.addClass("fc-state-highlight");
17041 //                       setTimeout(function(){
17042 //                            try{c.dom.firstChild.focus();}catch(e){}
17043 //                       }, 50);
17044 //                       return false;
17045 //                   }
17046 //                   return true;
17047 //                });
17048 //                return;
17049 //            }
17050 //        }
17051         
17052         var days = date.getDaysInMonth();
17053         
17054         var firstOfMonth = date.getFirstDateOfMonth();
17055         var startingPos = firstOfMonth.getDay()-this.startDay;
17056         
17057         if(startingPos < this.startDay){
17058             startingPos += 7;
17059         }
17060         
17061         var pm = date.add(Date.MONTH, -1);
17062         var prevStart = pm.getDaysInMonth()-startingPos;
17063 //        
17064         this.cells = this.el.select('.fc-day',true);
17065         this.textNodes = this.el.query('.fc-day-number');
17066         this.cells.addClassOnOver('fc-state-hover');
17067         
17068         var cells = this.cells.elements;
17069         var textEls = this.textNodes;
17070         
17071         Roo.each(cells, function(cell){
17072             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17073         });
17074         
17075         days += startingPos;
17076
17077         // convert everything to numbers so it's fast
17078         var day = 86400000;
17079         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17080         //Roo.log(d);
17081         //Roo.log(pm);
17082         //Roo.log(prevStart);
17083         
17084         var today = new Date().clearTime().getTime();
17085         var sel = date.clearTime().getTime();
17086         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17087         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17088         var ddMatch = this.disabledDatesRE;
17089         var ddText = this.disabledDatesText;
17090         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17091         var ddaysText = this.disabledDaysText;
17092         var format = this.format;
17093         
17094         var setCellClass = function(cal, cell){
17095             cell.row = 0;
17096             cell.events = [];
17097             cell.more = [];
17098             //Roo.log('set Cell Class');
17099             cell.title = "";
17100             var t = d.getTime();
17101             
17102             //Roo.log(d);
17103             
17104             cell.dateValue = t;
17105             if(t == today){
17106                 cell.className += " fc-today";
17107                 cell.className += " fc-state-highlight";
17108                 cell.title = cal.todayText;
17109             }
17110             if(t == sel){
17111                 // disable highlight in other month..
17112                 //cell.className += " fc-state-highlight";
17113                 
17114             }
17115             // disabling
17116             if(t < min) {
17117                 cell.className = " fc-state-disabled";
17118                 cell.title = cal.minText;
17119                 return;
17120             }
17121             if(t > max) {
17122                 cell.className = " fc-state-disabled";
17123                 cell.title = cal.maxText;
17124                 return;
17125             }
17126             if(ddays){
17127                 if(ddays.indexOf(d.getDay()) != -1){
17128                     cell.title = ddaysText;
17129                     cell.className = " fc-state-disabled";
17130                 }
17131             }
17132             if(ddMatch && format){
17133                 var fvalue = d.dateFormat(format);
17134                 if(ddMatch.test(fvalue)){
17135                     cell.title = ddText.replace("%0", fvalue);
17136                     cell.className = " fc-state-disabled";
17137                 }
17138             }
17139             
17140             if (!cell.initialClassName) {
17141                 cell.initialClassName = cell.dom.className;
17142             }
17143             
17144             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17145         };
17146
17147         var i = 0;
17148         
17149         for(; i < startingPos; i++) {
17150             textEls[i].innerHTML = (++prevStart);
17151             d.setDate(d.getDate()+1);
17152             
17153             cells[i].className = "fc-past fc-other-month";
17154             setCellClass(this, cells[i]);
17155         }
17156         
17157         var intDay = 0;
17158         
17159         for(; i < days; i++){
17160             intDay = i - startingPos + 1;
17161             textEls[i].innerHTML = (intDay);
17162             d.setDate(d.getDate()+1);
17163             
17164             cells[i].className = ''; // "x-date-active";
17165             setCellClass(this, cells[i]);
17166         }
17167         var extraDays = 0;
17168         
17169         for(; i < 42; i++) {
17170             textEls[i].innerHTML = (++extraDays);
17171             d.setDate(d.getDate()+1);
17172             
17173             cells[i].className = "fc-future fc-other-month";
17174             setCellClass(this, cells[i]);
17175         }
17176         
17177         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17178         
17179         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17180         
17181         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17182         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17183         
17184         if(totalRows != 6){
17185             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17186             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17187         }
17188         
17189         this.fireEvent('monthchange', this, date);
17190         
17191         
17192         /*
17193         if(!this.internalRender){
17194             var main = this.el.dom.firstChild;
17195             var w = main.offsetWidth;
17196             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17197             Roo.fly(main).setWidth(w);
17198             this.internalRender = true;
17199             // opera does not respect the auto grow header center column
17200             // then, after it gets a width opera refuses to recalculate
17201             // without a second pass
17202             if(Roo.isOpera && !this.secondPass){
17203                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17204                 this.secondPass = true;
17205                 this.update.defer(10, this, [date]);
17206             }
17207         }
17208         */
17209         
17210     },
17211     
17212     findCell : function(dt) {
17213         dt = dt.clearTime().getTime();
17214         var ret = false;
17215         this.cells.each(function(c){
17216             //Roo.log("check " +c.dateValue + '?=' + dt);
17217             if(c.dateValue == dt){
17218                 ret = c;
17219                 return false;
17220             }
17221             return true;
17222         });
17223         
17224         return ret;
17225     },
17226     
17227     findCells : function(ev) {
17228         var s = ev.start.clone().clearTime().getTime();
17229        // Roo.log(s);
17230         var e= ev.end.clone().clearTime().getTime();
17231        // Roo.log(e);
17232         var ret = [];
17233         this.cells.each(function(c){
17234              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17235             
17236             if(c.dateValue > e){
17237                 return ;
17238             }
17239             if(c.dateValue < s){
17240                 return ;
17241             }
17242             ret.push(c);
17243         });
17244         
17245         return ret;    
17246     },
17247     
17248 //    findBestRow: function(cells)
17249 //    {
17250 //        var ret = 0;
17251 //        
17252 //        for (var i =0 ; i < cells.length;i++) {
17253 //            ret  = Math.max(cells[i].rows || 0,ret);
17254 //        }
17255 //        return ret;
17256 //        
17257 //    },
17258     
17259     
17260     addItem : function(ev)
17261     {
17262         // look for vertical location slot in
17263         var cells = this.findCells(ev);
17264         
17265 //        ev.row = this.findBestRow(cells);
17266         
17267         // work out the location.
17268         
17269         var crow = false;
17270         var rows = [];
17271         for(var i =0; i < cells.length; i++) {
17272             
17273             cells[i].row = cells[0].row;
17274             
17275             if(i == 0){
17276                 cells[i].row = cells[i].row + 1;
17277             }
17278             
17279             if (!crow) {
17280                 crow = {
17281                     start : cells[i],
17282                     end :  cells[i]
17283                 };
17284                 continue;
17285             }
17286             if (crow.start.getY() == cells[i].getY()) {
17287                 // on same row.
17288                 crow.end = cells[i];
17289                 continue;
17290             }
17291             // different row.
17292             rows.push(crow);
17293             crow = {
17294                 start: cells[i],
17295                 end : cells[i]
17296             };
17297             
17298         }
17299         
17300         rows.push(crow);
17301         ev.els = [];
17302         ev.rows = rows;
17303         ev.cells = cells;
17304         
17305         cells[0].events.push(ev);
17306         
17307         this.calevents.push(ev);
17308     },
17309     
17310     clearEvents: function() {
17311         
17312         if(!this.calevents){
17313             return;
17314         }
17315         
17316         Roo.each(this.cells.elements, function(c){
17317             c.row = 0;
17318             c.events = [];
17319             c.more = [];
17320         });
17321         
17322         Roo.each(this.calevents, function(e) {
17323             Roo.each(e.els, function(el) {
17324                 el.un('mouseenter' ,this.onEventEnter, this);
17325                 el.un('mouseleave' ,this.onEventLeave, this);
17326                 el.remove();
17327             },this);
17328         },this);
17329         
17330         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17331             e.remove();
17332         });
17333         
17334     },
17335     
17336     renderEvents: function()
17337     {   
17338         var _this = this;
17339         
17340         this.cells.each(function(c) {
17341             
17342             if(c.row < 5){
17343                 return;
17344             }
17345             
17346             var ev = c.events;
17347             
17348             var r = 4;
17349             if(c.row != c.events.length){
17350                 r = 4 - (4 - (c.row - c.events.length));
17351             }
17352             
17353             c.events = ev.slice(0, r);
17354             c.more = ev.slice(r);
17355             
17356             if(c.more.length && c.more.length == 1){
17357                 c.events.push(c.more.pop());
17358             }
17359             
17360             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17361             
17362         });
17363             
17364         this.cells.each(function(c) {
17365             
17366             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17367             
17368             
17369             for (var e = 0; e < c.events.length; e++){
17370                 var ev = c.events[e];
17371                 var rows = ev.rows;
17372                 
17373                 for(var i = 0; i < rows.length; i++) {
17374                 
17375                     // how many rows should it span..
17376
17377                     var  cfg = {
17378                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17379                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17380
17381                         unselectable : "on",
17382                         cn : [
17383                             {
17384                                 cls: 'fc-event-inner',
17385                                 cn : [
17386     //                                {
17387     //                                  tag:'span',
17388     //                                  cls: 'fc-event-time',
17389     //                                  html : cells.length > 1 ? '' : ev.time
17390     //                                },
17391                                     {
17392                                       tag:'span',
17393                                       cls: 'fc-event-title',
17394                                       html : String.format('{0}', ev.title)
17395                                     }
17396
17397
17398                                 ]
17399                             },
17400                             {
17401                                 cls: 'ui-resizable-handle ui-resizable-e',
17402                                 html : '&nbsp;&nbsp;&nbsp'
17403                             }
17404
17405                         ]
17406                     };
17407
17408                     if (i == 0) {
17409                         cfg.cls += ' fc-event-start';
17410                     }
17411                     if ((i+1) == rows.length) {
17412                         cfg.cls += ' fc-event-end';
17413                     }
17414
17415                     var ctr = _this.el.select('.fc-event-container',true).first();
17416                     var cg = ctr.createChild(cfg);
17417
17418                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17419                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17420
17421                     var r = (c.more.length) ? 1 : 0;
17422                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17423                     cg.setWidth(ebox.right - sbox.x -2);
17424
17425                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17426                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17427                     cg.on('click', _this.onEventClick, _this, ev);
17428
17429                     ev.els.push(cg);
17430                     
17431                 }
17432                 
17433             }
17434             
17435             
17436             if(c.more.length){
17437                 var  cfg = {
17438                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17439                     style : 'position: absolute',
17440                     unselectable : "on",
17441                     cn : [
17442                         {
17443                             cls: 'fc-event-inner',
17444                             cn : [
17445                                 {
17446                                   tag:'span',
17447                                   cls: 'fc-event-title',
17448                                   html : 'More'
17449                                 }
17450
17451
17452                             ]
17453                         },
17454                         {
17455                             cls: 'ui-resizable-handle ui-resizable-e',
17456                             html : '&nbsp;&nbsp;&nbsp'
17457                         }
17458
17459                     ]
17460                 };
17461
17462                 var ctr = _this.el.select('.fc-event-container',true).first();
17463                 var cg = ctr.createChild(cfg);
17464
17465                 var sbox = c.select('.fc-day-content',true).first().getBox();
17466                 var ebox = c.select('.fc-day-content',true).first().getBox();
17467                 //Roo.log(cg);
17468                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17469                 cg.setWidth(ebox.right - sbox.x -2);
17470
17471                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17472                 
17473             }
17474             
17475         });
17476         
17477         
17478         
17479     },
17480     
17481     onEventEnter: function (e, el,event,d) {
17482         this.fireEvent('evententer', this, el, event);
17483     },
17484     
17485     onEventLeave: function (e, el,event,d) {
17486         this.fireEvent('eventleave', this, el, event);
17487     },
17488     
17489     onEventClick: function (e, el,event,d) {
17490         this.fireEvent('eventclick', this, el, event);
17491     },
17492     
17493     onMonthChange: function () {
17494         this.store.load();
17495     },
17496     
17497     onMoreEventClick: function(e, el, more)
17498     {
17499         var _this = this;
17500         
17501         this.calpopover.placement = 'right';
17502         this.calpopover.setTitle('More');
17503         
17504         this.calpopover.setContent('');
17505         
17506         var ctr = this.calpopover.el.select('.popover-content', true).first();
17507         
17508         Roo.each(more, function(m){
17509             var cfg = {
17510                 cls : 'fc-event-hori fc-event-draggable',
17511                 html : m.title
17512             };
17513             var cg = ctr.createChild(cfg);
17514             
17515             cg.on('click', _this.onEventClick, _this, m);
17516         });
17517         
17518         this.calpopover.show(el);
17519         
17520         
17521     },
17522     
17523     onLoad: function () 
17524     {   
17525         this.calevents = [];
17526         var cal = this;
17527         
17528         if(this.store.getCount() > 0){
17529             this.store.data.each(function(d){
17530                cal.addItem({
17531                     id : d.data.id,
17532                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17533                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17534                     time : d.data.start_time,
17535                     title : d.data.title,
17536                     description : d.data.description,
17537                     venue : d.data.venue
17538                 });
17539             });
17540         }
17541         
17542         this.renderEvents();
17543         
17544         if(this.calevents.length && this.loadMask){
17545             this.maskEl.hide();
17546         }
17547     },
17548     
17549     onBeforeLoad: function()
17550     {
17551         this.clearEvents();
17552         if(this.loadMask){
17553             this.maskEl.show();
17554         }
17555     }
17556 });
17557
17558  
17559  /*
17560  * - LGPL
17561  *
17562  * element
17563  * 
17564  */
17565
17566 /**
17567  * @class Roo.bootstrap.Popover
17568  * @extends Roo.bootstrap.Component
17569  * Bootstrap Popover class
17570  * @cfg {String} html contents of the popover   (or false to use children..)
17571  * @cfg {String} title of popover (or false to hide)
17572  * @cfg {String} placement how it is placed
17573  * @cfg {String} trigger click || hover (or false to trigger manually)
17574  * @cfg {String} over what (parent or false to trigger manually.)
17575  * @cfg {Number} delay - delay before showing
17576  
17577  * @constructor
17578  * Create a new Popover
17579  * @param {Object} config The config object
17580  */
17581
17582 Roo.bootstrap.Popover = function(config){
17583     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17584     
17585     this.addEvents({
17586         // raw events
17587          /**
17588          * @event show
17589          * After the popover show
17590          * 
17591          * @param {Roo.bootstrap.Popover} this
17592          */
17593         "show" : true,
17594         /**
17595          * @event hide
17596          * After the popover hide
17597          * 
17598          * @param {Roo.bootstrap.Popover} this
17599          */
17600         "hide" : true
17601     });
17602 };
17603
17604 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17605     
17606     title: 'Fill in a title',
17607     html: false,
17608     
17609     placement : 'right',
17610     trigger : 'hover', // hover
17611     
17612     delay : 0,
17613     
17614     over: 'parent',
17615     
17616     can_build_overlaid : false,
17617     
17618     getChildContainer : function()
17619     {
17620         return this.el.select('.popover-content',true).first();
17621     },
17622     
17623     getAutoCreate : function(){
17624          
17625         var cfg = {
17626            cls : 'popover roo-dynamic',
17627            style: 'display:block',
17628            cn : [
17629                 {
17630                     cls : 'arrow'
17631                 },
17632                 {
17633                     cls : 'popover-inner',
17634                     cn : [
17635                         {
17636                             tag: 'h3',
17637                             cls: 'popover-title',
17638                             html : this.title
17639                         },
17640                         {
17641                             cls : 'popover-content',
17642                             html : this.html
17643                         }
17644                     ]
17645                     
17646                 }
17647            ]
17648         };
17649         
17650         return cfg;
17651     },
17652     setTitle: function(str)
17653     {
17654         this.title = str;
17655         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17656     },
17657     setContent: function(str)
17658     {
17659         this.html = str;
17660         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17661     },
17662     // as it get's added to the bottom of the page.
17663     onRender : function(ct, position)
17664     {
17665         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17666         if(!this.el){
17667             var cfg = Roo.apply({},  this.getAutoCreate());
17668             cfg.id = Roo.id();
17669             
17670             if (this.cls) {
17671                 cfg.cls += ' ' + this.cls;
17672             }
17673             if (this.style) {
17674                 cfg.style = this.style;
17675             }
17676             //Roo.log("adding to ");
17677             this.el = Roo.get(document.body).createChild(cfg, position);
17678 //            Roo.log(this.el);
17679         }
17680         this.initEvents();
17681     },
17682     
17683     initEvents : function()
17684     {
17685         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17686         this.el.enableDisplayMode('block');
17687         this.el.hide();
17688         if (this.over === false) {
17689             return; 
17690         }
17691         if (this.triggers === false) {
17692             return;
17693         }
17694         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17695         var triggers = this.trigger ? this.trigger.split(' ') : [];
17696         Roo.each(triggers, function(trigger) {
17697         
17698             if (trigger == 'click') {
17699                 on_el.on('click', this.toggle, this);
17700             } else if (trigger != 'manual') {
17701                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17702                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17703       
17704                 on_el.on(eventIn  ,this.enter, this);
17705                 on_el.on(eventOut, this.leave, this);
17706             }
17707         }, this);
17708         
17709     },
17710     
17711     
17712     // private
17713     timeout : null,
17714     hoverState : null,
17715     
17716     toggle : function () {
17717         this.hoverState == 'in' ? this.leave() : this.enter();
17718     },
17719     
17720     enter : function () {
17721         
17722         clearTimeout(this.timeout);
17723     
17724         this.hoverState = 'in';
17725     
17726         if (!this.delay || !this.delay.show) {
17727             this.show();
17728             return;
17729         }
17730         var _t = this;
17731         this.timeout = setTimeout(function () {
17732             if (_t.hoverState == 'in') {
17733                 _t.show();
17734             }
17735         }, this.delay.show)
17736     },
17737     
17738     leave : function() {
17739         clearTimeout(this.timeout);
17740     
17741         this.hoverState = 'out';
17742     
17743         if (!this.delay || !this.delay.hide) {
17744             this.hide();
17745             return;
17746         }
17747         var _t = this;
17748         this.timeout = setTimeout(function () {
17749             if (_t.hoverState == 'out') {
17750                 _t.hide();
17751             }
17752         }, this.delay.hide)
17753     },
17754     
17755     show : function (on_el)
17756     {
17757         if (!on_el) {
17758             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17759         }
17760         
17761         // set content.
17762         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17763         if (this.html !== false) {
17764             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17765         }
17766         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17767         if (!this.title.length) {
17768             this.el.select('.popover-title',true).hide();
17769         }
17770         
17771         var placement = typeof this.placement == 'function' ?
17772             this.placement.call(this, this.el, on_el) :
17773             this.placement;
17774             
17775         var autoToken = /\s?auto?\s?/i;
17776         var autoPlace = autoToken.test(placement);
17777         if (autoPlace) {
17778             placement = placement.replace(autoToken, '') || 'top';
17779         }
17780         
17781         //this.el.detach()
17782         //this.el.setXY([0,0]);
17783         this.el.show();
17784         this.el.dom.style.display='block';
17785         this.el.addClass(placement);
17786         
17787         //this.el.appendTo(on_el);
17788         
17789         var p = this.getPosition();
17790         var box = this.el.getBox();
17791         
17792         if (autoPlace) {
17793             // fixme..
17794         }
17795         var align = Roo.bootstrap.Popover.alignment[placement];
17796         
17797 //        Roo.log(align);
17798         this.el.alignTo(on_el, align[0],align[1]);
17799         //var arrow = this.el.select('.arrow',true).first();
17800         //arrow.set(align[2], 
17801         
17802         this.el.addClass('in');
17803         
17804         
17805         if (this.el.hasClass('fade')) {
17806             // fade it?
17807         }
17808         
17809         this.hoverState = 'in';
17810         
17811         this.fireEvent('show', this);
17812         
17813     },
17814     hide : function()
17815     {
17816         this.el.setXY([0,0]);
17817         this.el.removeClass('in');
17818         this.el.hide();
17819         this.hoverState = null;
17820         
17821         this.fireEvent('hide', this);
17822     }
17823     
17824 });
17825
17826 Roo.bootstrap.Popover.alignment = {
17827     'left' : ['r-l', [-10,0], 'right'],
17828     'right' : ['l-r', [10,0], 'left'],
17829     'bottom' : ['t-b', [0,10], 'top'],
17830     'top' : [ 'b-t', [0,-10], 'bottom']
17831 };
17832
17833  /*
17834  * - LGPL
17835  *
17836  * Progress
17837  * 
17838  */
17839
17840 /**
17841  * @class Roo.bootstrap.Progress
17842  * @extends Roo.bootstrap.Component
17843  * Bootstrap Progress class
17844  * @cfg {Boolean} striped striped of the progress bar
17845  * @cfg {Boolean} active animated of the progress bar
17846  * 
17847  * 
17848  * @constructor
17849  * Create a new Progress
17850  * @param {Object} config The config object
17851  */
17852
17853 Roo.bootstrap.Progress = function(config){
17854     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17855 };
17856
17857 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17858     
17859     striped : false,
17860     active: false,
17861     
17862     getAutoCreate : function(){
17863         var cfg = {
17864             tag: 'div',
17865             cls: 'progress'
17866         };
17867         
17868         
17869         if(this.striped){
17870             cfg.cls += ' progress-striped';
17871         }
17872       
17873         if(this.active){
17874             cfg.cls += ' active';
17875         }
17876         
17877         
17878         return cfg;
17879     }
17880    
17881 });
17882
17883  
17884
17885  /*
17886  * - LGPL
17887  *
17888  * ProgressBar
17889  * 
17890  */
17891
17892 /**
17893  * @class Roo.bootstrap.ProgressBar
17894  * @extends Roo.bootstrap.Component
17895  * Bootstrap ProgressBar class
17896  * @cfg {Number} aria_valuenow aria-value now
17897  * @cfg {Number} aria_valuemin aria-value min
17898  * @cfg {Number} aria_valuemax aria-value max
17899  * @cfg {String} label label for the progress bar
17900  * @cfg {String} panel (success | info | warning | danger )
17901  * @cfg {String} role role of the progress bar
17902  * @cfg {String} sr_only text
17903  * 
17904  * 
17905  * @constructor
17906  * Create a new ProgressBar
17907  * @param {Object} config The config object
17908  */
17909
17910 Roo.bootstrap.ProgressBar = function(config){
17911     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17912 };
17913
17914 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17915     
17916     aria_valuenow : 0,
17917     aria_valuemin : 0,
17918     aria_valuemax : 100,
17919     label : false,
17920     panel : false,
17921     role : false,
17922     sr_only: false,
17923     
17924     getAutoCreate : function()
17925     {
17926         
17927         var cfg = {
17928             tag: 'div',
17929             cls: 'progress-bar',
17930             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17931         };
17932         
17933         if(this.sr_only){
17934             cfg.cn = {
17935                 tag: 'span',
17936                 cls: 'sr-only',
17937                 html: this.sr_only
17938             }
17939         }
17940         
17941         if(this.role){
17942             cfg.role = this.role;
17943         }
17944         
17945         if(this.aria_valuenow){
17946             cfg['aria-valuenow'] = this.aria_valuenow;
17947         }
17948         
17949         if(this.aria_valuemin){
17950             cfg['aria-valuemin'] = this.aria_valuemin;
17951         }
17952         
17953         if(this.aria_valuemax){
17954             cfg['aria-valuemax'] = this.aria_valuemax;
17955         }
17956         
17957         if(this.label && !this.sr_only){
17958             cfg.html = this.label;
17959         }
17960         
17961         if(this.panel){
17962             cfg.cls += ' progress-bar-' + this.panel;
17963         }
17964         
17965         return cfg;
17966     },
17967     
17968     update : function(aria_valuenow)
17969     {
17970         this.aria_valuenow = aria_valuenow;
17971         
17972         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17973     }
17974    
17975 });
17976
17977  
17978
17979  /*
17980  * - LGPL
17981  *
17982  * column
17983  * 
17984  */
17985
17986 /**
17987  * @class Roo.bootstrap.TabGroup
17988  * @extends Roo.bootstrap.Column
17989  * Bootstrap Column class
17990  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17991  * @cfg {Boolean} carousel true to make the group behave like a carousel
17992  * @cfg {Boolean} bullets show bullets for the panels
17993  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17994  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17995  * @cfg {Boolean} showarrow (true|false) show arrow default true
17996  * 
17997  * @constructor
17998  * Create a new TabGroup
17999  * @param {Object} config The config object
18000  */
18001
18002 Roo.bootstrap.TabGroup = function(config){
18003     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18004     if (!this.navId) {
18005         this.navId = Roo.id();
18006     }
18007     this.tabs = [];
18008     Roo.bootstrap.TabGroup.register(this);
18009     
18010 };
18011
18012 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18013     
18014     carousel : false,
18015     transition : false,
18016     bullets : 0,
18017     timer : 0,
18018     autoslide : false,
18019     slideFn : false,
18020     slideOnTouch : false,
18021     showarrow : true,
18022     
18023     getAutoCreate : function()
18024     {
18025         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18026         
18027         cfg.cls += ' tab-content';
18028         
18029         if (this.carousel) {
18030             cfg.cls += ' carousel slide';
18031             
18032             cfg.cn = [{
18033                cls : 'carousel-inner',
18034                cn : []
18035             }];
18036         
18037             if(this.bullets  && !Roo.isTouch){
18038                 
18039                 var bullets = {
18040                     cls : 'carousel-bullets',
18041                     cn : []
18042                 };
18043                
18044                 if(this.bullets_cls){
18045                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18046                 }
18047                 
18048                 bullets.cn.push({
18049                     cls : 'clear'
18050                 });
18051                 
18052                 cfg.cn[0].cn.push(bullets);
18053             }
18054             
18055             if(this.showarrow){
18056                 cfg.cn[0].cn.push({
18057                     tag : 'div',
18058                     class : 'carousel-arrow',
18059                     cn : [
18060                         {
18061                             tag : 'div',
18062                             class : 'carousel-prev',
18063                             cn : [
18064                                 {
18065                                     tag : 'i',
18066                                     class : 'fa fa-chevron-left'
18067                                 }
18068                             ]
18069                         },
18070                         {
18071                             tag : 'div',
18072                             class : 'carousel-next',
18073                             cn : [
18074                                 {
18075                                     tag : 'i',
18076                                     class : 'fa fa-chevron-right'
18077                                 }
18078                             ]
18079                         }
18080                     ]
18081                 });
18082             }
18083             
18084         }
18085         
18086         return cfg;
18087     },
18088     
18089     initEvents:  function()
18090     {
18091 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18092 //            this.el.on("touchstart", this.onTouchStart, this);
18093 //        }
18094         
18095         if(this.autoslide){
18096             var _this = this;
18097             
18098             this.slideFn = window.setInterval(function() {
18099                 _this.showPanelNext();
18100             }, this.timer);
18101         }
18102         
18103         if(this.showarrow){
18104             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18105             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18106         }
18107         
18108         
18109     },
18110     
18111 //    onTouchStart : function(e, el, o)
18112 //    {
18113 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18114 //            return;
18115 //        }
18116 //        
18117 //        this.showPanelNext();
18118 //    },
18119     
18120     
18121     getChildContainer : function()
18122     {
18123         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18124     },
18125     
18126     /**
18127     * register a Navigation item
18128     * @param {Roo.bootstrap.NavItem} the navitem to add
18129     */
18130     register : function(item)
18131     {
18132         this.tabs.push( item);
18133         item.navId = this.navId; // not really needed..
18134         this.addBullet();
18135     
18136     },
18137     
18138     getActivePanel : function()
18139     {
18140         var r = false;
18141         Roo.each(this.tabs, function(t) {
18142             if (t.active) {
18143                 r = t;
18144                 return false;
18145             }
18146             return null;
18147         });
18148         return r;
18149         
18150     },
18151     getPanelByName : function(n)
18152     {
18153         var r = false;
18154         Roo.each(this.tabs, function(t) {
18155             if (t.tabId == n) {
18156                 r = t;
18157                 return false;
18158             }
18159             return null;
18160         });
18161         return r;
18162     },
18163     indexOfPanel : function(p)
18164     {
18165         var r = false;
18166         Roo.each(this.tabs, function(t,i) {
18167             if (t.tabId == p.tabId) {
18168                 r = i;
18169                 return false;
18170             }
18171             return null;
18172         });
18173         return r;
18174     },
18175     /**
18176      * show a specific panel
18177      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18178      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18179      */
18180     showPanel : function (pan)
18181     {
18182         if(this.transition || typeof(pan) == 'undefined'){
18183             Roo.log("waiting for the transitionend");
18184             return;
18185         }
18186         
18187         if (typeof(pan) == 'number') {
18188             pan = this.tabs[pan];
18189         }
18190         
18191         if (typeof(pan) == 'string') {
18192             pan = this.getPanelByName(pan);
18193         }
18194         
18195         var cur = this.getActivePanel();
18196         
18197         if(!pan || !cur){
18198             Roo.log('pan or acitve pan is undefined');
18199             return false;
18200         }
18201         
18202         if (pan.tabId == this.getActivePanel().tabId) {
18203             return true;
18204         }
18205         
18206         if (false === cur.fireEvent('beforedeactivate')) {
18207             return false;
18208         }
18209         
18210         if(this.bullets > 0 && !Roo.isTouch){
18211             this.setActiveBullet(this.indexOfPanel(pan));
18212         }
18213         
18214         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18215             
18216             this.transition = true;
18217             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18218             var lr = dir == 'next' ? 'left' : 'right';
18219             pan.el.addClass(dir); // or prev
18220             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18221             cur.el.addClass(lr); // or right
18222             pan.el.addClass(lr);
18223             
18224             var _this = this;
18225             cur.el.on('transitionend', function() {
18226                 Roo.log("trans end?");
18227                 
18228                 pan.el.removeClass([lr,dir]);
18229                 pan.setActive(true);
18230                 
18231                 cur.el.removeClass([lr]);
18232                 cur.setActive(false);
18233                 
18234                 _this.transition = false;
18235                 
18236             }, this, { single:  true } );
18237             
18238             return true;
18239         }
18240         
18241         cur.setActive(false);
18242         pan.setActive(true);
18243         
18244         return true;
18245         
18246     },
18247     showPanelNext : function()
18248     {
18249         var i = this.indexOfPanel(this.getActivePanel());
18250         
18251         if (i >= this.tabs.length - 1 && !this.autoslide) {
18252             return;
18253         }
18254         
18255         if (i >= this.tabs.length - 1 && this.autoslide) {
18256             i = -1;
18257         }
18258         
18259         this.showPanel(this.tabs[i+1]);
18260     },
18261     
18262     showPanelPrev : function()
18263     {
18264         var i = this.indexOfPanel(this.getActivePanel());
18265         
18266         if (i  < 1 && !this.autoslide) {
18267             return;
18268         }
18269         
18270         if (i < 1 && this.autoslide) {
18271             i = this.tabs.length;
18272         }
18273         
18274         this.showPanel(this.tabs[i-1]);
18275     },
18276     
18277     
18278     addBullet: function()
18279     {
18280         if(!this.bullets || Roo.isTouch){
18281             return;
18282         }
18283         var ctr = this.el.select('.carousel-bullets',true).first();
18284         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18285         var bullet = ctr.createChild({
18286             cls : 'bullet bullet-' + i
18287         },ctr.dom.lastChild);
18288         
18289         
18290         var _this = this;
18291         
18292         bullet.on('click', (function(e, el, o, ii, t){
18293
18294             e.preventDefault();
18295
18296             this.showPanel(ii);
18297
18298             if(this.autoslide && this.slideFn){
18299                 clearInterval(this.slideFn);
18300                 this.slideFn = window.setInterval(function() {
18301                     _this.showPanelNext();
18302                 }, this.timer);
18303             }
18304
18305         }).createDelegate(this, [i, bullet], true));
18306                 
18307         
18308     },
18309      
18310     setActiveBullet : function(i)
18311     {
18312         if(Roo.isTouch){
18313             return;
18314         }
18315         
18316         Roo.each(this.el.select('.bullet', true).elements, function(el){
18317             el.removeClass('selected');
18318         });
18319
18320         var bullet = this.el.select('.bullet-' + i, true).first();
18321         
18322         if(!bullet){
18323             return;
18324         }
18325         
18326         bullet.addClass('selected');
18327     }
18328     
18329     
18330   
18331 });
18332
18333  
18334
18335  
18336  
18337 Roo.apply(Roo.bootstrap.TabGroup, {
18338     
18339     groups: {},
18340      /**
18341     * register a Navigation Group
18342     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18343     */
18344     register : function(navgrp)
18345     {
18346         this.groups[navgrp.navId] = navgrp;
18347         
18348     },
18349     /**
18350     * fetch a Navigation Group based on the navigation ID
18351     * if one does not exist , it will get created.
18352     * @param {string} the navgroup to add
18353     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18354     */
18355     get: function(navId) {
18356         if (typeof(this.groups[navId]) == 'undefined') {
18357             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18358         }
18359         return this.groups[navId] ;
18360     }
18361     
18362     
18363     
18364 });
18365
18366  /*
18367  * - LGPL
18368  *
18369  * TabPanel
18370  * 
18371  */
18372
18373 /**
18374  * @class Roo.bootstrap.TabPanel
18375  * @extends Roo.bootstrap.Component
18376  * Bootstrap TabPanel class
18377  * @cfg {Boolean} active panel active
18378  * @cfg {String} html panel content
18379  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18380  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18381  * @cfg {String} href click to link..
18382  * 
18383  * 
18384  * @constructor
18385  * Create a new TabPanel
18386  * @param {Object} config The config object
18387  */
18388
18389 Roo.bootstrap.TabPanel = function(config){
18390     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18391     this.addEvents({
18392         /**
18393              * @event changed
18394              * Fires when the active status changes
18395              * @param {Roo.bootstrap.TabPanel} this
18396              * @param {Boolean} state the new state
18397             
18398          */
18399         'changed': true,
18400         /**
18401              * @event beforedeactivate
18402              * Fires before a tab is de-activated - can be used to do validation on a form.
18403              * @param {Roo.bootstrap.TabPanel} this
18404              * @return {Boolean} false if there is an error
18405             
18406          */
18407         'beforedeactivate': true
18408      });
18409     
18410     this.tabId = this.tabId || Roo.id();
18411   
18412 };
18413
18414 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18415     
18416     active: false,
18417     html: false,
18418     tabId: false,
18419     navId : false,
18420     href : '',
18421     
18422     getAutoCreate : function(){
18423         var cfg = {
18424             tag: 'div',
18425             // item is needed for carousel - not sure if it has any effect otherwise
18426             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18427             html: this.html || ''
18428         };
18429         
18430         if(this.active){
18431             cfg.cls += ' active';
18432         }
18433         
18434         if(this.tabId){
18435             cfg.tabId = this.tabId;
18436         }
18437         
18438         
18439         return cfg;
18440     },
18441     
18442     initEvents:  function()
18443     {
18444         var p = this.parent();
18445         
18446         this.navId = this.navId || p.navId;
18447         
18448         if (typeof(this.navId) != 'undefined') {
18449             // not really needed.. but just in case.. parent should be a NavGroup.
18450             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18451             
18452             tg.register(this);
18453             
18454             var i = tg.tabs.length - 1;
18455             
18456             if(this.active && tg.bullets > 0 && i < tg.bullets){
18457                 tg.setActiveBullet(i);
18458             }
18459         }
18460         
18461         this.el.on('click', this.onClick, this);
18462         
18463         if(Roo.isTouch){
18464             this.el.on("touchstart", this.onTouchStart, this);
18465             this.el.on("touchmove", this.onTouchMove, this);
18466             this.el.on("touchend", this.onTouchEnd, this);
18467         }
18468         
18469     },
18470     
18471     onRender : function(ct, position)
18472     {
18473         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18474     },
18475     
18476     setActive : function(state)
18477     {
18478         Roo.log("panel - set active " + this.tabId + "=" + state);
18479         
18480         this.active = state;
18481         if (!state) {
18482             this.el.removeClass('active');
18483             
18484         } else  if (!this.el.hasClass('active')) {
18485             this.el.addClass('active');
18486         }
18487         
18488         this.fireEvent('changed', this, state);
18489     },
18490     
18491     onClick : function(e)
18492     {
18493         e.preventDefault();
18494         
18495         if(!this.href.length){
18496             return;
18497         }
18498         
18499         window.location.href = this.href;
18500     },
18501     
18502     startX : 0,
18503     startY : 0,
18504     endX : 0,
18505     endY : 0,
18506     swiping : false,
18507     
18508     onTouchStart : function(e)
18509     {
18510         this.swiping = false;
18511         
18512         this.startX = e.browserEvent.touches[0].clientX;
18513         this.startY = e.browserEvent.touches[0].clientY;
18514     },
18515     
18516     onTouchMove : function(e)
18517     {
18518         this.swiping = true;
18519         
18520         this.endX = e.browserEvent.touches[0].clientX;
18521         this.endY = e.browserEvent.touches[0].clientY;
18522     },
18523     
18524     onTouchEnd : function(e)
18525     {
18526         if(!this.swiping){
18527             this.onClick(e);
18528             return;
18529         }
18530         
18531         var tabGroup = this.parent();
18532         
18533         if(this.endX > this.startX){ // swiping right
18534             tabGroup.showPanelPrev();
18535             return;
18536         }
18537         
18538         if(this.startX > this.endX){ // swiping left
18539             tabGroup.showPanelNext();
18540             return;
18541         }
18542     }
18543     
18544     
18545 });
18546  
18547
18548  
18549
18550  /*
18551  * - LGPL
18552  *
18553  * DateField
18554  * 
18555  */
18556
18557 /**
18558  * @class Roo.bootstrap.DateField
18559  * @extends Roo.bootstrap.Input
18560  * Bootstrap DateField class
18561  * @cfg {Number} weekStart default 0
18562  * @cfg {String} viewMode default empty, (months|years)
18563  * @cfg {String} minViewMode default empty, (months|years)
18564  * @cfg {Number} startDate default -Infinity
18565  * @cfg {Number} endDate default Infinity
18566  * @cfg {Boolean} todayHighlight default false
18567  * @cfg {Boolean} todayBtn default false
18568  * @cfg {Boolean} calendarWeeks default false
18569  * @cfg {Object} daysOfWeekDisabled default empty
18570  * @cfg {Boolean} singleMode default false (true | false)
18571  * 
18572  * @cfg {Boolean} keyboardNavigation default true
18573  * @cfg {String} language default en
18574  * 
18575  * @constructor
18576  * Create a new DateField
18577  * @param {Object} config The config object
18578  */
18579
18580 Roo.bootstrap.DateField = function(config){
18581     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18582      this.addEvents({
18583             /**
18584              * @event show
18585              * Fires when this field show.
18586              * @param {Roo.bootstrap.DateField} this
18587              * @param {Mixed} date The date value
18588              */
18589             show : true,
18590             /**
18591              * @event show
18592              * Fires when this field hide.
18593              * @param {Roo.bootstrap.DateField} this
18594              * @param {Mixed} date The date value
18595              */
18596             hide : true,
18597             /**
18598              * @event select
18599              * Fires when select a date.
18600              * @param {Roo.bootstrap.DateField} this
18601              * @param {Mixed} date The date value
18602              */
18603             select : true,
18604             /**
18605              * @event beforeselect
18606              * Fires when before select a date.
18607              * @param {Roo.bootstrap.DateField} this
18608              * @param {Mixed} date The date value
18609              */
18610             beforeselect : true
18611         });
18612 };
18613
18614 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18615     
18616     /**
18617      * @cfg {String} format
18618      * The default date format string which can be overriden for localization support.  The format must be
18619      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18620      */
18621     format : "m/d/y",
18622     /**
18623      * @cfg {String} altFormats
18624      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18625      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18626      */
18627     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18628     
18629     weekStart : 0,
18630     
18631     viewMode : '',
18632     
18633     minViewMode : '',
18634     
18635     todayHighlight : false,
18636     
18637     todayBtn: false,
18638     
18639     language: 'en',
18640     
18641     keyboardNavigation: true,
18642     
18643     calendarWeeks: false,
18644     
18645     startDate: -Infinity,
18646     
18647     endDate: Infinity,
18648     
18649     daysOfWeekDisabled: [],
18650     
18651     _events: [],
18652     
18653     singleMode : false,
18654     
18655     UTCDate: function()
18656     {
18657         return new Date(Date.UTC.apply(Date, arguments));
18658     },
18659     
18660     UTCToday: function()
18661     {
18662         var today = new Date();
18663         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18664     },
18665     
18666     getDate: function() {
18667             var d = this.getUTCDate();
18668             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18669     },
18670     
18671     getUTCDate: function() {
18672             return this.date;
18673     },
18674     
18675     setDate: function(d) {
18676             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18677     },
18678     
18679     setUTCDate: function(d) {
18680             this.date = d;
18681             this.setValue(this.formatDate(this.date));
18682     },
18683         
18684     onRender: function(ct, position)
18685     {
18686         
18687         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18688         
18689         this.language = this.language || 'en';
18690         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18691         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18692         
18693         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18694         this.format = this.format || 'm/d/y';
18695         this.isInline = false;
18696         this.isInput = true;
18697         this.component = this.el.select('.add-on', true).first() || false;
18698         this.component = (this.component && this.component.length === 0) ? false : this.component;
18699         this.hasInput = this.component && this.inputEl().length;
18700         
18701         if (typeof(this.minViewMode === 'string')) {
18702             switch (this.minViewMode) {
18703                 case 'months':
18704                     this.minViewMode = 1;
18705                     break;
18706                 case 'years':
18707                     this.minViewMode = 2;
18708                     break;
18709                 default:
18710                     this.minViewMode = 0;
18711                     break;
18712             }
18713         }
18714         
18715         if (typeof(this.viewMode === 'string')) {
18716             switch (this.viewMode) {
18717                 case 'months':
18718                     this.viewMode = 1;
18719                     break;
18720                 case 'years':
18721                     this.viewMode = 2;
18722                     break;
18723                 default:
18724                     this.viewMode = 0;
18725                     break;
18726             }
18727         }
18728                 
18729         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18730         
18731 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18732         
18733         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18734         
18735         this.picker().on('mousedown', this.onMousedown, this);
18736         this.picker().on('click', this.onClick, this);
18737         
18738         this.picker().addClass('datepicker-dropdown');
18739         
18740         this.startViewMode = this.viewMode;
18741         
18742         if(this.singleMode){
18743             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18744                 v.setVisibilityMode(Roo.Element.DISPLAY);
18745                 v.hide();
18746             });
18747             
18748             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18749                 v.setStyle('width', '189px');
18750             });
18751         }
18752         
18753         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18754             if(!this.calendarWeeks){
18755                 v.remove();
18756                 return;
18757             }
18758             
18759             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18760             v.attr('colspan', function(i, val){
18761                 return parseInt(val) + 1;
18762             });
18763         });
18764                         
18765         
18766         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18767         
18768         this.setStartDate(this.startDate);
18769         this.setEndDate(this.endDate);
18770         
18771         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18772         
18773         this.fillDow();
18774         this.fillMonths();
18775         this.update();
18776         this.showMode();
18777         
18778         if(this.isInline) {
18779             this.showPopup();
18780         }
18781     },
18782     
18783     picker : function()
18784     {
18785         return this.pickerEl;
18786 //        return this.el.select('.datepicker', true).first();
18787     },
18788     
18789     fillDow: function()
18790     {
18791         var dowCnt = this.weekStart;
18792         
18793         var dow = {
18794             tag: 'tr',
18795             cn: [
18796                 
18797             ]
18798         };
18799         
18800         if(this.calendarWeeks){
18801             dow.cn.push({
18802                 tag: 'th',
18803                 cls: 'cw',
18804                 html: '&nbsp;'
18805             })
18806         }
18807         
18808         while (dowCnt < this.weekStart + 7) {
18809             dow.cn.push({
18810                 tag: 'th',
18811                 cls: 'dow',
18812                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18813             });
18814         }
18815         
18816         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18817     },
18818     
18819     fillMonths: function()
18820     {    
18821         var i = 0;
18822         var months = this.picker().select('>.datepicker-months td', true).first();
18823         
18824         months.dom.innerHTML = '';
18825         
18826         while (i < 12) {
18827             var month = {
18828                 tag: 'span',
18829                 cls: 'month',
18830                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18831             };
18832             
18833             months.createChild(month);
18834         }
18835         
18836     },
18837     
18838     update: function()
18839     {
18840         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;
18841         
18842         if (this.date < this.startDate) {
18843             this.viewDate = new Date(this.startDate);
18844         } else if (this.date > this.endDate) {
18845             this.viewDate = new Date(this.endDate);
18846         } else {
18847             this.viewDate = new Date(this.date);
18848         }
18849         
18850         this.fill();
18851     },
18852     
18853     fill: function() 
18854     {
18855         var d = new Date(this.viewDate),
18856                 year = d.getUTCFullYear(),
18857                 month = d.getUTCMonth(),
18858                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18859                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18860                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18861                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18862                 currentDate = this.date && this.date.valueOf(),
18863                 today = this.UTCToday();
18864         
18865         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18866         
18867 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18868         
18869 //        this.picker.select('>tfoot th.today').
18870 //                                              .text(dates[this.language].today)
18871 //                                              .toggle(this.todayBtn !== false);
18872     
18873         this.updateNavArrows();
18874         this.fillMonths();
18875                                                 
18876         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18877         
18878         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18879          
18880         prevMonth.setUTCDate(day);
18881         
18882         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18883         
18884         var nextMonth = new Date(prevMonth);
18885         
18886         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18887         
18888         nextMonth = nextMonth.valueOf();
18889         
18890         var fillMonths = false;
18891         
18892         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18893         
18894         while(prevMonth.valueOf() <= nextMonth) {
18895             var clsName = '';
18896             
18897             if (prevMonth.getUTCDay() === this.weekStart) {
18898                 if(fillMonths){
18899                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18900                 }
18901                     
18902                 fillMonths = {
18903                     tag: 'tr',
18904                     cn: []
18905                 };
18906                 
18907                 if(this.calendarWeeks){
18908                     // ISO 8601: First week contains first thursday.
18909                     // ISO also states week starts on Monday, but we can be more abstract here.
18910                     var
18911                     // Start of current week: based on weekstart/current date
18912                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18913                     // Thursday of this week
18914                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18915                     // First Thursday of year, year from thursday
18916                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18917                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18918                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18919                     
18920                     fillMonths.cn.push({
18921                         tag: 'td',
18922                         cls: 'cw',
18923                         html: calWeek
18924                     });
18925                 }
18926             }
18927             
18928             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18929                 clsName += ' old';
18930             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18931                 clsName += ' new';
18932             }
18933             if (this.todayHighlight &&
18934                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18935                 prevMonth.getUTCMonth() == today.getMonth() &&
18936                 prevMonth.getUTCDate() == today.getDate()) {
18937                 clsName += ' today';
18938             }
18939             
18940             if (currentDate && prevMonth.valueOf() === currentDate) {
18941                 clsName += ' active';
18942             }
18943             
18944             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18945                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18946                     clsName += ' disabled';
18947             }
18948             
18949             fillMonths.cn.push({
18950                 tag: 'td',
18951                 cls: 'day ' + clsName,
18952                 html: prevMonth.getDate()
18953             });
18954             
18955             prevMonth.setDate(prevMonth.getDate()+1);
18956         }
18957           
18958         var currentYear = this.date && this.date.getUTCFullYear();
18959         var currentMonth = this.date && this.date.getUTCMonth();
18960         
18961         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18962         
18963         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18964             v.removeClass('active');
18965             
18966             if(currentYear === year && k === currentMonth){
18967                 v.addClass('active');
18968             }
18969             
18970             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18971                 v.addClass('disabled');
18972             }
18973             
18974         });
18975         
18976         
18977         year = parseInt(year/10, 10) * 10;
18978         
18979         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18980         
18981         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18982         
18983         year -= 1;
18984         for (var i = -1; i < 11; i++) {
18985             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18986                 tag: 'span',
18987                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18988                 html: year
18989             });
18990             
18991             year += 1;
18992         }
18993     },
18994     
18995     showMode: function(dir) 
18996     {
18997         if (dir) {
18998             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18999         }
19000         
19001         Roo.each(this.picker().select('>div',true).elements, function(v){
19002             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19003             v.hide();
19004         });
19005         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19006     },
19007     
19008     place: function()
19009     {
19010         if(this.isInline) {
19011             return;
19012         }
19013         
19014         this.picker().removeClass(['bottom', 'top']);
19015         
19016         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19017             /*
19018              * place to the top of element!
19019              *
19020              */
19021             
19022             this.picker().addClass('top');
19023             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19024             
19025             return;
19026         }
19027         
19028         this.picker().addClass('bottom');
19029         
19030         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19031     },
19032     
19033     parseDate : function(value)
19034     {
19035         if(!value || value instanceof Date){
19036             return value;
19037         }
19038         var v = Date.parseDate(value, this.format);
19039         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19040             v = Date.parseDate(value, 'Y-m-d');
19041         }
19042         if(!v && this.altFormats){
19043             if(!this.altFormatsArray){
19044                 this.altFormatsArray = this.altFormats.split("|");
19045             }
19046             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19047                 v = Date.parseDate(value, this.altFormatsArray[i]);
19048             }
19049         }
19050         return v;
19051     },
19052     
19053     formatDate : function(date, fmt)
19054     {   
19055         return (!date || !(date instanceof Date)) ?
19056         date : date.dateFormat(fmt || this.format);
19057     },
19058     
19059     onFocus : function()
19060     {
19061         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19062         this.showPopup();
19063     },
19064     
19065     onBlur : function()
19066     {
19067         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19068         
19069         var d = this.inputEl().getValue();
19070         
19071         this.setValue(d);
19072                 
19073         this.hidePopup();
19074     },
19075     
19076     showPopup : function()
19077     {
19078         this.picker().show();
19079         this.update();
19080         this.place();
19081         
19082         this.fireEvent('showpopup', this, this.date);
19083     },
19084     
19085     hidePopup : function()
19086     {
19087         if(this.isInline) {
19088             return;
19089         }
19090         this.picker().hide();
19091         this.viewMode = this.startViewMode;
19092         this.showMode();
19093         
19094         this.fireEvent('hidepopup', this, this.date);
19095         
19096     },
19097     
19098     onMousedown: function(e)
19099     {
19100         e.stopPropagation();
19101         e.preventDefault();
19102     },
19103     
19104     keyup: function(e)
19105     {
19106         Roo.bootstrap.DateField.superclass.keyup.call(this);
19107         this.update();
19108     },
19109
19110     setValue: function(v)
19111     {
19112         if(this.fireEvent('beforeselect', this, v) !== false){
19113             var d = new Date(this.parseDate(v) ).clearTime();
19114         
19115             if(isNaN(d.getTime())){
19116                 this.date = this.viewDate = '';
19117                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19118                 return;
19119             }
19120
19121             v = this.formatDate(d);
19122
19123             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19124
19125             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19126
19127             this.update();
19128
19129             this.fireEvent('select', this, this.date);
19130         }
19131     },
19132     
19133     getValue: function()
19134     {
19135         return this.formatDate(this.date);
19136     },
19137     
19138     fireKey: function(e)
19139     {
19140         if (!this.picker().isVisible()){
19141             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19142                 this.showPopup();
19143             }
19144             return;
19145         }
19146         
19147         var dateChanged = false,
19148         dir, day, month,
19149         newDate, newViewDate;
19150         
19151         switch(e.keyCode){
19152             case 27: // escape
19153                 this.hidePopup();
19154                 e.preventDefault();
19155                 break;
19156             case 37: // left
19157             case 39: // right
19158                 if (!this.keyboardNavigation) {
19159                     break;
19160                 }
19161                 dir = e.keyCode == 37 ? -1 : 1;
19162                 
19163                 if (e.ctrlKey){
19164                     newDate = this.moveYear(this.date, dir);
19165                     newViewDate = this.moveYear(this.viewDate, dir);
19166                 } else if (e.shiftKey){
19167                     newDate = this.moveMonth(this.date, dir);
19168                     newViewDate = this.moveMonth(this.viewDate, dir);
19169                 } else {
19170                     newDate = new Date(this.date);
19171                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19172                     newViewDate = new Date(this.viewDate);
19173                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19174                 }
19175                 if (this.dateWithinRange(newDate)){
19176                     this.date = newDate;
19177                     this.viewDate = newViewDate;
19178                     this.setValue(this.formatDate(this.date));
19179 //                    this.update();
19180                     e.preventDefault();
19181                     dateChanged = true;
19182                 }
19183                 break;
19184             case 38: // up
19185             case 40: // down
19186                 if (!this.keyboardNavigation) {
19187                     break;
19188                 }
19189                 dir = e.keyCode == 38 ? -1 : 1;
19190                 if (e.ctrlKey){
19191                     newDate = this.moveYear(this.date, dir);
19192                     newViewDate = this.moveYear(this.viewDate, dir);
19193                 } else if (e.shiftKey){
19194                     newDate = this.moveMonth(this.date, dir);
19195                     newViewDate = this.moveMonth(this.viewDate, dir);
19196                 } else {
19197                     newDate = new Date(this.date);
19198                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19199                     newViewDate = new Date(this.viewDate);
19200                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19201                 }
19202                 if (this.dateWithinRange(newDate)){
19203                     this.date = newDate;
19204                     this.viewDate = newViewDate;
19205                     this.setValue(this.formatDate(this.date));
19206 //                    this.update();
19207                     e.preventDefault();
19208                     dateChanged = true;
19209                 }
19210                 break;
19211             case 13: // enter
19212                 this.setValue(this.formatDate(this.date));
19213                 this.hidePopup();
19214                 e.preventDefault();
19215                 break;
19216             case 9: // tab
19217                 this.setValue(this.formatDate(this.date));
19218                 this.hidePopup();
19219                 break;
19220             case 16: // shift
19221             case 17: // ctrl
19222             case 18: // alt
19223                 break;
19224             default :
19225                 this.hidePopup();
19226                 
19227         }
19228     },
19229     
19230     
19231     onClick: function(e) 
19232     {
19233         e.stopPropagation();
19234         e.preventDefault();
19235         
19236         var target = e.getTarget();
19237         
19238         if(target.nodeName.toLowerCase() === 'i'){
19239             target = Roo.get(target).dom.parentNode;
19240         }
19241         
19242         var nodeName = target.nodeName;
19243         var className = target.className;
19244         var html = target.innerHTML;
19245         //Roo.log(nodeName);
19246         
19247         switch(nodeName.toLowerCase()) {
19248             case 'th':
19249                 switch(className) {
19250                     case 'switch':
19251                         this.showMode(1);
19252                         break;
19253                     case 'prev':
19254                     case 'next':
19255                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19256                         switch(this.viewMode){
19257                                 case 0:
19258                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19259                                         break;
19260                                 case 1:
19261                                 case 2:
19262                                         this.viewDate = this.moveYear(this.viewDate, dir);
19263                                         break;
19264                         }
19265                         this.fill();
19266                         break;
19267                     case 'today':
19268                         var date = new Date();
19269                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19270 //                        this.fill()
19271                         this.setValue(this.formatDate(this.date));
19272                         
19273                         this.hidePopup();
19274                         break;
19275                 }
19276                 break;
19277             case 'span':
19278                 if (className.indexOf('disabled') < 0) {
19279                     this.viewDate.setUTCDate(1);
19280                     if (className.indexOf('month') > -1) {
19281                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19282                     } else {
19283                         var year = parseInt(html, 10) || 0;
19284                         this.viewDate.setUTCFullYear(year);
19285                         
19286                     }
19287                     
19288                     if(this.singleMode){
19289                         this.setValue(this.formatDate(this.viewDate));
19290                         this.hidePopup();
19291                         return;
19292                     }
19293                     
19294                     this.showMode(-1);
19295                     this.fill();
19296                 }
19297                 break;
19298                 
19299             case 'td':
19300                 //Roo.log(className);
19301                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19302                     var day = parseInt(html, 10) || 1;
19303                     var year = this.viewDate.getUTCFullYear(),
19304                         month = this.viewDate.getUTCMonth();
19305
19306                     if (className.indexOf('old') > -1) {
19307                         if(month === 0 ){
19308                             month = 11;
19309                             year -= 1;
19310                         }else{
19311                             month -= 1;
19312                         }
19313                     } else if (className.indexOf('new') > -1) {
19314                         if (month == 11) {
19315                             month = 0;
19316                             year += 1;
19317                         } else {
19318                             month += 1;
19319                         }
19320                     }
19321                     //Roo.log([year,month,day]);
19322                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19323                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19324 //                    this.fill();
19325                     //Roo.log(this.formatDate(this.date));
19326                     this.setValue(this.formatDate(this.date));
19327                     this.hidePopup();
19328                 }
19329                 break;
19330         }
19331     },
19332     
19333     setStartDate: function(startDate)
19334     {
19335         this.startDate = startDate || -Infinity;
19336         if (this.startDate !== -Infinity) {
19337             this.startDate = this.parseDate(this.startDate);
19338         }
19339         this.update();
19340         this.updateNavArrows();
19341     },
19342
19343     setEndDate: function(endDate)
19344     {
19345         this.endDate = endDate || Infinity;
19346         if (this.endDate !== Infinity) {
19347             this.endDate = this.parseDate(this.endDate);
19348         }
19349         this.update();
19350         this.updateNavArrows();
19351     },
19352     
19353     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19354     {
19355         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19356         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19357             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19358         }
19359         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19360             return parseInt(d, 10);
19361         });
19362         this.update();
19363         this.updateNavArrows();
19364     },
19365     
19366     updateNavArrows: function() 
19367     {
19368         if(this.singleMode){
19369             return;
19370         }
19371         
19372         var d = new Date(this.viewDate),
19373         year = d.getUTCFullYear(),
19374         month = d.getUTCMonth();
19375         
19376         Roo.each(this.picker().select('.prev', true).elements, function(v){
19377             v.show();
19378             switch (this.viewMode) {
19379                 case 0:
19380
19381                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19382                         v.hide();
19383                     }
19384                     break;
19385                 case 1:
19386                 case 2:
19387                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19388                         v.hide();
19389                     }
19390                     break;
19391             }
19392         });
19393         
19394         Roo.each(this.picker().select('.next', true).elements, function(v){
19395             v.show();
19396             switch (this.viewMode) {
19397                 case 0:
19398
19399                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19400                         v.hide();
19401                     }
19402                     break;
19403                 case 1:
19404                 case 2:
19405                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19406                         v.hide();
19407                     }
19408                     break;
19409             }
19410         })
19411     },
19412     
19413     moveMonth: function(date, dir)
19414     {
19415         if (!dir) {
19416             return date;
19417         }
19418         var new_date = new Date(date.valueOf()),
19419         day = new_date.getUTCDate(),
19420         month = new_date.getUTCMonth(),
19421         mag = Math.abs(dir),
19422         new_month, test;
19423         dir = dir > 0 ? 1 : -1;
19424         if (mag == 1){
19425             test = dir == -1
19426             // If going back one month, make sure month is not current month
19427             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19428             ? function(){
19429                 return new_date.getUTCMonth() == month;
19430             }
19431             // If going forward one month, make sure month is as expected
19432             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19433             : function(){
19434                 return new_date.getUTCMonth() != new_month;
19435             };
19436             new_month = month + dir;
19437             new_date.setUTCMonth(new_month);
19438             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19439             if (new_month < 0 || new_month > 11) {
19440                 new_month = (new_month + 12) % 12;
19441             }
19442         } else {
19443             // For magnitudes >1, move one month at a time...
19444             for (var i=0; i<mag; i++) {
19445                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19446                 new_date = this.moveMonth(new_date, dir);
19447             }
19448             // ...then reset the day, keeping it in the new month
19449             new_month = new_date.getUTCMonth();
19450             new_date.setUTCDate(day);
19451             test = function(){
19452                 return new_month != new_date.getUTCMonth();
19453             };
19454         }
19455         // Common date-resetting loop -- if date is beyond end of month, make it
19456         // end of month
19457         while (test()){
19458             new_date.setUTCDate(--day);
19459             new_date.setUTCMonth(new_month);
19460         }
19461         return new_date;
19462     },
19463
19464     moveYear: function(date, dir)
19465     {
19466         return this.moveMonth(date, dir*12);
19467     },
19468
19469     dateWithinRange: function(date)
19470     {
19471         return date >= this.startDate && date <= this.endDate;
19472     },
19473
19474     
19475     remove: function() 
19476     {
19477         this.picker().remove();
19478     },
19479     
19480     validateValue : function(value)
19481     {
19482         if(this.getVisibilityEl().hasClass('hidden')){
19483             return true;
19484         }
19485         
19486         if(value.length < 1)  {
19487             if(this.allowBlank){
19488                 return true;
19489             }
19490             return false;
19491         }
19492         
19493         if(value.length < this.minLength){
19494             return false;
19495         }
19496         if(value.length > this.maxLength){
19497             return false;
19498         }
19499         if(this.vtype){
19500             var vt = Roo.form.VTypes;
19501             if(!vt[this.vtype](value, this)){
19502                 return false;
19503             }
19504         }
19505         if(typeof this.validator == "function"){
19506             var msg = this.validator(value);
19507             if(msg !== true){
19508                 return false;
19509             }
19510         }
19511         
19512         if(this.regex && !this.regex.test(value)){
19513             return false;
19514         }
19515         
19516         if(typeof(this.parseDate(value)) == 'undefined'){
19517             return false;
19518         }
19519         
19520         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19521             return false;
19522         }      
19523         
19524         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19525             return false;
19526         } 
19527         
19528         
19529         return true;
19530     },
19531     
19532     reset : function()
19533     {
19534         this.date = this.viewDate = '';
19535         
19536         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19537     }
19538    
19539 });
19540
19541 Roo.apply(Roo.bootstrap.DateField,  {
19542     
19543     head : {
19544         tag: 'thead',
19545         cn: [
19546         {
19547             tag: 'tr',
19548             cn: [
19549             {
19550                 tag: 'th',
19551                 cls: 'prev',
19552                 html: '<i class="fa fa-arrow-left"/>'
19553             },
19554             {
19555                 tag: 'th',
19556                 cls: 'switch',
19557                 colspan: '5'
19558             },
19559             {
19560                 tag: 'th',
19561                 cls: 'next',
19562                 html: '<i class="fa fa-arrow-right"/>'
19563             }
19564
19565             ]
19566         }
19567         ]
19568     },
19569     
19570     content : {
19571         tag: 'tbody',
19572         cn: [
19573         {
19574             tag: 'tr',
19575             cn: [
19576             {
19577                 tag: 'td',
19578                 colspan: '7'
19579             }
19580             ]
19581         }
19582         ]
19583     },
19584     
19585     footer : {
19586         tag: 'tfoot',
19587         cn: [
19588         {
19589             tag: 'tr',
19590             cn: [
19591             {
19592                 tag: 'th',
19593                 colspan: '7',
19594                 cls: 'today'
19595             }
19596                     
19597             ]
19598         }
19599         ]
19600     },
19601     
19602     dates:{
19603         en: {
19604             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19605             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19606             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19607             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19608             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19609             today: "Today"
19610         }
19611     },
19612     
19613     modes: [
19614     {
19615         clsName: 'days',
19616         navFnc: 'Month',
19617         navStep: 1
19618     },
19619     {
19620         clsName: 'months',
19621         navFnc: 'FullYear',
19622         navStep: 1
19623     },
19624     {
19625         clsName: 'years',
19626         navFnc: 'FullYear',
19627         navStep: 10
19628     }]
19629 });
19630
19631 Roo.apply(Roo.bootstrap.DateField,  {
19632   
19633     template : {
19634         tag: 'div',
19635         cls: 'datepicker dropdown-menu roo-dynamic',
19636         cn: [
19637         {
19638             tag: 'div',
19639             cls: 'datepicker-days',
19640             cn: [
19641             {
19642                 tag: 'table',
19643                 cls: 'table-condensed',
19644                 cn:[
19645                 Roo.bootstrap.DateField.head,
19646                 {
19647                     tag: 'tbody'
19648                 },
19649                 Roo.bootstrap.DateField.footer
19650                 ]
19651             }
19652             ]
19653         },
19654         {
19655             tag: 'div',
19656             cls: 'datepicker-months',
19657             cn: [
19658             {
19659                 tag: 'table',
19660                 cls: 'table-condensed',
19661                 cn:[
19662                 Roo.bootstrap.DateField.head,
19663                 Roo.bootstrap.DateField.content,
19664                 Roo.bootstrap.DateField.footer
19665                 ]
19666             }
19667             ]
19668         },
19669         {
19670             tag: 'div',
19671             cls: 'datepicker-years',
19672             cn: [
19673             {
19674                 tag: 'table',
19675                 cls: 'table-condensed',
19676                 cn:[
19677                 Roo.bootstrap.DateField.head,
19678                 Roo.bootstrap.DateField.content,
19679                 Roo.bootstrap.DateField.footer
19680                 ]
19681             }
19682             ]
19683         }
19684         ]
19685     }
19686 });
19687
19688  
19689
19690  /*
19691  * - LGPL
19692  *
19693  * TimeField
19694  * 
19695  */
19696
19697 /**
19698  * @class Roo.bootstrap.TimeField
19699  * @extends Roo.bootstrap.Input
19700  * Bootstrap DateField class
19701  * 
19702  * 
19703  * @constructor
19704  * Create a new TimeField
19705  * @param {Object} config The config object
19706  */
19707
19708 Roo.bootstrap.TimeField = function(config){
19709     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19710     this.addEvents({
19711             /**
19712              * @event show
19713              * Fires when this field show.
19714              * @param {Roo.bootstrap.DateField} thisthis
19715              * @param {Mixed} date The date value
19716              */
19717             show : true,
19718             /**
19719              * @event show
19720              * Fires when this field hide.
19721              * @param {Roo.bootstrap.DateField} this
19722              * @param {Mixed} date The date value
19723              */
19724             hide : true,
19725             /**
19726              * @event select
19727              * Fires when select a date.
19728              * @param {Roo.bootstrap.DateField} this
19729              * @param {Mixed} date The date value
19730              */
19731             select : true
19732         });
19733 };
19734
19735 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19736     
19737     /**
19738      * @cfg {String} format
19739      * The default time format string which can be overriden for localization support.  The format must be
19740      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19741      */
19742     format : "H:i",
19743        
19744     onRender: function(ct, position)
19745     {
19746         
19747         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19748                 
19749         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19750         
19751         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19752         
19753         this.pop = this.picker().select('>.datepicker-time',true).first();
19754         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19755         
19756         this.picker().on('mousedown', this.onMousedown, this);
19757         this.picker().on('click', this.onClick, this);
19758         
19759         this.picker().addClass('datepicker-dropdown');
19760     
19761         this.fillTime();
19762         this.update();
19763             
19764         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19765         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19766         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19767         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19768         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19769         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19770
19771     },
19772     
19773     fireKey: function(e){
19774         if (!this.picker().isVisible()){
19775             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19776                 this.show();
19777             }
19778             return;
19779         }
19780
19781         e.preventDefault();
19782         
19783         switch(e.keyCode){
19784             case 27: // escape
19785                 this.hide();
19786                 break;
19787             case 37: // left
19788             case 39: // right
19789                 this.onTogglePeriod();
19790                 break;
19791             case 38: // up
19792                 this.onIncrementMinutes();
19793                 break;
19794             case 40: // down
19795                 this.onDecrementMinutes();
19796                 break;
19797             case 13: // enter
19798             case 9: // tab
19799                 this.setTime();
19800                 break;
19801         }
19802     },
19803     
19804     onClick: function(e) {
19805         e.stopPropagation();
19806         e.preventDefault();
19807     },
19808     
19809     picker : function()
19810     {
19811         return this.el.select('.datepicker', true).first();
19812     },
19813     
19814     fillTime: function()
19815     {    
19816         var time = this.pop.select('tbody', true).first();
19817         
19818         time.dom.innerHTML = '';
19819         
19820         time.createChild({
19821             tag: 'tr',
19822             cn: [
19823                 {
19824                     tag: 'td',
19825                     cn: [
19826                         {
19827                             tag: 'a',
19828                             href: '#',
19829                             cls: 'btn',
19830                             cn: [
19831                                 {
19832                                     tag: 'span',
19833                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19834                                 }
19835                             ]
19836                         } 
19837                     ]
19838                 },
19839                 {
19840                     tag: 'td',
19841                     cls: 'separator'
19842                 },
19843                 {
19844                     tag: 'td',
19845                     cn: [
19846                         {
19847                             tag: 'a',
19848                             href: '#',
19849                             cls: 'btn',
19850                             cn: [
19851                                 {
19852                                     tag: 'span',
19853                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19854                                 }
19855                             ]
19856                         }
19857                     ]
19858                 },
19859                 {
19860                     tag: 'td',
19861                     cls: 'separator'
19862                 }
19863             ]
19864         });
19865         
19866         time.createChild({
19867             tag: 'tr',
19868             cn: [
19869                 {
19870                     tag: 'td',
19871                     cn: [
19872                         {
19873                             tag: 'span',
19874                             cls: 'timepicker-hour',
19875                             html: '00'
19876                         }  
19877                     ]
19878                 },
19879                 {
19880                     tag: 'td',
19881                     cls: 'separator',
19882                     html: ':'
19883                 },
19884                 {
19885                     tag: 'td',
19886                     cn: [
19887                         {
19888                             tag: 'span',
19889                             cls: 'timepicker-minute',
19890                             html: '00'
19891                         }  
19892                     ]
19893                 },
19894                 {
19895                     tag: 'td',
19896                     cls: 'separator'
19897                 },
19898                 {
19899                     tag: 'td',
19900                     cn: [
19901                         {
19902                             tag: 'button',
19903                             type: 'button',
19904                             cls: 'btn btn-primary period',
19905                             html: 'AM'
19906                             
19907                         }
19908                     ]
19909                 }
19910             ]
19911         });
19912         
19913         time.createChild({
19914             tag: 'tr',
19915             cn: [
19916                 {
19917                     tag: 'td',
19918                     cn: [
19919                         {
19920                             tag: 'a',
19921                             href: '#',
19922                             cls: 'btn',
19923                             cn: [
19924                                 {
19925                                     tag: 'span',
19926                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19927                                 }
19928                             ]
19929                         }
19930                     ]
19931                 },
19932                 {
19933                     tag: 'td',
19934                     cls: 'separator'
19935                 },
19936                 {
19937                     tag: 'td',
19938                     cn: [
19939                         {
19940                             tag: 'a',
19941                             href: '#',
19942                             cls: 'btn',
19943                             cn: [
19944                                 {
19945                                     tag: 'span',
19946                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19947                                 }
19948                             ]
19949                         }
19950                     ]
19951                 },
19952                 {
19953                     tag: 'td',
19954                     cls: 'separator'
19955                 }
19956             ]
19957         });
19958         
19959     },
19960     
19961     update: function()
19962     {
19963         
19964         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19965         
19966         this.fill();
19967     },
19968     
19969     fill: function() 
19970     {
19971         var hours = this.time.getHours();
19972         var minutes = this.time.getMinutes();
19973         var period = 'AM';
19974         
19975         if(hours > 11){
19976             period = 'PM';
19977         }
19978         
19979         if(hours == 0){
19980             hours = 12;
19981         }
19982         
19983         
19984         if(hours > 12){
19985             hours = hours - 12;
19986         }
19987         
19988         if(hours < 10){
19989             hours = '0' + hours;
19990         }
19991         
19992         if(minutes < 10){
19993             minutes = '0' + minutes;
19994         }
19995         
19996         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19997         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19998         this.pop.select('button', true).first().dom.innerHTML = period;
19999         
20000     },
20001     
20002     place: function()
20003     {   
20004         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20005         
20006         var cls = ['bottom'];
20007         
20008         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20009             cls.pop();
20010             cls.push('top');
20011         }
20012         
20013         cls.push('right');
20014         
20015         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20016             cls.pop();
20017             cls.push('left');
20018         }
20019         
20020         this.picker().addClass(cls.join('-'));
20021         
20022         var _this = this;
20023         
20024         Roo.each(cls, function(c){
20025             if(c == 'bottom'){
20026                 _this.picker().setTop(_this.inputEl().getHeight());
20027                 return;
20028             }
20029             if(c == 'top'){
20030                 _this.picker().setTop(0 - _this.picker().getHeight());
20031                 return;
20032             }
20033             
20034             if(c == 'left'){
20035                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20036                 return;
20037             }
20038             if(c == 'right'){
20039                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20040                 return;
20041             }
20042         });
20043         
20044     },
20045   
20046     onFocus : function()
20047     {
20048         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20049         this.show();
20050     },
20051     
20052     onBlur : function()
20053     {
20054         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20055         this.hide();
20056     },
20057     
20058     show : function()
20059     {
20060         this.picker().show();
20061         this.pop.show();
20062         this.update();
20063         this.place();
20064         
20065         this.fireEvent('show', this, this.date);
20066     },
20067     
20068     hide : function()
20069     {
20070         this.picker().hide();
20071         this.pop.hide();
20072         
20073         this.fireEvent('hide', this, this.date);
20074     },
20075     
20076     setTime : function()
20077     {
20078         this.hide();
20079         this.setValue(this.time.format(this.format));
20080         
20081         this.fireEvent('select', this, this.date);
20082         
20083         
20084     },
20085     
20086     onMousedown: function(e){
20087         e.stopPropagation();
20088         e.preventDefault();
20089     },
20090     
20091     onIncrementHours: function()
20092     {
20093         Roo.log('onIncrementHours');
20094         this.time = this.time.add(Date.HOUR, 1);
20095         this.update();
20096         
20097     },
20098     
20099     onDecrementHours: function()
20100     {
20101         Roo.log('onDecrementHours');
20102         this.time = this.time.add(Date.HOUR, -1);
20103         this.update();
20104     },
20105     
20106     onIncrementMinutes: function()
20107     {
20108         Roo.log('onIncrementMinutes');
20109         this.time = this.time.add(Date.MINUTE, 1);
20110         this.update();
20111     },
20112     
20113     onDecrementMinutes: function()
20114     {
20115         Roo.log('onDecrementMinutes');
20116         this.time = this.time.add(Date.MINUTE, -1);
20117         this.update();
20118     },
20119     
20120     onTogglePeriod: function()
20121     {
20122         Roo.log('onTogglePeriod');
20123         this.time = this.time.add(Date.HOUR, 12);
20124         this.update();
20125     }
20126     
20127    
20128 });
20129
20130 Roo.apply(Roo.bootstrap.TimeField,  {
20131     
20132     content : {
20133         tag: 'tbody',
20134         cn: [
20135             {
20136                 tag: 'tr',
20137                 cn: [
20138                 {
20139                     tag: 'td',
20140                     colspan: '7'
20141                 }
20142                 ]
20143             }
20144         ]
20145     },
20146     
20147     footer : {
20148         tag: 'tfoot',
20149         cn: [
20150             {
20151                 tag: 'tr',
20152                 cn: [
20153                 {
20154                     tag: 'th',
20155                     colspan: '7',
20156                     cls: '',
20157                     cn: [
20158                         {
20159                             tag: 'button',
20160                             cls: 'btn btn-info ok',
20161                             html: 'OK'
20162                         }
20163                     ]
20164                 }
20165
20166                 ]
20167             }
20168         ]
20169     }
20170 });
20171
20172 Roo.apply(Roo.bootstrap.TimeField,  {
20173   
20174     template : {
20175         tag: 'div',
20176         cls: 'datepicker dropdown-menu',
20177         cn: [
20178             {
20179                 tag: 'div',
20180                 cls: 'datepicker-time',
20181                 cn: [
20182                 {
20183                     tag: 'table',
20184                     cls: 'table-condensed',
20185                     cn:[
20186                     Roo.bootstrap.TimeField.content,
20187                     Roo.bootstrap.TimeField.footer
20188                     ]
20189                 }
20190                 ]
20191             }
20192         ]
20193     }
20194 });
20195
20196  
20197
20198  /*
20199  * - LGPL
20200  *
20201  * MonthField
20202  * 
20203  */
20204
20205 /**
20206  * @class Roo.bootstrap.MonthField
20207  * @extends Roo.bootstrap.Input
20208  * Bootstrap MonthField class
20209  * 
20210  * @cfg {String} language default en
20211  * 
20212  * @constructor
20213  * Create a new MonthField
20214  * @param {Object} config The config object
20215  */
20216
20217 Roo.bootstrap.MonthField = function(config){
20218     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20219     
20220     this.addEvents({
20221         /**
20222          * @event show
20223          * Fires when this field show.
20224          * @param {Roo.bootstrap.MonthField} this
20225          * @param {Mixed} date The date value
20226          */
20227         show : true,
20228         /**
20229          * @event show
20230          * Fires when this field hide.
20231          * @param {Roo.bootstrap.MonthField} this
20232          * @param {Mixed} date The date value
20233          */
20234         hide : true,
20235         /**
20236          * @event select
20237          * Fires when select a date.
20238          * @param {Roo.bootstrap.MonthField} this
20239          * @param {String} oldvalue The old value
20240          * @param {String} newvalue The new value
20241          */
20242         select : true
20243     });
20244 };
20245
20246 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20247     
20248     onRender: function(ct, position)
20249     {
20250         
20251         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20252         
20253         this.language = this.language || 'en';
20254         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20255         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20256         
20257         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20258         this.isInline = false;
20259         this.isInput = true;
20260         this.component = this.el.select('.add-on', true).first() || false;
20261         this.component = (this.component && this.component.length === 0) ? false : this.component;
20262         this.hasInput = this.component && this.inputEL().length;
20263         
20264         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20265         
20266         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20267         
20268         this.picker().on('mousedown', this.onMousedown, this);
20269         this.picker().on('click', this.onClick, this);
20270         
20271         this.picker().addClass('datepicker-dropdown');
20272         
20273         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20274             v.setStyle('width', '189px');
20275         });
20276         
20277         this.fillMonths();
20278         
20279         this.update();
20280         
20281         if(this.isInline) {
20282             this.show();
20283         }
20284         
20285     },
20286     
20287     setValue: function(v, suppressEvent)
20288     {   
20289         var o = this.getValue();
20290         
20291         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20292         
20293         this.update();
20294
20295         if(suppressEvent !== true){
20296             this.fireEvent('select', this, o, v);
20297         }
20298         
20299     },
20300     
20301     getValue: function()
20302     {
20303         return this.value;
20304     },
20305     
20306     onClick: function(e) 
20307     {
20308         e.stopPropagation();
20309         e.preventDefault();
20310         
20311         var target = e.getTarget();
20312         
20313         if(target.nodeName.toLowerCase() === 'i'){
20314             target = Roo.get(target).dom.parentNode;
20315         }
20316         
20317         var nodeName = target.nodeName;
20318         var className = target.className;
20319         var html = target.innerHTML;
20320         
20321         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20322             return;
20323         }
20324         
20325         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20326         
20327         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20328         
20329         this.hide();
20330                         
20331     },
20332     
20333     picker : function()
20334     {
20335         return this.pickerEl;
20336     },
20337     
20338     fillMonths: function()
20339     {    
20340         var i = 0;
20341         var months = this.picker().select('>.datepicker-months td', true).first();
20342         
20343         months.dom.innerHTML = '';
20344         
20345         while (i < 12) {
20346             var month = {
20347                 tag: 'span',
20348                 cls: 'month',
20349                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20350             };
20351             
20352             months.createChild(month);
20353         }
20354         
20355     },
20356     
20357     update: function()
20358     {
20359         var _this = this;
20360         
20361         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20362             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20363         }
20364         
20365         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20366             e.removeClass('active');
20367             
20368             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20369                 e.addClass('active');
20370             }
20371         })
20372     },
20373     
20374     place: function()
20375     {
20376         if(this.isInline) {
20377             return;
20378         }
20379         
20380         this.picker().removeClass(['bottom', 'top']);
20381         
20382         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20383             /*
20384              * place to the top of element!
20385              *
20386              */
20387             
20388             this.picker().addClass('top');
20389             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20390             
20391             return;
20392         }
20393         
20394         this.picker().addClass('bottom');
20395         
20396         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20397     },
20398     
20399     onFocus : function()
20400     {
20401         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20402         this.show();
20403     },
20404     
20405     onBlur : function()
20406     {
20407         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20408         
20409         var d = this.inputEl().getValue();
20410         
20411         this.setValue(d);
20412                 
20413         this.hide();
20414     },
20415     
20416     show : function()
20417     {
20418         this.picker().show();
20419         this.picker().select('>.datepicker-months', true).first().show();
20420         this.update();
20421         this.place();
20422         
20423         this.fireEvent('show', this, this.date);
20424     },
20425     
20426     hide : function()
20427     {
20428         if(this.isInline) {
20429             return;
20430         }
20431         this.picker().hide();
20432         this.fireEvent('hide', this, this.date);
20433         
20434     },
20435     
20436     onMousedown: function(e)
20437     {
20438         e.stopPropagation();
20439         e.preventDefault();
20440     },
20441     
20442     keyup: function(e)
20443     {
20444         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20445         this.update();
20446     },
20447
20448     fireKey: function(e)
20449     {
20450         if (!this.picker().isVisible()){
20451             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20452                 this.show();
20453             }
20454             return;
20455         }
20456         
20457         var dir;
20458         
20459         switch(e.keyCode){
20460             case 27: // escape
20461                 this.hide();
20462                 e.preventDefault();
20463                 break;
20464             case 37: // left
20465             case 39: // right
20466                 dir = e.keyCode == 37 ? -1 : 1;
20467                 
20468                 this.vIndex = this.vIndex + dir;
20469                 
20470                 if(this.vIndex < 0){
20471                     this.vIndex = 0;
20472                 }
20473                 
20474                 if(this.vIndex > 11){
20475                     this.vIndex = 11;
20476                 }
20477                 
20478                 if(isNaN(this.vIndex)){
20479                     this.vIndex = 0;
20480                 }
20481                 
20482                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20483                 
20484                 break;
20485             case 38: // up
20486             case 40: // down
20487                 
20488                 dir = e.keyCode == 38 ? -1 : 1;
20489                 
20490                 this.vIndex = this.vIndex + dir * 4;
20491                 
20492                 if(this.vIndex < 0){
20493                     this.vIndex = 0;
20494                 }
20495                 
20496                 if(this.vIndex > 11){
20497                     this.vIndex = 11;
20498                 }
20499                 
20500                 if(isNaN(this.vIndex)){
20501                     this.vIndex = 0;
20502                 }
20503                 
20504                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20505                 break;
20506                 
20507             case 13: // enter
20508                 
20509                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20510                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20511                 }
20512                 
20513                 this.hide();
20514                 e.preventDefault();
20515                 break;
20516             case 9: // tab
20517                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20518                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20519                 }
20520                 this.hide();
20521                 break;
20522             case 16: // shift
20523             case 17: // ctrl
20524             case 18: // alt
20525                 break;
20526             default :
20527                 this.hide();
20528                 
20529         }
20530     },
20531     
20532     remove: function() 
20533     {
20534         this.picker().remove();
20535     }
20536    
20537 });
20538
20539 Roo.apply(Roo.bootstrap.MonthField,  {
20540     
20541     content : {
20542         tag: 'tbody',
20543         cn: [
20544         {
20545             tag: 'tr',
20546             cn: [
20547             {
20548                 tag: 'td',
20549                 colspan: '7'
20550             }
20551             ]
20552         }
20553         ]
20554     },
20555     
20556     dates:{
20557         en: {
20558             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20559             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20560         }
20561     }
20562 });
20563
20564 Roo.apply(Roo.bootstrap.MonthField,  {
20565   
20566     template : {
20567         tag: 'div',
20568         cls: 'datepicker dropdown-menu roo-dynamic',
20569         cn: [
20570             {
20571                 tag: 'div',
20572                 cls: 'datepicker-months',
20573                 cn: [
20574                 {
20575                     tag: 'table',
20576                     cls: 'table-condensed',
20577                     cn:[
20578                         Roo.bootstrap.DateField.content
20579                     ]
20580                 }
20581                 ]
20582             }
20583         ]
20584     }
20585 });
20586
20587  
20588
20589  
20590  /*
20591  * - LGPL
20592  *
20593  * CheckBox
20594  * 
20595  */
20596
20597 /**
20598  * @class Roo.bootstrap.CheckBox
20599  * @extends Roo.bootstrap.Input
20600  * Bootstrap CheckBox class
20601  * 
20602  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20603  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20604  * @cfg {String} boxLabel The text that appears beside the checkbox
20605  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20606  * @cfg {Boolean} checked initnal the element
20607  * @cfg {Boolean} inline inline the element (default false)
20608  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20609  * @cfg {String} tooltip label tooltip
20610  * 
20611  * @constructor
20612  * Create a new CheckBox
20613  * @param {Object} config The config object
20614  */
20615
20616 Roo.bootstrap.CheckBox = function(config){
20617     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20618    
20619     this.addEvents({
20620         /**
20621         * @event check
20622         * Fires when the element is checked or unchecked.
20623         * @param {Roo.bootstrap.CheckBox} this This input
20624         * @param {Boolean} checked The new checked value
20625         */
20626        check : true,
20627        /**
20628         * @event click
20629         * Fires when the element is click.
20630         * @param {Roo.bootstrap.CheckBox} this This input
20631         */
20632        click : true
20633     });
20634     
20635 };
20636
20637 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20638   
20639     inputType: 'checkbox',
20640     inputValue: 1,
20641     valueOff: 0,
20642     boxLabel: false,
20643     checked: false,
20644     weight : false,
20645     inline: false,
20646     tooltip : '',
20647     
20648     getAutoCreate : function()
20649     {
20650         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20651         
20652         var id = Roo.id();
20653         
20654         var cfg = {};
20655         
20656         cfg.cls = 'form-group ' + this.inputType; //input-group
20657         
20658         if(this.inline){
20659             cfg.cls += ' ' + this.inputType + '-inline';
20660         }
20661         
20662         var input =  {
20663             tag: 'input',
20664             id : id,
20665             type : this.inputType,
20666             value : this.inputValue,
20667             cls : 'roo-' + this.inputType, //'form-box',
20668             placeholder : this.placeholder || ''
20669             
20670         };
20671         
20672         if(this.inputType != 'radio'){
20673             var hidden =  {
20674                 tag: 'input',
20675                 type : 'hidden',
20676                 cls : 'roo-hidden-value',
20677                 value : this.checked ? this.inputValue : this.valueOff
20678             };
20679         }
20680         
20681             
20682         if (this.weight) { // Validity check?
20683             cfg.cls += " " + this.inputType + "-" + this.weight;
20684         }
20685         
20686         if (this.disabled) {
20687             input.disabled=true;
20688         }
20689         
20690         if(this.checked){
20691             input.checked = this.checked;
20692         }
20693         
20694         if (this.name) {
20695             
20696             input.name = this.name;
20697             
20698             if(this.inputType != 'radio'){
20699                 hidden.name = this.name;
20700                 input.name = '_hidden_' + this.name;
20701             }
20702         }
20703         
20704         if (this.size) {
20705             input.cls += ' input-' + this.size;
20706         }
20707         
20708         var settings=this;
20709         
20710         ['xs','sm','md','lg'].map(function(size){
20711             if (settings[size]) {
20712                 cfg.cls += ' col-' + size + '-' + settings[size];
20713             }
20714         });
20715         
20716         var inputblock = input;
20717          
20718         if (this.before || this.after) {
20719             
20720             inputblock = {
20721                 cls : 'input-group',
20722                 cn :  [] 
20723             };
20724             
20725             if (this.before) {
20726                 inputblock.cn.push({
20727                     tag :'span',
20728                     cls : 'input-group-addon',
20729                     html : this.before
20730                 });
20731             }
20732             
20733             inputblock.cn.push(input);
20734             
20735             if(this.inputType != 'radio'){
20736                 inputblock.cn.push(hidden);
20737             }
20738             
20739             if (this.after) {
20740                 inputblock.cn.push({
20741                     tag :'span',
20742                     cls : 'input-group-addon',
20743                     html : this.after
20744                 });
20745             }
20746             
20747         }
20748         
20749         if (align ==='left' && this.fieldLabel.length) {
20750 //                Roo.log("left and has label");
20751             cfg.cn = [
20752                 {
20753                     tag: 'label',
20754                     'for' :  id,
20755                     cls : 'control-label',
20756                     html : this.fieldLabel
20757                 },
20758                 {
20759                     cls : "", 
20760                     cn: [
20761                         inputblock
20762                     ]
20763                 }
20764             ];
20765             
20766             if(this.labelWidth > 12){
20767                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20768             }
20769             
20770             if(this.labelWidth < 13 && this.labelmd == 0){
20771                 this.labelmd = this.labelWidth;
20772             }
20773             
20774             if(this.labellg > 0){
20775                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20776                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20777             }
20778             
20779             if(this.labelmd > 0){
20780                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20781                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20782             }
20783             
20784             if(this.labelsm > 0){
20785                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20786                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20787             }
20788             
20789             if(this.labelxs > 0){
20790                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20791                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20792             }
20793             
20794         } else if ( this.fieldLabel.length) {
20795 //                Roo.log(" label");
20796                 cfg.cn = [
20797                    
20798                     {
20799                         tag: this.boxLabel ? 'span' : 'label',
20800                         'for': id,
20801                         cls: 'control-label box-input-label',
20802                         //cls : 'input-group-addon',
20803                         html : this.fieldLabel
20804                     },
20805                     
20806                     inputblock
20807                     
20808                 ];
20809
20810         } else {
20811             
20812 //                Roo.log(" no label && no align");
20813                 cfg.cn = [  inputblock ] ;
20814                 
20815                 
20816         }
20817         
20818         if(this.boxLabel){
20819              var boxLabelCfg = {
20820                 tag: 'label',
20821                 //'for': id, // box label is handled by onclick - so no for...
20822                 cls: 'box-label',
20823                 html: this.boxLabel
20824             };
20825             
20826             if(this.tooltip){
20827                 boxLabelCfg.tooltip = this.tooltip;
20828             }
20829              
20830             cfg.cn.push(boxLabelCfg);
20831         }
20832         
20833         if(this.inputType != 'radio'){
20834             cfg.cn.push(hidden);
20835         }
20836         
20837         return cfg;
20838         
20839     },
20840     
20841     /**
20842      * return the real input element.
20843      */
20844     inputEl: function ()
20845     {
20846         return this.el.select('input.roo-' + this.inputType,true).first();
20847     },
20848     hiddenEl: function ()
20849     {
20850         return this.el.select('input.roo-hidden-value',true).first();
20851     },
20852     
20853     labelEl: function()
20854     {
20855         return this.el.select('label.control-label',true).first();
20856     },
20857     /* depricated... */
20858     
20859     label: function()
20860     {
20861         return this.labelEl();
20862     },
20863     
20864     boxLabelEl: function()
20865     {
20866         return this.el.select('label.box-label',true).first();
20867     },
20868     
20869     initEvents : function()
20870     {
20871 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20872         
20873         this.inputEl().on('click', this.onClick,  this);
20874         
20875         if (this.boxLabel) { 
20876             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20877         }
20878         
20879         this.startValue = this.getValue();
20880         
20881         if(this.groupId){
20882             Roo.bootstrap.CheckBox.register(this);
20883         }
20884     },
20885     
20886     onClick : function(e)
20887     {   
20888         if(this.fireEvent('click', this, e) !== false){
20889             this.setChecked(!this.checked);
20890         }
20891         
20892     },
20893     
20894     setChecked : function(state,suppressEvent)
20895     {
20896         this.startValue = this.getValue();
20897
20898         if(this.inputType == 'radio'){
20899             
20900             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20901                 e.dom.checked = false;
20902             });
20903             
20904             this.inputEl().dom.checked = true;
20905             
20906             this.inputEl().dom.value = this.inputValue;
20907             
20908             if(suppressEvent !== true){
20909                 this.fireEvent('check', this, true);
20910             }
20911             
20912             this.validate();
20913             
20914             return;
20915         }
20916         
20917         this.checked = state;
20918         
20919         this.inputEl().dom.checked = state;
20920         
20921         
20922         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20923         
20924         if(suppressEvent !== true){
20925             this.fireEvent('check', this, state);
20926         }
20927         
20928         this.validate();
20929     },
20930     
20931     getValue : function()
20932     {
20933         if(this.inputType == 'radio'){
20934             return this.getGroupValue();
20935         }
20936         
20937         return this.hiddenEl().dom.value;
20938         
20939     },
20940     
20941     getGroupValue : function()
20942     {
20943         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20944             return '';
20945         }
20946         
20947         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20948     },
20949     
20950     setValue : function(v,suppressEvent)
20951     {
20952         if(this.inputType == 'radio'){
20953             this.setGroupValue(v, suppressEvent);
20954             return;
20955         }
20956         
20957         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20958         
20959         this.validate();
20960     },
20961     
20962     setGroupValue : function(v, suppressEvent)
20963     {
20964         this.startValue = this.getValue();
20965         
20966         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20967             e.dom.checked = false;
20968             
20969             if(e.dom.value == v){
20970                 e.dom.checked = true;
20971             }
20972         });
20973         
20974         if(suppressEvent !== true){
20975             this.fireEvent('check', this, true);
20976         }
20977
20978         this.validate();
20979         
20980         return;
20981     },
20982     
20983     validate : function()
20984     {
20985         if(this.getVisibilityEl().hasClass('hidden')){
20986             return true;
20987         }
20988         
20989         if(
20990                 this.disabled || 
20991                 (this.inputType == 'radio' && this.validateRadio()) ||
20992                 (this.inputType == 'checkbox' && this.validateCheckbox())
20993         ){
20994             this.markValid();
20995             return true;
20996         }
20997         
20998         this.markInvalid();
20999         return false;
21000     },
21001     
21002     validateRadio : function()
21003     {
21004         if(this.getVisibilityEl().hasClass('hidden')){
21005             return true;
21006         }
21007         
21008         if(this.allowBlank){
21009             return true;
21010         }
21011         
21012         var valid = false;
21013         
21014         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21015             if(!e.dom.checked){
21016                 return;
21017             }
21018             
21019             valid = true;
21020             
21021             return false;
21022         });
21023         
21024         return valid;
21025     },
21026     
21027     validateCheckbox : function()
21028     {
21029         if(!this.groupId){
21030             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21031             //return (this.getValue() == this.inputValue) ? true : false;
21032         }
21033         
21034         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21035         
21036         if(!group){
21037             return false;
21038         }
21039         
21040         var r = false;
21041         
21042         for(var i in group){
21043             if(group[i].el.isVisible(true)){
21044                 r = false;
21045                 break;
21046             }
21047             
21048             r = true;
21049         }
21050         
21051         for(var i in group){
21052             if(r){
21053                 break;
21054             }
21055             
21056             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21057         }
21058         
21059         return r;
21060     },
21061     
21062     /**
21063      * Mark this field as valid
21064      */
21065     markValid : function()
21066     {
21067         var _this = this;
21068         
21069         this.fireEvent('valid', this);
21070         
21071         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21072         
21073         if(this.groupId){
21074             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21075         }
21076         
21077         if(label){
21078             label.markValid();
21079         }
21080
21081         if(this.inputType == 'radio'){
21082             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21083                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21084                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21085             });
21086             
21087             return;
21088         }
21089
21090         if(!this.groupId){
21091             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21092             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21093             return;
21094         }
21095         
21096         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21097         
21098         if(!group){
21099             return;
21100         }
21101         
21102         for(var i in group){
21103             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21104             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21105         }
21106     },
21107     
21108      /**
21109      * Mark this field as invalid
21110      * @param {String} msg The validation message
21111      */
21112     markInvalid : function(msg)
21113     {
21114         if(this.allowBlank){
21115             return;
21116         }
21117         
21118         var _this = this;
21119         
21120         this.fireEvent('invalid', this, msg);
21121         
21122         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21123         
21124         if(this.groupId){
21125             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21126         }
21127         
21128         if(label){
21129             label.markInvalid();
21130         }
21131             
21132         if(this.inputType == 'radio'){
21133             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21134                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21135                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21136             });
21137             
21138             return;
21139         }
21140         
21141         if(!this.groupId){
21142             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21143             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21144             return;
21145         }
21146         
21147         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21148         
21149         if(!group){
21150             return;
21151         }
21152         
21153         for(var i in group){
21154             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21155             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21156         }
21157         
21158     },
21159     
21160     clearInvalid : function()
21161     {
21162         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21163         
21164         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21165         
21166         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21167         
21168         if (label && label.iconEl) {
21169             label.iconEl.removeClass(label.validClass);
21170             label.iconEl.removeClass(label.invalidClass);
21171         }
21172     },
21173     
21174     disable : function()
21175     {
21176         if(this.inputType != 'radio'){
21177             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21178             return;
21179         }
21180         
21181         var _this = this;
21182         
21183         if(this.rendered){
21184             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21185                 _this.getActionEl().addClass(this.disabledClass);
21186                 e.dom.disabled = true;
21187             });
21188         }
21189         
21190         this.disabled = true;
21191         this.fireEvent("disable", this);
21192         return this;
21193     },
21194
21195     enable : function()
21196     {
21197         if(this.inputType != 'radio'){
21198             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21199             return;
21200         }
21201         
21202         var _this = this;
21203         
21204         if(this.rendered){
21205             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21206                 _this.getActionEl().removeClass(this.disabledClass);
21207                 e.dom.disabled = false;
21208             });
21209         }
21210         
21211         this.disabled = false;
21212         this.fireEvent("enable", this);
21213         return this;
21214     },
21215     
21216     setBoxLabel : function(v)
21217     {
21218         this.boxLabel = v;
21219         
21220         if(this.rendered){
21221             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21222         }
21223     }
21224
21225 });
21226
21227 Roo.apply(Roo.bootstrap.CheckBox, {
21228     
21229     groups: {},
21230     
21231      /**
21232     * register a CheckBox Group
21233     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21234     */
21235     register : function(checkbox)
21236     {
21237         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21238             this.groups[checkbox.groupId] = {};
21239         }
21240         
21241         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21242             return;
21243         }
21244         
21245         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21246         
21247     },
21248     /**
21249     * fetch a CheckBox Group based on the group ID
21250     * @param {string} the group ID
21251     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21252     */
21253     get: function(groupId) {
21254         if (typeof(this.groups[groupId]) == 'undefined') {
21255             return false;
21256         }
21257         
21258         return this.groups[groupId] ;
21259     }
21260     
21261     
21262 });
21263 /*
21264  * - LGPL
21265  *
21266  * RadioItem
21267  * 
21268  */
21269
21270 /**
21271  * @class Roo.bootstrap.Radio
21272  * @extends Roo.bootstrap.Component
21273  * Bootstrap Radio class
21274  * @cfg {String} boxLabel - the label associated
21275  * @cfg {String} value - the value of radio
21276  * 
21277  * @constructor
21278  * Create a new Radio
21279  * @param {Object} config The config object
21280  */
21281 Roo.bootstrap.Radio = function(config){
21282     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21283     
21284 };
21285
21286 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21287     
21288     boxLabel : '',
21289     
21290     value : '',
21291     
21292     getAutoCreate : function()
21293     {
21294         var cfg = {
21295             tag : 'div',
21296             cls : 'form-group radio',
21297             cn : [
21298                 {
21299                     tag : 'label',
21300                     cls : 'box-label',
21301                     html : this.boxLabel
21302                 }
21303             ]
21304         };
21305         
21306         return cfg;
21307     },
21308     
21309     initEvents : function() 
21310     {
21311         this.parent().register(this);
21312         
21313         this.el.on('click', this.onClick, this);
21314         
21315     },
21316     
21317     onClick : function(e)
21318     {
21319         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21320             this.setChecked(true);
21321         }
21322     },
21323     
21324     setChecked : function(state, suppressEvent)
21325     {
21326         this.parent().setValue(this.value, suppressEvent);
21327         
21328     },
21329     
21330     setBoxLabel : function(v)
21331     {
21332         this.boxLabel = v;
21333         
21334         if(this.rendered){
21335             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21336         }
21337     }
21338     
21339 });
21340  
21341
21342  /*
21343  * - LGPL
21344  *
21345  * Input
21346  * 
21347  */
21348
21349 /**
21350  * @class Roo.bootstrap.SecurePass
21351  * @extends Roo.bootstrap.Input
21352  * Bootstrap SecurePass class
21353  *
21354  * 
21355  * @constructor
21356  * Create a new SecurePass
21357  * @param {Object} config The config object
21358  */
21359  
21360 Roo.bootstrap.SecurePass = function (config) {
21361     // these go here, so the translation tool can replace them..
21362     this.errors = {
21363         PwdEmpty: "Please type a password, and then retype it to confirm.",
21364         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21365         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21366         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21367         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21368         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21369         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21370         TooWeak: "Your password is Too Weak."
21371     },
21372     this.meterLabel = "Password strength:";
21373     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21374     this.meterClass = [
21375         "roo-password-meter-tooweak", 
21376         "roo-password-meter-weak", 
21377         "roo-password-meter-medium", 
21378         "roo-password-meter-strong", 
21379         "roo-password-meter-grey"
21380     ];
21381     
21382     this.errors = {};
21383     
21384     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21385 }
21386
21387 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21388     /**
21389      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21390      * {
21391      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21392      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21393      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21394      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21395      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21396      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21397      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21398      * })
21399      */
21400     // private
21401     
21402     meterWidth: 300,
21403     errorMsg :'',    
21404     errors: false,
21405     imageRoot: '/',
21406     /**
21407      * @cfg {String/Object} Label for the strength meter (defaults to
21408      * 'Password strength:')
21409      */
21410     // private
21411     meterLabel: '',
21412     /**
21413      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21414      * ['Weak', 'Medium', 'Strong'])
21415      */
21416     // private    
21417     pwdStrengths: false,    
21418     // private
21419     strength: 0,
21420     // private
21421     _lastPwd: null,
21422     // private
21423     kCapitalLetter: 0,
21424     kSmallLetter: 1,
21425     kDigit: 2,
21426     kPunctuation: 3,
21427     
21428     insecure: false,
21429     // private
21430     initEvents: function ()
21431     {
21432         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21433
21434         if (this.el.is('input[type=password]') && Roo.isSafari) {
21435             this.el.on('keydown', this.SafariOnKeyDown, this);
21436         }
21437
21438         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21439     },
21440     // private
21441     onRender: function (ct, position)
21442     {
21443         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21444         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21445         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21446
21447         this.trigger.createChild({
21448                    cn: [
21449                     {
21450                     //id: 'PwdMeter',
21451                     tag: 'div',
21452                     cls: 'roo-password-meter-grey col-xs-12',
21453                     style: {
21454                         //width: 0,
21455                         //width: this.meterWidth + 'px'                                                
21456                         }
21457                     },
21458                     {                            
21459                          cls: 'roo-password-meter-text'                          
21460                     }
21461                 ]            
21462         });
21463
21464          
21465         if (this.hideTrigger) {
21466             this.trigger.setDisplayed(false);
21467         }
21468         this.setSize(this.width || '', this.height || '');
21469     },
21470     // private
21471     onDestroy: function ()
21472     {
21473         if (this.trigger) {
21474             this.trigger.removeAllListeners();
21475             this.trigger.remove();
21476         }
21477         if (this.wrap) {
21478             this.wrap.remove();
21479         }
21480         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21481     },
21482     // private
21483     checkStrength: function ()
21484     {
21485         var pwd = this.inputEl().getValue();
21486         if (pwd == this._lastPwd) {
21487             return;
21488         }
21489
21490         var strength;
21491         if (this.ClientSideStrongPassword(pwd)) {
21492             strength = 3;
21493         } else if (this.ClientSideMediumPassword(pwd)) {
21494             strength = 2;
21495         } else if (this.ClientSideWeakPassword(pwd)) {
21496             strength = 1;
21497         } else {
21498             strength = 0;
21499         }
21500         
21501         Roo.log('strength1: ' + strength);
21502         
21503         //var pm = this.trigger.child('div/div/div').dom;
21504         var pm = this.trigger.child('div/div');
21505         pm.removeClass(this.meterClass);
21506         pm.addClass(this.meterClass[strength]);
21507                 
21508         
21509         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21510                 
21511         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21512         
21513         this._lastPwd = pwd;
21514     },
21515     reset: function ()
21516     {
21517         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21518         
21519         this._lastPwd = '';
21520         
21521         var pm = this.trigger.child('div/div');
21522         pm.removeClass(this.meterClass);
21523         pm.addClass('roo-password-meter-grey');        
21524         
21525         
21526         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21527         
21528         pt.innerHTML = '';
21529         this.inputEl().dom.type='password';
21530     },
21531     // private
21532     validateValue: function (value)
21533     {
21534         
21535         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21536             return false;
21537         }
21538         if (value.length == 0) {
21539             if (this.allowBlank) {
21540                 this.clearInvalid();
21541                 return true;
21542             }
21543
21544             this.markInvalid(this.errors.PwdEmpty);
21545             this.errorMsg = this.errors.PwdEmpty;
21546             return false;
21547         }
21548         
21549         if(this.insecure){
21550             return true;
21551         }
21552         
21553         if ('[\x21-\x7e]*'.match(value)) {
21554             this.markInvalid(this.errors.PwdBadChar);
21555             this.errorMsg = this.errors.PwdBadChar;
21556             return false;
21557         }
21558         if (value.length < 6) {
21559             this.markInvalid(this.errors.PwdShort);
21560             this.errorMsg = this.errors.PwdShort;
21561             return false;
21562         }
21563         if (value.length > 16) {
21564             this.markInvalid(this.errors.PwdLong);
21565             this.errorMsg = this.errors.PwdLong;
21566             return false;
21567         }
21568         var strength;
21569         if (this.ClientSideStrongPassword(value)) {
21570             strength = 3;
21571         } else if (this.ClientSideMediumPassword(value)) {
21572             strength = 2;
21573         } else if (this.ClientSideWeakPassword(value)) {
21574             strength = 1;
21575         } else {
21576             strength = 0;
21577         }
21578
21579         
21580         if (strength < 2) {
21581             //this.markInvalid(this.errors.TooWeak);
21582             this.errorMsg = this.errors.TooWeak;
21583             //return false;
21584         }
21585         
21586         
21587         console.log('strength2: ' + strength);
21588         
21589         //var pm = this.trigger.child('div/div/div').dom;
21590         
21591         var pm = this.trigger.child('div/div');
21592         pm.removeClass(this.meterClass);
21593         pm.addClass(this.meterClass[strength]);
21594                 
21595         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21596                 
21597         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21598         
21599         this.errorMsg = ''; 
21600         return true;
21601     },
21602     // private
21603     CharacterSetChecks: function (type)
21604     {
21605         this.type = type;
21606         this.fResult = false;
21607     },
21608     // private
21609     isctype: function (character, type)
21610     {
21611         switch (type) {  
21612             case this.kCapitalLetter:
21613                 if (character >= 'A' && character <= 'Z') {
21614                     return true;
21615                 }
21616                 break;
21617             
21618             case this.kSmallLetter:
21619                 if (character >= 'a' && character <= 'z') {
21620                     return true;
21621                 }
21622                 break;
21623             
21624             case this.kDigit:
21625                 if (character >= '0' && character <= '9') {
21626                     return true;
21627                 }
21628                 break;
21629             
21630             case this.kPunctuation:
21631                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21632                     return true;
21633                 }
21634                 break;
21635             
21636             default:
21637                 return false;
21638         }
21639
21640     },
21641     // private
21642     IsLongEnough: function (pwd, size)
21643     {
21644         return !(pwd == null || isNaN(size) || pwd.length < size);
21645     },
21646     // private
21647     SpansEnoughCharacterSets: function (word, nb)
21648     {
21649         if (!this.IsLongEnough(word, nb))
21650         {
21651             return false;
21652         }
21653
21654         var characterSetChecks = new Array(
21655             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21656             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21657         );
21658         
21659         for (var index = 0; index < word.length; ++index) {
21660             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21661                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21662                     characterSetChecks[nCharSet].fResult = true;
21663                     break;
21664                 }
21665             }
21666         }
21667
21668         var nCharSets = 0;
21669         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21670             if (characterSetChecks[nCharSet].fResult) {
21671                 ++nCharSets;
21672             }
21673         }
21674
21675         if (nCharSets < nb) {
21676             return false;
21677         }
21678         return true;
21679     },
21680     // private
21681     ClientSideStrongPassword: function (pwd)
21682     {
21683         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21684     },
21685     // private
21686     ClientSideMediumPassword: function (pwd)
21687     {
21688         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21689     },
21690     // private
21691     ClientSideWeakPassword: function (pwd)
21692     {
21693         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21694     }
21695           
21696 })//<script type="text/javascript">
21697
21698 /*
21699  * Based  Ext JS Library 1.1.1
21700  * Copyright(c) 2006-2007, Ext JS, LLC.
21701  * LGPL
21702  *
21703  */
21704  
21705 /**
21706  * @class Roo.HtmlEditorCore
21707  * @extends Roo.Component
21708  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21709  *
21710  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21711  */
21712
21713 Roo.HtmlEditorCore = function(config){
21714     
21715     
21716     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21717     
21718     
21719     this.addEvents({
21720         /**
21721          * @event initialize
21722          * Fires when the editor is fully initialized (including the iframe)
21723          * @param {Roo.HtmlEditorCore} this
21724          */
21725         initialize: true,
21726         /**
21727          * @event activate
21728          * Fires when the editor is first receives the focus. Any insertion must wait
21729          * until after this event.
21730          * @param {Roo.HtmlEditorCore} this
21731          */
21732         activate: true,
21733          /**
21734          * @event beforesync
21735          * Fires before the textarea is updated with content from the editor iframe. Return false
21736          * to cancel the sync.
21737          * @param {Roo.HtmlEditorCore} this
21738          * @param {String} html
21739          */
21740         beforesync: true,
21741          /**
21742          * @event beforepush
21743          * Fires before the iframe editor is updated with content from the textarea. Return false
21744          * to cancel the push.
21745          * @param {Roo.HtmlEditorCore} this
21746          * @param {String} html
21747          */
21748         beforepush: true,
21749          /**
21750          * @event sync
21751          * Fires when the textarea is updated with content from the editor iframe.
21752          * @param {Roo.HtmlEditorCore} this
21753          * @param {String} html
21754          */
21755         sync: true,
21756          /**
21757          * @event push
21758          * Fires when the iframe editor is updated with content from the textarea.
21759          * @param {Roo.HtmlEditorCore} this
21760          * @param {String} html
21761          */
21762         push: true,
21763         
21764         /**
21765          * @event editorevent
21766          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21767          * @param {Roo.HtmlEditorCore} this
21768          */
21769         editorevent: true
21770         
21771     });
21772     
21773     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21774     
21775     // defaults : white / black...
21776     this.applyBlacklists();
21777     
21778     
21779     
21780 };
21781
21782
21783 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21784
21785
21786      /**
21787      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21788      */
21789     
21790     owner : false,
21791     
21792      /**
21793      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21794      *                        Roo.resizable.
21795      */
21796     resizable : false,
21797      /**
21798      * @cfg {Number} height (in pixels)
21799      */   
21800     height: 300,
21801    /**
21802      * @cfg {Number} width (in pixels)
21803      */   
21804     width: 500,
21805     
21806     /**
21807      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21808      * 
21809      */
21810     stylesheets: false,
21811     
21812     // id of frame..
21813     frameId: false,
21814     
21815     // private properties
21816     validationEvent : false,
21817     deferHeight: true,
21818     initialized : false,
21819     activated : false,
21820     sourceEditMode : false,
21821     onFocus : Roo.emptyFn,
21822     iframePad:3,
21823     hideMode:'offsets',
21824     
21825     clearUp: true,
21826     
21827     // blacklist + whitelisted elements..
21828     black: false,
21829     white: false,
21830      
21831     bodyCls : '',
21832
21833     /**
21834      * Protected method that will not generally be called directly. It
21835      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21836      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21837      */
21838     getDocMarkup : function(){
21839         // body styles..
21840         var st = '';
21841         
21842         // inherit styels from page...?? 
21843         if (this.stylesheets === false) {
21844             
21845             Roo.get(document.head).select('style').each(function(node) {
21846                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21847             });
21848             
21849             Roo.get(document.head).select('link').each(function(node) { 
21850                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21851             });
21852             
21853         } else if (!this.stylesheets.length) {
21854                 // simple..
21855                 st = '<style type="text/css">' +
21856                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21857                    '</style>';
21858         } else { 
21859             st = '<style type="text/css">' +
21860                     this.stylesheets +
21861                 '</style>';
21862         }
21863         
21864         st +=  '<style type="text/css">' +
21865             'IMG { cursor: pointer } ' +
21866         '</style>';
21867
21868         var cls = 'roo-htmleditor-body';
21869         
21870         if(this.bodyCls.length){
21871             cls += ' ' + this.bodyCls;
21872         }
21873         
21874         return '<html><head>' + st  +
21875             //<style type="text/css">' +
21876             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21877             //'</style>' +
21878             ' </head><body class="' +  cls + '"></body></html>';
21879     },
21880
21881     // private
21882     onRender : function(ct, position)
21883     {
21884         var _t = this;
21885         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21886         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21887         
21888         
21889         this.el.dom.style.border = '0 none';
21890         this.el.dom.setAttribute('tabIndex', -1);
21891         this.el.addClass('x-hidden hide');
21892         
21893         
21894         
21895         if(Roo.isIE){ // fix IE 1px bogus margin
21896             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21897         }
21898        
21899         
21900         this.frameId = Roo.id();
21901         
21902          
21903         
21904         var iframe = this.owner.wrap.createChild({
21905             tag: 'iframe',
21906             cls: 'form-control', // bootstrap..
21907             id: this.frameId,
21908             name: this.frameId,
21909             frameBorder : 'no',
21910             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21911         }, this.el
21912         );
21913         
21914         
21915         this.iframe = iframe.dom;
21916
21917          this.assignDocWin();
21918         
21919         this.doc.designMode = 'on';
21920        
21921         this.doc.open();
21922         this.doc.write(this.getDocMarkup());
21923         this.doc.close();
21924
21925         
21926         var task = { // must defer to wait for browser to be ready
21927             run : function(){
21928                 //console.log("run task?" + this.doc.readyState);
21929                 this.assignDocWin();
21930                 if(this.doc.body || this.doc.readyState == 'complete'){
21931                     try {
21932                         this.doc.designMode="on";
21933                     } catch (e) {
21934                         return;
21935                     }
21936                     Roo.TaskMgr.stop(task);
21937                     this.initEditor.defer(10, this);
21938                 }
21939             },
21940             interval : 10,
21941             duration: 10000,
21942             scope: this
21943         };
21944         Roo.TaskMgr.start(task);
21945
21946     },
21947
21948     // private
21949     onResize : function(w, h)
21950     {
21951          Roo.log('resize: ' +w + ',' + h );
21952         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21953         if(!this.iframe){
21954             return;
21955         }
21956         if(typeof w == 'number'){
21957             
21958             this.iframe.style.width = w + 'px';
21959         }
21960         if(typeof h == 'number'){
21961             
21962             this.iframe.style.height = h + 'px';
21963             if(this.doc){
21964                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21965             }
21966         }
21967         
21968     },
21969
21970     /**
21971      * Toggles the editor between standard and source edit mode.
21972      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21973      */
21974     toggleSourceEdit : function(sourceEditMode){
21975         
21976         this.sourceEditMode = sourceEditMode === true;
21977         
21978         if(this.sourceEditMode){
21979  
21980             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21981             
21982         }else{
21983             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21984             //this.iframe.className = '';
21985             this.deferFocus();
21986         }
21987         //this.setSize(this.owner.wrap.getSize());
21988         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21989     },
21990
21991     
21992   
21993
21994     /**
21995      * Protected method that will not generally be called directly. If you need/want
21996      * custom HTML cleanup, this is the method you should override.
21997      * @param {String} html The HTML to be cleaned
21998      * return {String} The cleaned HTML
21999      */
22000     cleanHtml : function(html){
22001         html = String(html);
22002         if(html.length > 5){
22003             if(Roo.isSafari){ // strip safari nonsense
22004                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22005             }
22006         }
22007         if(html == '&nbsp;'){
22008             html = '';
22009         }
22010         return html;
22011     },
22012
22013     /**
22014      * HTML Editor -> Textarea
22015      * Protected method that will not generally be called directly. Syncs the contents
22016      * of the editor iframe with the textarea.
22017      */
22018     syncValue : function(){
22019         if(this.initialized){
22020             var bd = (this.doc.body || this.doc.documentElement);
22021             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22022             var html = bd.innerHTML;
22023             if(Roo.isSafari){
22024                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22025                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22026                 if(m && m[1]){
22027                     html = '<div style="'+m[0]+'">' + html + '</div>';
22028                 }
22029             }
22030             html = this.cleanHtml(html);
22031             // fix up the special chars.. normaly like back quotes in word...
22032             // however we do not want to do this with chinese..
22033             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22034                 var cc = b.charCodeAt();
22035                 if (
22036                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22037                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22038                     (cc >= 0xf900 && cc < 0xfb00 )
22039                 ) {
22040                         return b;
22041                 }
22042                 return "&#"+cc+";" 
22043             });
22044             if(this.owner.fireEvent('beforesync', this, html) !== false){
22045                 this.el.dom.value = html;
22046                 this.owner.fireEvent('sync', this, html);
22047             }
22048         }
22049     },
22050
22051     /**
22052      * Protected method that will not generally be called directly. Pushes the value of the textarea
22053      * into the iframe editor.
22054      */
22055     pushValue : function(){
22056         if(this.initialized){
22057             var v = this.el.dom.value.trim();
22058             
22059 //            if(v.length < 1){
22060 //                v = '&#160;';
22061 //            }
22062             
22063             if(this.owner.fireEvent('beforepush', this, v) !== false){
22064                 var d = (this.doc.body || this.doc.documentElement);
22065                 d.innerHTML = v;
22066                 this.cleanUpPaste();
22067                 this.el.dom.value = d.innerHTML;
22068                 this.owner.fireEvent('push', this, v);
22069             }
22070         }
22071     },
22072
22073     // private
22074     deferFocus : function(){
22075         this.focus.defer(10, this);
22076     },
22077
22078     // doc'ed in Field
22079     focus : function(){
22080         if(this.win && !this.sourceEditMode){
22081             this.win.focus();
22082         }else{
22083             this.el.focus();
22084         }
22085     },
22086     
22087     assignDocWin: function()
22088     {
22089         var iframe = this.iframe;
22090         
22091          if(Roo.isIE){
22092             this.doc = iframe.contentWindow.document;
22093             this.win = iframe.contentWindow;
22094         } else {
22095 //            if (!Roo.get(this.frameId)) {
22096 //                return;
22097 //            }
22098 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22099 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22100             
22101             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22102                 return;
22103             }
22104             
22105             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22106             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22107         }
22108     },
22109     
22110     // private
22111     initEditor : function(){
22112         //console.log("INIT EDITOR");
22113         this.assignDocWin();
22114         
22115         
22116         
22117         this.doc.designMode="on";
22118         this.doc.open();
22119         this.doc.write(this.getDocMarkup());
22120         this.doc.close();
22121         
22122         var dbody = (this.doc.body || this.doc.documentElement);
22123         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22124         // this copies styles from the containing element into thsi one..
22125         // not sure why we need all of this..
22126         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22127         
22128         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22129         //ss['background-attachment'] = 'fixed'; // w3c
22130         dbody.bgProperties = 'fixed'; // ie
22131         //Roo.DomHelper.applyStyles(dbody, ss);
22132         Roo.EventManager.on(this.doc, {
22133             //'mousedown': this.onEditorEvent,
22134             'mouseup': this.onEditorEvent,
22135             'dblclick': this.onEditorEvent,
22136             'click': this.onEditorEvent,
22137             'keyup': this.onEditorEvent,
22138             buffer:100,
22139             scope: this
22140         });
22141         if(Roo.isGecko){
22142             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22143         }
22144         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22145             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22146         }
22147         this.initialized = true;
22148
22149         this.owner.fireEvent('initialize', this);
22150         this.pushValue();
22151     },
22152
22153     // private
22154     onDestroy : function(){
22155         
22156         
22157         
22158         if(this.rendered){
22159             
22160             //for (var i =0; i < this.toolbars.length;i++) {
22161             //    // fixme - ask toolbars for heights?
22162             //    this.toolbars[i].onDestroy();
22163            // }
22164             
22165             //this.wrap.dom.innerHTML = '';
22166             //this.wrap.remove();
22167         }
22168     },
22169
22170     // private
22171     onFirstFocus : function(){
22172         
22173         this.assignDocWin();
22174         
22175         
22176         this.activated = true;
22177          
22178     
22179         if(Roo.isGecko){ // prevent silly gecko errors
22180             this.win.focus();
22181             var s = this.win.getSelection();
22182             if(!s.focusNode || s.focusNode.nodeType != 3){
22183                 var r = s.getRangeAt(0);
22184                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22185                 r.collapse(true);
22186                 this.deferFocus();
22187             }
22188             try{
22189                 this.execCmd('useCSS', true);
22190                 this.execCmd('styleWithCSS', false);
22191             }catch(e){}
22192         }
22193         this.owner.fireEvent('activate', this);
22194     },
22195
22196     // private
22197     adjustFont: function(btn){
22198         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22199         //if(Roo.isSafari){ // safari
22200         //    adjust *= 2;
22201        // }
22202         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22203         if(Roo.isSafari){ // safari
22204             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22205             v =  (v < 10) ? 10 : v;
22206             v =  (v > 48) ? 48 : v;
22207             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22208             
22209         }
22210         
22211         
22212         v = Math.max(1, v+adjust);
22213         
22214         this.execCmd('FontSize', v  );
22215     },
22216
22217     onEditorEvent : function(e)
22218     {
22219         this.owner.fireEvent('editorevent', this, e);
22220       //  this.updateToolbar();
22221         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22222     },
22223
22224     insertTag : function(tg)
22225     {
22226         // could be a bit smarter... -> wrap the current selected tRoo..
22227         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22228             
22229             range = this.createRange(this.getSelection());
22230             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22231             wrappingNode.appendChild(range.extractContents());
22232             range.insertNode(wrappingNode);
22233
22234             return;
22235             
22236             
22237             
22238         }
22239         this.execCmd("formatblock",   tg);
22240         
22241     },
22242     
22243     insertText : function(txt)
22244     {
22245         
22246         
22247         var range = this.createRange();
22248         range.deleteContents();
22249                //alert(Sender.getAttribute('label'));
22250                
22251         range.insertNode(this.doc.createTextNode(txt));
22252     } ,
22253     
22254      
22255
22256     /**
22257      * Executes a Midas editor command on the editor document and performs necessary focus and
22258      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22259      * @param {String} cmd The Midas command
22260      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22261      */
22262     relayCmd : function(cmd, value){
22263         this.win.focus();
22264         this.execCmd(cmd, value);
22265         this.owner.fireEvent('editorevent', this);
22266         //this.updateToolbar();
22267         this.owner.deferFocus();
22268     },
22269
22270     /**
22271      * Executes a Midas editor command directly on the editor document.
22272      * For visual commands, you should use {@link #relayCmd} instead.
22273      * <b>This should only be called after the editor is initialized.</b>
22274      * @param {String} cmd The Midas command
22275      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22276      */
22277     execCmd : function(cmd, value){
22278         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22279         this.syncValue();
22280     },
22281  
22282  
22283    
22284     /**
22285      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22286      * to insert tRoo.
22287      * @param {String} text | dom node.. 
22288      */
22289     insertAtCursor : function(text)
22290     {
22291         
22292         if(!this.activated){
22293             return;
22294         }
22295         /*
22296         if(Roo.isIE){
22297             this.win.focus();
22298             var r = this.doc.selection.createRange();
22299             if(r){
22300                 r.collapse(true);
22301                 r.pasteHTML(text);
22302                 this.syncValue();
22303                 this.deferFocus();
22304             
22305             }
22306             return;
22307         }
22308         */
22309         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22310             this.win.focus();
22311             
22312             
22313             // from jquery ui (MIT licenced)
22314             var range, node;
22315             var win = this.win;
22316             
22317             if (win.getSelection && win.getSelection().getRangeAt) {
22318                 range = win.getSelection().getRangeAt(0);
22319                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22320                 range.insertNode(node);
22321             } else if (win.document.selection && win.document.selection.createRange) {
22322                 // no firefox support
22323                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22324                 win.document.selection.createRange().pasteHTML(txt);
22325             } else {
22326                 // no firefox support
22327                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22328                 this.execCmd('InsertHTML', txt);
22329             } 
22330             
22331             this.syncValue();
22332             
22333             this.deferFocus();
22334         }
22335     },
22336  // private
22337     mozKeyPress : function(e){
22338         if(e.ctrlKey){
22339             var c = e.getCharCode(), cmd;
22340           
22341             if(c > 0){
22342                 c = String.fromCharCode(c).toLowerCase();
22343                 switch(c){
22344                     case 'b':
22345                         cmd = 'bold';
22346                         break;
22347                     case 'i':
22348                         cmd = 'italic';
22349                         break;
22350                     
22351                     case 'u':
22352                         cmd = 'underline';
22353                         break;
22354                     
22355                     case 'v':
22356                         this.cleanUpPaste.defer(100, this);
22357                         return;
22358                         
22359                 }
22360                 if(cmd){
22361                     this.win.focus();
22362                     this.execCmd(cmd);
22363                     this.deferFocus();
22364                     e.preventDefault();
22365                 }
22366                 
22367             }
22368         }
22369     },
22370
22371     // private
22372     fixKeys : function(){ // load time branching for fastest keydown performance
22373         if(Roo.isIE){
22374             return function(e){
22375                 var k = e.getKey(), r;
22376                 if(k == e.TAB){
22377                     e.stopEvent();
22378                     r = this.doc.selection.createRange();
22379                     if(r){
22380                         r.collapse(true);
22381                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22382                         this.deferFocus();
22383                     }
22384                     return;
22385                 }
22386                 
22387                 if(k == e.ENTER){
22388                     r = this.doc.selection.createRange();
22389                     if(r){
22390                         var target = r.parentElement();
22391                         if(!target || target.tagName.toLowerCase() != 'li'){
22392                             e.stopEvent();
22393                             r.pasteHTML('<br />');
22394                             r.collapse(false);
22395                             r.select();
22396                         }
22397                     }
22398                 }
22399                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22400                     this.cleanUpPaste.defer(100, this);
22401                     return;
22402                 }
22403                 
22404                 
22405             };
22406         }else if(Roo.isOpera){
22407             return function(e){
22408                 var k = e.getKey();
22409                 if(k == e.TAB){
22410                     e.stopEvent();
22411                     this.win.focus();
22412                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22413                     this.deferFocus();
22414                 }
22415                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22416                     this.cleanUpPaste.defer(100, this);
22417                     return;
22418                 }
22419                 
22420             };
22421         }else if(Roo.isSafari){
22422             return function(e){
22423                 var k = e.getKey();
22424                 
22425                 if(k == e.TAB){
22426                     e.stopEvent();
22427                     this.execCmd('InsertText','\t');
22428                     this.deferFocus();
22429                     return;
22430                 }
22431                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22432                     this.cleanUpPaste.defer(100, this);
22433                     return;
22434                 }
22435                 
22436              };
22437         }
22438     }(),
22439     
22440     getAllAncestors: function()
22441     {
22442         var p = this.getSelectedNode();
22443         var a = [];
22444         if (!p) {
22445             a.push(p); // push blank onto stack..
22446             p = this.getParentElement();
22447         }
22448         
22449         
22450         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22451             a.push(p);
22452             p = p.parentNode;
22453         }
22454         a.push(this.doc.body);
22455         return a;
22456     },
22457     lastSel : false,
22458     lastSelNode : false,
22459     
22460     
22461     getSelection : function() 
22462     {
22463         this.assignDocWin();
22464         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22465     },
22466     
22467     getSelectedNode: function() 
22468     {
22469         // this may only work on Gecko!!!
22470         
22471         // should we cache this!!!!
22472         
22473         
22474         
22475          
22476         var range = this.createRange(this.getSelection()).cloneRange();
22477         
22478         if (Roo.isIE) {
22479             var parent = range.parentElement();
22480             while (true) {
22481                 var testRange = range.duplicate();
22482                 testRange.moveToElementText(parent);
22483                 if (testRange.inRange(range)) {
22484                     break;
22485                 }
22486                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22487                     break;
22488                 }
22489                 parent = parent.parentElement;
22490             }
22491             return parent;
22492         }
22493         
22494         // is ancestor a text element.
22495         var ac =  range.commonAncestorContainer;
22496         if (ac.nodeType == 3) {
22497             ac = ac.parentNode;
22498         }
22499         
22500         var ar = ac.childNodes;
22501          
22502         var nodes = [];
22503         var other_nodes = [];
22504         var has_other_nodes = false;
22505         for (var i=0;i<ar.length;i++) {
22506             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22507                 continue;
22508             }
22509             // fullly contained node.
22510             
22511             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22512                 nodes.push(ar[i]);
22513                 continue;
22514             }
22515             
22516             // probably selected..
22517             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22518                 other_nodes.push(ar[i]);
22519                 continue;
22520             }
22521             // outer..
22522             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22523                 continue;
22524             }
22525             
22526             
22527             has_other_nodes = true;
22528         }
22529         if (!nodes.length && other_nodes.length) {
22530             nodes= other_nodes;
22531         }
22532         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22533             return false;
22534         }
22535         
22536         return nodes[0];
22537     },
22538     createRange: function(sel)
22539     {
22540         // this has strange effects when using with 
22541         // top toolbar - not sure if it's a great idea.
22542         //this.editor.contentWindow.focus();
22543         if (typeof sel != "undefined") {
22544             try {
22545                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22546             } catch(e) {
22547                 return this.doc.createRange();
22548             }
22549         } else {
22550             return this.doc.createRange();
22551         }
22552     },
22553     getParentElement: function()
22554     {
22555         
22556         this.assignDocWin();
22557         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22558         
22559         var range = this.createRange(sel);
22560          
22561         try {
22562             var p = range.commonAncestorContainer;
22563             while (p.nodeType == 3) { // text node
22564                 p = p.parentNode;
22565             }
22566             return p;
22567         } catch (e) {
22568             return null;
22569         }
22570     
22571     },
22572     /***
22573      *
22574      * Range intersection.. the hard stuff...
22575      *  '-1' = before
22576      *  '0' = hits..
22577      *  '1' = after.
22578      *         [ -- selected range --- ]
22579      *   [fail]                        [fail]
22580      *
22581      *    basically..
22582      *      if end is before start or  hits it. fail.
22583      *      if start is after end or hits it fail.
22584      *
22585      *   if either hits (but other is outside. - then it's not 
22586      *   
22587      *    
22588      **/
22589     
22590     
22591     // @see http://www.thismuchiknow.co.uk/?p=64.
22592     rangeIntersectsNode : function(range, node)
22593     {
22594         var nodeRange = node.ownerDocument.createRange();
22595         try {
22596             nodeRange.selectNode(node);
22597         } catch (e) {
22598             nodeRange.selectNodeContents(node);
22599         }
22600     
22601         var rangeStartRange = range.cloneRange();
22602         rangeStartRange.collapse(true);
22603     
22604         var rangeEndRange = range.cloneRange();
22605         rangeEndRange.collapse(false);
22606     
22607         var nodeStartRange = nodeRange.cloneRange();
22608         nodeStartRange.collapse(true);
22609     
22610         var nodeEndRange = nodeRange.cloneRange();
22611         nodeEndRange.collapse(false);
22612     
22613         return rangeStartRange.compareBoundaryPoints(
22614                  Range.START_TO_START, nodeEndRange) == -1 &&
22615                rangeEndRange.compareBoundaryPoints(
22616                  Range.START_TO_START, nodeStartRange) == 1;
22617         
22618          
22619     },
22620     rangeCompareNode : function(range, node)
22621     {
22622         var nodeRange = node.ownerDocument.createRange();
22623         try {
22624             nodeRange.selectNode(node);
22625         } catch (e) {
22626             nodeRange.selectNodeContents(node);
22627         }
22628         
22629         
22630         range.collapse(true);
22631     
22632         nodeRange.collapse(true);
22633      
22634         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22635         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22636          
22637         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22638         
22639         var nodeIsBefore   =  ss == 1;
22640         var nodeIsAfter    = ee == -1;
22641         
22642         if (nodeIsBefore && nodeIsAfter) {
22643             return 0; // outer
22644         }
22645         if (!nodeIsBefore && nodeIsAfter) {
22646             return 1; //right trailed.
22647         }
22648         
22649         if (nodeIsBefore && !nodeIsAfter) {
22650             return 2;  // left trailed.
22651         }
22652         // fully contined.
22653         return 3;
22654     },
22655
22656     // private? - in a new class?
22657     cleanUpPaste :  function()
22658     {
22659         // cleans up the whole document..
22660         Roo.log('cleanuppaste');
22661         
22662         this.cleanUpChildren(this.doc.body);
22663         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22664         if (clean != this.doc.body.innerHTML) {
22665             this.doc.body.innerHTML = clean;
22666         }
22667         
22668     },
22669     
22670     cleanWordChars : function(input) {// change the chars to hex code
22671         var he = Roo.HtmlEditorCore;
22672         
22673         var output = input;
22674         Roo.each(he.swapCodes, function(sw) { 
22675             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22676             
22677             output = output.replace(swapper, sw[1]);
22678         });
22679         
22680         return output;
22681     },
22682     
22683     
22684     cleanUpChildren : function (n)
22685     {
22686         if (!n.childNodes.length) {
22687             return;
22688         }
22689         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22690            this.cleanUpChild(n.childNodes[i]);
22691         }
22692     },
22693     
22694     
22695         
22696     
22697     cleanUpChild : function (node)
22698     {
22699         var ed = this;
22700         //console.log(node);
22701         if (node.nodeName == "#text") {
22702             // clean up silly Windows -- stuff?
22703             return; 
22704         }
22705         if (node.nodeName == "#comment") {
22706             node.parentNode.removeChild(node);
22707             // clean up silly Windows -- stuff?
22708             return; 
22709         }
22710         var lcname = node.tagName.toLowerCase();
22711         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22712         // whitelist of tags..
22713         
22714         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22715             // remove node.
22716             node.parentNode.removeChild(node);
22717             return;
22718             
22719         }
22720         
22721         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22722         
22723         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22724         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22725         
22726         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22727         //    remove_keep_children = true;
22728         //}
22729         
22730         if (remove_keep_children) {
22731             this.cleanUpChildren(node);
22732             // inserts everything just before this node...
22733             while (node.childNodes.length) {
22734                 var cn = node.childNodes[0];
22735                 node.removeChild(cn);
22736                 node.parentNode.insertBefore(cn, node);
22737             }
22738             node.parentNode.removeChild(node);
22739             return;
22740         }
22741         
22742         if (!node.attributes || !node.attributes.length) {
22743             this.cleanUpChildren(node);
22744             return;
22745         }
22746         
22747         function cleanAttr(n,v)
22748         {
22749             
22750             if (v.match(/^\./) || v.match(/^\//)) {
22751                 return;
22752             }
22753             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22754                 return;
22755             }
22756             if (v.match(/^#/)) {
22757                 return;
22758             }
22759 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22760             node.removeAttribute(n);
22761             
22762         }
22763         
22764         var cwhite = this.cwhite;
22765         var cblack = this.cblack;
22766             
22767         function cleanStyle(n,v)
22768         {
22769             if (v.match(/expression/)) { //XSS?? should we even bother..
22770                 node.removeAttribute(n);
22771                 return;
22772             }
22773             
22774             var parts = v.split(/;/);
22775             var clean = [];
22776             
22777             Roo.each(parts, function(p) {
22778                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22779                 if (!p.length) {
22780                     return true;
22781                 }
22782                 var l = p.split(':').shift().replace(/\s+/g,'');
22783                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22784                 
22785                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22786 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22787                     //node.removeAttribute(n);
22788                     return true;
22789                 }
22790                 //Roo.log()
22791                 // only allow 'c whitelisted system attributes'
22792                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22793 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22794                     //node.removeAttribute(n);
22795                     return true;
22796                 }
22797                 
22798                 
22799                  
22800                 
22801                 clean.push(p);
22802                 return true;
22803             });
22804             if (clean.length) { 
22805                 node.setAttribute(n, clean.join(';'));
22806             } else {
22807                 node.removeAttribute(n);
22808             }
22809             
22810         }
22811         
22812         
22813         for (var i = node.attributes.length-1; i > -1 ; i--) {
22814             var a = node.attributes[i];
22815             //console.log(a);
22816             
22817             if (a.name.toLowerCase().substr(0,2)=='on')  {
22818                 node.removeAttribute(a.name);
22819                 continue;
22820             }
22821             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22822                 node.removeAttribute(a.name);
22823                 continue;
22824             }
22825             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22826                 cleanAttr(a.name,a.value); // fixme..
22827                 continue;
22828             }
22829             if (a.name == 'style') {
22830                 cleanStyle(a.name,a.value);
22831                 continue;
22832             }
22833             /// clean up MS crap..
22834             // tecnically this should be a list of valid class'es..
22835             
22836             
22837             if (a.name == 'class') {
22838                 if (a.value.match(/^Mso/)) {
22839                     node.className = '';
22840                 }
22841                 
22842                 if (a.value.match(/^body$/)) {
22843                     node.className = '';
22844                 }
22845                 continue;
22846             }
22847             
22848             // style cleanup!?
22849             // class cleanup?
22850             
22851         }
22852         
22853         
22854         this.cleanUpChildren(node);
22855         
22856         
22857     },
22858     
22859     /**
22860      * Clean up MS wordisms...
22861      */
22862     cleanWord : function(node)
22863     {
22864         
22865         
22866         if (!node) {
22867             this.cleanWord(this.doc.body);
22868             return;
22869         }
22870         if (node.nodeName == "#text") {
22871             // clean up silly Windows -- stuff?
22872             return; 
22873         }
22874         if (node.nodeName == "#comment") {
22875             node.parentNode.removeChild(node);
22876             // clean up silly Windows -- stuff?
22877             return; 
22878         }
22879         
22880         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22881             node.parentNode.removeChild(node);
22882             return;
22883         }
22884         
22885         // remove - but keep children..
22886         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22887             while (node.childNodes.length) {
22888                 var cn = node.childNodes[0];
22889                 node.removeChild(cn);
22890                 node.parentNode.insertBefore(cn, node);
22891             }
22892             node.parentNode.removeChild(node);
22893             this.iterateChildren(node, this.cleanWord);
22894             return;
22895         }
22896         // clean styles
22897         if (node.className.length) {
22898             
22899             var cn = node.className.split(/\W+/);
22900             var cna = [];
22901             Roo.each(cn, function(cls) {
22902                 if (cls.match(/Mso[a-zA-Z]+/)) {
22903                     return;
22904                 }
22905                 cna.push(cls);
22906             });
22907             node.className = cna.length ? cna.join(' ') : '';
22908             if (!cna.length) {
22909                 node.removeAttribute("class");
22910             }
22911         }
22912         
22913         if (node.hasAttribute("lang")) {
22914             node.removeAttribute("lang");
22915         }
22916         
22917         if (node.hasAttribute("style")) {
22918             
22919             var styles = node.getAttribute("style").split(";");
22920             var nstyle = [];
22921             Roo.each(styles, function(s) {
22922                 if (!s.match(/:/)) {
22923                     return;
22924                 }
22925                 var kv = s.split(":");
22926                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22927                     return;
22928                 }
22929                 // what ever is left... we allow.
22930                 nstyle.push(s);
22931             });
22932             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22933             if (!nstyle.length) {
22934                 node.removeAttribute('style');
22935             }
22936         }
22937         this.iterateChildren(node, this.cleanWord);
22938         
22939         
22940         
22941     },
22942     /**
22943      * iterateChildren of a Node, calling fn each time, using this as the scole..
22944      * @param {DomNode} node node to iterate children of.
22945      * @param {Function} fn method of this class to call on each item.
22946      */
22947     iterateChildren : function(node, fn)
22948     {
22949         if (!node.childNodes.length) {
22950                 return;
22951         }
22952         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22953            fn.call(this, node.childNodes[i])
22954         }
22955     },
22956     
22957     
22958     /**
22959      * cleanTableWidths.
22960      *
22961      * Quite often pasting from word etc.. results in tables with column and widths.
22962      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22963      *
22964      */
22965     cleanTableWidths : function(node)
22966     {
22967          
22968          
22969         if (!node) {
22970             this.cleanTableWidths(this.doc.body);
22971             return;
22972         }
22973         
22974         // ignore list...
22975         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22976             return; 
22977         }
22978         Roo.log(node.tagName);
22979         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22980             this.iterateChildren(node, this.cleanTableWidths);
22981             return;
22982         }
22983         if (node.hasAttribute('width')) {
22984             node.removeAttribute('width');
22985         }
22986         
22987          
22988         if (node.hasAttribute("style")) {
22989             // pretty basic...
22990             
22991             var styles = node.getAttribute("style").split(";");
22992             var nstyle = [];
22993             Roo.each(styles, function(s) {
22994                 if (!s.match(/:/)) {
22995                     return;
22996                 }
22997                 var kv = s.split(":");
22998                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22999                     return;
23000                 }
23001                 // what ever is left... we allow.
23002                 nstyle.push(s);
23003             });
23004             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23005             if (!nstyle.length) {
23006                 node.removeAttribute('style');
23007             }
23008         }
23009         
23010         this.iterateChildren(node, this.cleanTableWidths);
23011         
23012         
23013     },
23014     
23015     
23016     
23017     
23018     domToHTML : function(currentElement, depth, nopadtext) {
23019         
23020         depth = depth || 0;
23021         nopadtext = nopadtext || false;
23022     
23023         if (!currentElement) {
23024             return this.domToHTML(this.doc.body);
23025         }
23026         
23027         //Roo.log(currentElement);
23028         var j;
23029         var allText = false;
23030         var nodeName = currentElement.nodeName;
23031         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23032         
23033         if  (nodeName == '#text') {
23034             
23035             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23036         }
23037         
23038         
23039         var ret = '';
23040         if (nodeName != 'BODY') {
23041              
23042             var i = 0;
23043             // Prints the node tagName, such as <A>, <IMG>, etc
23044             if (tagName) {
23045                 var attr = [];
23046                 for(i = 0; i < currentElement.attributes.length;i++) {
23047                     // quoting?
23048                     var aname = currentElement.attributes.item(i).name;
23049                     if (!currentElement.attributes.item(i).value.length) {
23050                         continue;
23051                     }
23052                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23053                 }
23054                 
23055                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23056             } 
23057             else {
23058                 
23059                 // eack
23060             }
23061         } else {
23062             tagName = false;
23063         }
23064         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23065             return ret;
23066         }
23067         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23068             nopadtext = true;
23069         }
23070         
23071         
23072         // Traverse the tree
23073         i = 0;
23074         var currentElementChild = currentElement.childNodes.item(i);
23075         var allText = true;
23076         var innerHTML  = '';
23077         lastnode = '';
23078         while (currentElementChild) {
23079             // Formatting code (indent the tree so it looks nice on the screen)
23080             var nopad = nopadtext;
23081             if (lastnode == 'SPAN') {
23082                 nopad  = true;
23083             }
23084             // text
23085             if  (currentElementChild.nodeName == '#text') {
23086                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23087                 toadd = nopadtext ? toadd : toadd.trim();
23088                 if (!nopad && toadd.length > 80) {
23089                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23090                 }
23091                 innerHTML  += toadd;
23092                 
23093                 i++;
23094                 currentElementChild = currentElement.childNodes.item(i);
23095                 lastNode = '';
23096                 continue;
23097             }
23098             allText = false;
23099             
23100             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23101                 
23102             // Recursively traverse the tree structure of the child node
23103             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23104             lastnode = currentElementChild.nodeName;
23105             i++;
23106             currentElementChild=currentElement.childNodes.item(i);
23107         }
23108         
23109         ret += innerHTML;
23110         
23111         if (!allText) {
23112                 // The remaining code is mostly for formatting the tree
23113             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23114         }
23115         
23116         
23117         if (tagName) {
23118             ret+= "</"+tagName+">";
23119         }
23120         return ret;
23121         
23122     },
23123         
23124     applyBlacklists : function()
23125     {
23126         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23127         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23128         
23129         this.white = [];
23130         this.black = [];
23131         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23132             if (b.indexOf(tag) > -1) {
23133                 return;
23134             }
23135             this.white.push(tag);
23136             
23137         }, this);
23138         
23139         Roo.each(w, function(tag) {
23140             if (b.indexOf(tag) > -1) {
23141                 return;
23142             }
23143             if (this.white.indexOf(tag) > -1) {
23144                 return;
23145             }
23146             this.white.push(tag);
23147             
23148         }, this);
23149         
23150         
23151         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23152             if (w.indexOf(tag) > -1) {
23153                 return;
23154             }
23155             this.black.push(tag);
23156             
23157         }, this);
23158         
23159         Roo.each(b, function(tag) {
23160             if (w.indexOf(tag) > -1) {
23161                 return;
23162             }
23163             if (this.black.indexOf(tag) > -1) {
23164                 return;
23165             }
23166             this.black.push(tag);
23167             
23168         }, this);
23169         
23170         
23171         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23172         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23173         
23174         this.cwhite = [];
23175         this.cblack = [];
23176         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23177             if (b.indexOf(tag) > -1) {
23178                 return;
23179             }
23180             this.cwhite.push(tag);
23181             
23182         }, this);
23183         
23184         Roo.each(w, function(tag) {
23185             if (b.indexOf(tag) > -1) {
23186                 return;
23187             }
23188             if (this.cwhite.indexOf(tag) > -1) {
23189                 return;
23190             }
23191             this.cwhite.push(tag);
23192             
23193         }, this);
23194         
23195         
23196         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23197             if (w.indexOf(tag) > -1) {
23198                 return;
23199             }
23200             this.cblack.push(tag);
23201             
23202         }, this);
23203         
23204         Roo.each(b, function(tag) {
23205             if (w.indexOf(tag) > -1) {
23206                 return;
23207             }
23208             if (this.cblack.indexOf(tag) > -1) {
23209                 return;
23210             }
23211             this.cblack.push(tag);
23212             
23213         }, this);
23214     },
23215     
23216     setStylesheets : function(stylesheets)
23217     {
23218         if(typeof(stylesheets) == 'string'){
23219             Roo.get(this.iframe.contentDocument.head).createChild({
23220                 tag : 'link',
23221                 rel : 'stylesheet',
23222                 type : 'text/css',
23223                 href : stylesheets
23224             });
23225             
23226             return;
23227         }
23228         var _this = this;
23229      
23230         Roo.each(stylesheets, function(s) {
23231             if(!s.length){
23232                 return;
23233             }
23234             
23235             Roo.get(_this.iframe.contentDocument.head).createChild({
23236                 tag : 'link',
23237                 rel : 'stylesheet',
23238                 type : 'text/css',
23239                 href : s
23240             });
23241         });
23242
23243         
23244     },
23245     
23246     removeStylesheets : function()
23247     {
23248         var _this = this;
23249         
23250         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23251             s.remove();
23252         });
23253     },
23254     
23255     setStyle : function(style)
23256     {
23257         Roo.get(this.iframe.contentDocument.head).createChild({
23258             tag : 'style',
23259             type : 'text/css',
23260             html : style
23261         });
23262
23263         return;
23264     }
23265     
23266     // hide stuff that is not compatible
23267     /**
23268      * @event blur
23269      * @hide
23270      */
23271     /**
23272      * @event change
23273      * @hide
23274      */
23275     /**
23276      * @event focus
23277      * @hide
23278      */
23279     /**
23280      * @event specialkey
23281      * @hide
23282      */
23283     /**
23284      * @cfg {String} fieldClass @hide
23285      */
23286     /**
23287      * @cfg {String} focusClass @hide
23288      */
23289     /**
23290      * @cfg {String} autoCreate @hide
23291      */
23292     /**
23293      * @cfg {String} inputType @hide
23294      */
23295     /**
23296      * @cfg {String} invalidClass @hide
23297      */
23298     /**
23299      * @cfg {String} invalidText @hide
23300      */
23301     /**
23302      * @cfg {String} msgFx @hide
23303      */
23304     /**
23305      * @cfg {String} validateOnBlur @hide
23306      */
23307 });
23308
23309 Roo.HtmlEditorCore.white = [
23310         'area', 'br', 'img', 'input', 'hr', 'wbr',
23311         
23312        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23313        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23314        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23315        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23316        'table',   'ul',         'xmp', 
23317        
23318        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23319       'thead',   'tr', 
23320      
23321       'dir', 'menu', 'ol', 'ul', 'dl',
23322        
23323       'embed',  'object'
23324 ];
23325
23326
23327 Roo.HtmlEditorCore.black = [
23328     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23329         'applet', // 
23330         'base',   'basefont', 'bgsound', 'blink',  'body', 
23331         'frame',  'frameset', 'head',    'html',   'ilayer', 
23332         'iframe', 'layer',  'link',     'meta',    'object',   
23333         'script', 'style' ,'title',  'xml' // clean later..
23334 ];
23335 Roo.HtmlEditorCore.clean = [
23336     'script', 'style', 'title', 'xml'
23337 ];
23338 Roo.HtmlEditorCore.remove = [
23339     'font'
23340 ];
23341 // attributes..
23342
23343 Roo.HtmlEditorCore.ablack = [
23344     'on'
23345 ];
23346     
23347 Roo.HtmlEditorCore.aclean = [ 
23348     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23349 ];
23350
23351 // protocols..
23352 Roo.HtmlEditorCore.pwhite= [
23353         'http',  'https',  'mailto'
23354 ];
23355
23356 // white listed style attributes.
23357 Roo.HtmlEditorCore.cwhite= [
23358       //  'text-align', /// default is to allow most things..
23359       
23360          
23361 //        'font-size'//??
23362 ];
23363
23364 // black listed style attributes.
23365 Roo.HtmlEditorCore.cblack= [
23366       //  'font-size' -- this can be set by the project 
23367 ];
23368
23369
23370 Roo.HtmlEditorCore.swapCodes   =[ 
23371     [    8211, "--" ], 
23372     [    8212, "--" ], 
23373     [    8216,  "'" ],  
23374     [    8217, "'" ],  
23375     [    8220, '"' ],  
23376     [    8221, '"' ],  
23377     [    8226, "*" ],  
23378     [    8230, "..." ]
23379 ]; 
23380
23381     /*
23382  * - LGPL
23383  *
23384  * HtmlEditor
23385  * 
23386  */
23387
23388 /**
23389  * @class Roo.bootstrap.HtmlEditor
23390  * @extends Roo.bootstrap.TextArea
23391  * Bootstrap HtmlEditor class
23392
23393  * @constructor
23394  * Create a new HtmlEditor
23395  * @param {Object} config The config object
23396  */
23397
23398 Roo.bootstrap.HtmlEditor = function(config){
23399     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23400     if (!this.toolbars) {
23401         this.toolbars = [];
23402     }
23403     
23404     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23405     this.addEvents({
23406             /**
23407              * @event initialize
23408              * Fires when the editor is fully initialized (including the iframe)
23409              * @param {HtmlEditor} this
23410              */
23411             initialize: true,
23412             /**
23413              * @event activate
23414              * Fires when the editor is first receives the focus. Any insertion must wait
23415              * until after this event.
23416              * @param {HtmlEditor} this
23417              */
23418             activate: true,
23419              /**
23420              * @event beforesync
23421              * Fires before the textarea is updated with content from the editor iframe. Return false
23422              * to cancel the sync.
23423              * @param {HtmlEditor} this
23424              * @param {String} html
23425              */
23426             beforesync: true,
23427              /**
23428              * @event beforepush
23429              * Fires before the iframe editor is updated with content from the textarea. Return false
23430              * to cancel the push.
23431              * @param {HtmlEditor} this
23432              * @param {String} html
23433              */
23434             beforepush: true,
23435              /**
23436              * @event sync
23437              * Fires when the textarea is updated with content from the editor iframe.
23438              * @param {HtmlEditor} this
23439              * @param {String} html
23440              */
23441             sync: true,
23442              /**
23443              * @event push
23444              * Fires when the iframe editor is updated with content from the textarea.
23445              * @param {HtmlEditor} this
23446              * @param {String} html
23447              */
23448             push: true,
23449              /**
23450              * @event editmodechange
23451              * Fires when the editor switches edit modes
23452              * @param {HtmlEditor} this
23453              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23454              */
23455             editmodechange: true,
23456             /**
23457              * @event editorevent
23458              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23459              * @param {HtmlEditor} this
23460              */
23461             editorevent: true,
23462             /**
23463              * @event firstfocus
23464              * Fires when on first focus - needed by toolbars..
23465              * @param {HtmlEditor} this
23466              */
23467             firstfocus: true,
23468             /**
23469              * @event autosave
23470              * Auto save the htmlEditor value as a file into Events
23471              * @param {HtmlEditor} this
23472              */
23473             autosave: true,
23474             /**
23475              * @event savedpreview
23476              * preview the saved version of htmlEditor
23477              * @param {HtmlEditor} this
23478              */
23479             savedpreview: true
23480         });
23481 };
23482
23483
23484 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23485     
23486     
23487       /**
23488      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23489      */
23490     toolbars : false,
23491     
23492      /**
23493     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23494     */
23495     btns : [],
23496    
23497      /**
23498      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23499      *                        Roo.resizable.
23500      */
23501     resizable : false,
23502      /**
23503      * @cfg {Number} height (in pixels)
23504      */   
23505     height: 300,
23506    /**
23507      * @cfg {Number} width (in pixels)
23508      */   
23509     width: false,
23510     
23511     /**
23512      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23513      * 
23514      */
23515     stylesheets: false,
23516     
23517     // id of frame..
23518     frameId: false,
23519     
23520     // private properties
23521     validationEvent : false,
23522     deferHeight: true,
23523     initialized : false,
23524     activated : false,
23525     
23526     onFocus : Roo.emptyFn,
23527     iframePad:3,
23528     hideMode:'offsets',
23529     
23530     tbContainer : false,
23531     
23532     bodyCls : '',
23533     
23534     toolbarContainer :function() {
23535         return this.wrap.select('.x-html-editor-tb',true).first();
23536     },
23537
23538     /**
23539      * Protected method that will not generally be called directly. It
23540      * is called when the editor creates its toolbar. Override this method if you need to
23541      * add custom toolbar buttons.
23542      * @param {HtmlEditor} editor
23543      */
23544     createToolbar : function(){
23545         Roo.log('renewing');
23546         Roo.log("create toolbars");
23547         
23548         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23549         this.toolbars[0].render(this.toolbarContainer());
23550         
23551         return;
23552         
23553 //        if (!editor.toolbars || !editor.toolbars.length) {
23554 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23555 //        }
23556 //        
23557 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23558 //            editor.toolbars[i] = Roo.factory(
23559 //                    typeof(editor.toolbars[i]) == 'string' ?
23560 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23561 //                Roo.bootstrap.HtmlEditor);
23562 //            editor.toolbars[i].init(editor);
23563 //        }
23564     },
23565
23566      
23567     // private
23568     onRender : function(ct, position)
23569     {
23570        // Roo.log("Call onRender: " + this.xtype);
23571         var _t = this;
23572         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23573       
23574         this.wrap = this.inputEl().wrap({
23575             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23576         });
23577         
23578         this.editorcore.onRender(ct, position);
23579          
23580         if (this.resizable) {
23581             this.resizeEl = new Roo.Resizable(this.wrap, {
23582                 pinned : true,
23583                 wrap: true,
23584                 dynamic : true,
23585                 minHeight : this.height,
23586                 height: this.height,
23587                 handles : this.resizable,
23588                 width: this.width,
23589                 listeners : {
23590                     resize : function(r, w, h) {
23591                         _t.onResize(w,h); // -something
23592                     }
23593                 }
23594             });
23595             
23596         }
23597         this.createToolbar(this);
23598        
23599         
23600         if(!this.width && this.resizable){
23601             this.setSize(this.wrap.getSize());
23602         }
23603         if (this.resizeEl) {
23604             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23605             // should trigger onReize..
23606         }
23607         
23608     },
23609
23610     // private
23611     onResize : function(w, h)
23612     {
23613         Roo.log('resize: ' +w + ',' + h );
23614         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23615         var ew = false;
23616         var eh = false;
23617         
23618         if(this.inputEl() ){
23619             if(typeof w == 'number'){
23620                 var aw = w - this.wrap.getFrameWidth('lr');
23621                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23622                 ew = aw;
23623             }
23624             if(typeof h == 'number'){
23625                  var tbh = -11;  // fixme it needs to tool bar size!
23626                 for (var i =0; i < this.toolbars.length;i++) {
23627                     // fixme - ask toolbars for heights?
23628                     tbh += this.toolbars[i].el.getHeight();
23629                     //if (this.toolbars[i].footer) {
23630                     //    tbh += this.toolbars[i].footer.el.getHeight();
23631                     //}
23632                 }
23633               
23634                 
23635                 
23636                 
23637                 
23638                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23639                 ah -= 5; // knock a few pixes off for look..
23640                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23641                 var eh = ah;
23642             }
23643         }
23644         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23645         this.editorcore.onResize(ew,eh);
23646         
23647     },
23648
23649     /**
23650      * Toggles the editor between standard and source edit mode.
23651      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23652      */
23653     toggleSourceEdit : function(sourceEditMode)
23654     {
23655         this.editorcore.toggleSourceEdit(sourceEditMode);
23656         
23657         if(this.editorcore.sourceEditMode){
23658             Roo.log('editor - showing textarea');
23659             
23660 //            Roo.log('in');
23661 //            Roo.log(this.syncValue());
23662             this.syncValue();
23663             this.inputEl().removeClass(['hide', 'x-hidden']);
23664             this.inputEl().dom.removeAttribute('tabIndex');
23665             this.inputEl().focus();
23666         }else{
23667             Roo.log('editor - hiding textarea');
23668 //            Roo.log('out')
23669 //            Roo.log(this.pushValue()); 
23670             this.pushValue();
23671             
23672             this.inputEl().addClass(['hide', 'x-hidden']);
23673             this.inputEl().dom.setAttribute('tabIndex', -1);
23674             //this.deferFocus();
23675         }
23676          
23677         if(this.resizable){
23678             this.setSize(this.wrap.getSize());
23679         }
23680         
23681         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23682     },
23683  
23684     // private (for BoxComponent)
23685     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23686
23687     // private (for BoxComponent)
23688     getResizeEl : function(){
23689         return this.wrap;
23690     },
23691
23692     // private (for BoxComponent)
23693     getPositionEl : function(){
23694         return this.wrap;
23695     },
23696
23697     // private
23698     initEvents : function(){
23699         this.originalValue = this.getValue();
23700     },
23701
23702 //    /**
23703 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23704 //     * @method
23705 //     */
23706 //    markInvalid : Roo.emptyFn,
23707 //    /**
23708 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23709 //     * @method
23710 //     */
23711 //    clearInvalid : Roo.emptyFn,
23712
23713     setValue : function(v){
23714         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23715         this.editorcore.pushValue();
23716     },
23717
23718      
23719     // private
23720     deferFocus : function(){
23721         this.focus.defer(10, this);
23722     },
23723
23724     // doc'ed in Field
23725     focus : function(){
23726         this.editorcore.focus();
23727         
23728     },
23729       
23730
23731     // private
23732     onDestroy : function(){
23733         
23734         
23735         
23736         if(this.rendered){
23737             
23738             for (var i =0; i < this.toolbars.length;i++) {
23739                 // fixme - ask toolbars for heights?
23740                 this.toolbars[i].onDestroy();
23741             }
23742             
23743             this.wrap.dom.innerHTML = '';
23744             this.wrap.remove();
23745         }
23746     },
23747
23748     // private
23749     onFirstFocus : function(){
23750         //Roo.log("onFirstFocus");
23751         this.editorcore.onFirstFocus();
23752          for (var i =0; i < this.toolbars.length;i++) {
23753             this.toolbars[i].onFirstFocus();
23754         }
23755         
23756     },
23757     
23758     // private
23759     syncValue : function()
23760     {   
23761         this.editorcore.syncValue();
23762     },
23763     
23764     pushValue : function()
23765     {   
23766         this.editorcore.pushValue();
23767     }
23768      
23769     
23770     // hide stuff that is not compatible
23771     /**
23772      * @event blur
23773      * @hide
23774      */
23775     /**
23776      * @event change
23777      * @hide
23778      */
23779     /**
23780      * @event focus
23781      * @hide
23782      */
23783     /**
23784      * @event specialkey
23785      * @hide
23786      */
23787     /**
23788      * @cfg {String} fieldClass @hide
23789      */
23790     /**
23791      * @cfg {String} focusClass @hide
23792      */
23793     /**
23794      * @cfg {String} autoCreate @hide
23795      */
23796     /**
23797      * @cfg {String} inputType @hide
23798      */
23799     /**
23800      * @cfg {String} invalidClass @hide
23801      */
23802     /**
23803      * @cfg {String} invalidText @hide
23804      */
23805     /**
23806      * @cfg {String} msgFx @hide
23807      */
23808     /**
23809      * @cfg {String} validateOnBlur @hide
23810      */
23811 });
23812  
23813     
23814    
23815    
23816    
23817       
23818 Roo.namespace('Roo.bootstrap.htmleditor');
23819 /**
23820  * @class Roo.bootstrap.HtmlEditorToolbar1
23821  * Basic Toolbar
23822  * 
23823  * Usage:
23824  *
23825  new Roo.bootstrap.HtmlEditor({
23826     ....
23827     toolbars : [
23828         new Roo.bootstrap.HtmlEditorToolbar1({
23829             disable : { fonts: 1 , format: 1, ..., ... , ...],
23830             btns : [ .... ]
23831         })
23832     }
23833      
23834  * 
23835  * @cfg {Object} disable List of elements to disable..
23836  * @cfg {Array} btns List of additional buttons.
23837  * 
23838  * 
23839  * NEEDS Extra CSS? 
23840  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23841  */
23842  
23843 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23844 {
23845     
23846     Roo.apply(this, config);
23847     
23848     // default disabled, based on 'good practice'..
23849     this.disable = this.disable || {};
23850     Roo.applyIf(this.disable, {
23851         fontSize : true,
23852         colors : true,
23853         specialElements : true
23854     });
23855     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23856     
23857     this.editor = config.editor;
23858     this.editorcore = config.editor.editorcore;
23859     
23860     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23861     
23862     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23863     // dont call parent... till later.
23864 }
23865 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23866      
23867     bar : true,
23868     
23869     editor : false,
23870     editorcore : false,
23871     
23872     
23873     formats : [
23874         "p" ,  
23875         "h1","h2","h3","h4","h5","h6", 
23876         "pre", "code", 
23877         "abbr", "acronym", "address", "cite", "samp", "var",
23878         'div','span'
23879     ],
23880     
23881     onRender : function(ct, position)
23882     {
23883        // Roo.log("Call onRender: " + this.xtype);
23884         
23885        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23886        Roo.log(this.el);
23887        this.el.dom.style.marginBottom = '0';
23888        var _this = this;
23889        var editorcore = this.editorcore;
23890        var editor= this.editor;
23891        
23892        var children = [];
23893        var btn = function(id,cmd , toggle, handler, html){
23894        
23895             var  event = toggle ? 'toggle' : 'click';
23896        
23897             var a = {
23898                 size : 'sm',
23899                 xtype: 'Button',
23900                 xns: Roo.bootstrap,
23901                 glyphicon : id,
23902                 cmd : id || cmd,
23903                 enableToggle:toggle !== false,
23904                 html : html || '',
23905                 pressed : toggle ? false : null,
23906                 listeners : {}
23907             };
23908             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23909                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23910             };
23911             children.push(a);
23912             return a;
23913        }
23914        
23915     //    var cb_box = function...
23916         
23917         var style = {
23918                 xtype: 'Button',
23919                 size : 'sm',
23920                 xns: Roo.bootstrap,
23921                 glyphicon : 'font',
23922                 //html : 'submit'
23923                 menu : {
23924                     xtype: 'Menu',
23925                     xns: Roo.bootstrap,
23926                     items:  []
23927                 }
23928         };
23929         Roo.each(this.formats, function(f) {
23930             style.menu.items.push({
23931                 xtype :'MenuItem',
23932                 xns: Roo.bootstrap,
23933                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23934                 tagname : f,
23935                 listeners : {
23936                     click : function()
23937                     {
23938                         editorcore.insertTag(this.tagname);
23939                         editor.focus();
23940                     }
23941                 }
23942                 
23943             });
23944         });
23945         children.push(style);   
23946         
23947         btn('bold',false,true);
23948         btn('italic',false,true);
23949         btn('align-left', 'justifyleft',true);
23950         btn('align-center', 'justifycenter',true);
23951         btn('align-right' , 'justifyright',true);
23952         btn('link', false, false, function(btn) {
23953             //Roo.log("create link?");
23954             var url = prompt(this.createLinkText, this.defaultLinkValue);
23955             if(url && url != 'http:/'+'/'){
23956                 this.editorcore.relayCmd('createlink', url);
23957             }
23958         }),
23959         btn('list','insertunorderedlist',true);
23960         btn('pencil', false,true, function(btn){
23961                 Roo.log(this);
23962                 this.toggleSourceEdit(btn.pressed);
23963         });
23964         
23965         if (this.editor.btns.length > 0) {
23966             for (var i = 0; i<this.editor.btns.length; i++) {
23967                 children.push(this.editor.btns[i]);
23968             }
23969         }
23970         
23971         /*
23972         var cog = {
23973                 xtype: 'Button',
23974                 size : 'sm',
23975                 xns: Roo.bootstrap,
23976                 glyphicon : 'cog',
23977                 //html : 'submit'
23978                 menu : {
23979                     xtype: 'Menu',
23980                     xns: Roo.bootstrap,
23981                     items:  []
23982                 }
23983         };
23984         
23985         cog.menu.items.push({
23986             xtype :'MenuItem',
23987             xns: Roo.bootstrap,
23988             html : Clean styles,
23989             tagname : f,
23990             listeners : {
23991                 click : function()
23992                 {
23993                     editorcore.insertTag(this.tagname);
23994                     editor.focus();
23995                 }
23996             }
23997             
23998         });
23999        */
24000         
24001          
24002        this.xtype = 'NavSimplebar';
24003         
24004         for(var i=0;i< children.length;i++) {
24005             
24006             this.buttons.add(this.addxtypeChild(children[i]));
24007             
24008         }
24009         
24010         editor.on('editorevent', this.updateToolbar, this);
24011     },
24012     onBtnClick : function(id)
24013     {
24014        this.editorcore.relayCmd(id);
24015        this.editorcore.focus();
24016     },
24017     
24018     /**
24019      * Protected method that will not generally be called directly. It triggers
24020      * a toolbar update by reading the markup state of the current selection in the editor.
24021      */
24022     updateToolbar: function(){
24023
24024         if(!this.editorcore.activated){
24025             this.editor.onFirstFocus(); // is this neeed?
24026             return;
24027         }
24028
24029         var btns = this.buttons; 
24030         var doc = this.editorcore.doc;
24031         btns.get('bold').setActive(doc.queryCommandState('bold'));
24032         btns.get('italic').setActive(doc.queryCommandState('italic'));
24033         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24034         
24035         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24036         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24037         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24038         
24039         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24040         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24041          /*
24042         
24043         var ans = this.editorcore.getAllAncestors();
24044         if (this.formatCombo) {
24045             
24046             
24047             var store = this.formatCombo.store;
24048             this.formatCombo.setValue("");
24049             for (var i =0; i < ans.length;i++) {
24050                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24051                     // select it..
24052                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24053                     break;
24054                 }
24055             }
24056         }
24057         
24058         
24059         
24060         // hides menus... - so this cant be on a menu...
24061         Roo.bootstrap.MenuMgr.hideAll();
24062         */
24063         Roo.bootstrap.MenuMgr.hideAll();
24064         //this.editorsyncValue();
24065     },
24066     onFirstFocus: function() {
24067         this.buttons.each(function(item){
24068            item.enable();
24069         });
24070     },
24071     toggleSourceEdit : function(sourceEditMode){
24072         
24073           
24074         if(sourceEditMode){
24075             Roo.log("disabling buttons");
24076            this.buttons.each( function(item){
24077                 if(item.cmd != 'pencil'){
24078                     item.disable();
24079                 }
24080             });
24081           
24082         }else{
24083             Roo.log("enabling buttons");
24084             if(this.editorcore.initialized){
24085                 this.buttons.each( function(item){
24086                     item.enable();
24087                 });
24088             }
24089             
24090         }
24091         Roo.log("calling toggole on editor");
24092         // tell the editor that it's been pressed..
24093         this.editor.toggleSourceEdit(sourceEditMode);
24094        
24095     }
24096 });
24097
24098
24099
24100
24101
24102 /**
24103  * @class Roo.bootstrap.Table.AbstractSelectionModel
24104  * @extends Roo.util.Observable
24105  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24106  * implemented by descendant classes.  This class should not be directly instantiated.
24107  * @constructor
24108  */
24109 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24110     this.locked = false;
24111     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24112 };
24113
24114
24115 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24116     /** @ignore Called by the grid automatically. Do not call directly. */
24117     init : function(grid){
24118         this.grid = grid;
24119         this.initEvents();
24120     },
24121
24122     /**
24123      * Locks the selections.
24124      */
24125     lock : function(){
24126         this.locked = true;
24127     },
24128
24129     /**
24130      * Unlocks the selections.
24131      */
24132     unlock : function(){
24133         this.locked = false;
24134     },
24135
24136     /**
24137      * Returns true if the selections are locked.
24138      * @return {Boolean}
24139      */
24140     isLocked : function(){
24141         return this.locked;
24142     }
24143 });
24144 /**
24145  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24146  * @class Roo.bootstrap.Table.RowSelectionModel
24147  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24148  * It supports multiple selections and keyboard selection/navigation. 
24149  * @constructor
24150  * @param {Object} config
24151  */
24152
24153 Roo.bootstrap.Table.RowSelectionModel = function(config){
24154     Roo.apply(this, config);
24155     this.selections = new Roo.util.MixedCollection(false, function(o){
24156         return o.id;
24157     });
24158
24159     this.last = false;
24160     this.lastActive = false;
24161
24162     this.addEvents({
24163         /**
24164              * @event selectionchange
24165              * Fires when the selection changes
24166              * @param {SelectionModel} this
24167              */
24168             "selectionchange" : true,
24169         /**
24170              * @event afterselectionchange
24171              * Fires after the selection changes (eg. by key press or clicking)
24172              * @param {SelectionModel} this
24173              */
24174             "afterselectionchange" : true,
24175         /**
24176              * @event beforerowselect
24177              * Fires when a row is selected being selected, return false to cancel.
24178              * @param {SelectionModel} this
24179              * @param {Number} rowIndex The selected index
24180              * @param {Boolean} keepExisting False if other selections will be cleared
24181              */
24182             "beforerowselect" : true,
24183         /**
24184              * @event rowselect
24185              * Fires when a row is selected.
24186              * @param {SelectionModel} this
24187              * @param {Number} rowIndex The selected index
24188              * @param {Roo.data.Record} r The record
24189              */
24190             "rowselect" : true,
24191         /**
24192              * @event rowdeselect
24193              * Fires when a row is deselected.
24194              * @param {SelectionModel} this
24195              * @param {Number} rowIndex The selected index
24196              */
24197         "rowdeselect" : true
24198     });
24199     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24200     this.locked = false;
24201  };
24202
24203 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24204     /**
24205      * @cfg {Boolean} singleSelect
24206      * True to allow selection of only one row at a time (defaults to false)
24207      */
24208     singleSelect : false,
24209
24210     // private
24211     initEvents : function()
24212     {
24213
24214         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24215         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24216         //}else{ // allow click to work like normal
24217          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24218         //}
24219         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24220         this.grid.on("rowclick", this.handleMouseDown, this);
24221         
24222         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24223             "up" : function(e){
24224                 if(!e.shiftKey){
24225                     this.selectPrevious(e.shiftKey);
24226                 }else if(this.last !== false && this.lastActive !== false){
24227                     var last = this.last;
24228                     this.selectRange(this.last,  this.lastActive-1);
24229                     this.grid.getView().focusRow(this.lastActive);
24230                     if(last !== false){
24231                         this.last = last;
24232                     }
24233                 }else{
24234                     this.selectFirstRow();
24235                 }
24236                 this.fireEvent("afterselectionchange", this);
24237             },
24238             "down" : function(e){
24239                 if(!e.shiftKey){
24240                     this.selectNext(e.shiftKey);
24241                 }else if(this.last !== false && this.lastActive !== false){
24242                     var last = this.last;
24243                     this.selectRange(this.last,  this.lastActive+1);
24244                     this.grid.getView().focusRow(this.lastActive);
24245                     if(last !== false){
24246                         this.last = last;
24247                     }
24248                 }else{
24249                     this.selectFirstRow();
24250                 }
24251                 this.fireEvent("afterselectionchange", this);
24252             },
24253             scope: this
24254         });
24255         this.grid.store.on('load', function(){
24256             this.selections.clear();
24257         },this);
24258         /*
24259         var view = this.grid.view;
24260         view.on("refresh", this.onRefresh, this);
24261         view.on("rowupdated", this.onRowUpdated, this);
24262         view.on("rowremoved", this.onRemove, this);
24263         */
24264     },
24265
24266     // private
24267     onRefresh : function()
24268     {
24269         var ds = this.grid.store, i, v = this.grid.view;
24270         var s = this.selections;
24271         s.each(function(r){
24272             if((i = ds.indexOfId(r.id)) != -1){
24273                 v.onRowSelect(i);
24274             }else{
24275                 s.remove(r);
24276             }
24277         });
24278     },
24279
24280     // private
24281     onRemove : function(v, index, r){
24282         this.selections.remove(r);
24283     },
24284
24285     // private
24286     onRowUpdated : function(v, index, r){
24287         if(this.isSelected(r)){
24288             v.onRowSelect(index);
24289         }
24290     },
24291
24292     /**
24293      * Select records.
24294      * @param {Array} records The records to select
24295      * @param {Boolean} keepExisting (optional) True to keep existing selections
24296      */
24297     selectRecords : function(records, keepExisting)
24298     {
24299         if(!keepExisting){
24300             this.clearSelections();
24301         }
24302             var ds = this.grid.store;
24303         for(var i = 0, len = records.length; i < len; i++){
24304             this.selectRow(ds.indexOf(records[i]), true);
24305         }
24306     },
24307
24308     /**
24309      * Gets the number of selected rows.
24310      * @return {Number}
24311      */
24312     getCount : function(){
24313         return this.selections.length;
24314     },
24315
24316     /**
24317      * Selects the first row in the grid.
24318      */
24319     selectFirstRow : function(){
24320         this.selectRow(0);
24321     },
24322
24323     /**
24324      * Select the last row.
24325      * @param {Boolean} keepExisting (optional) True to keep existing selections
24326      */
24327     selectLastRow : function(keepExisting){
24328         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24329         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24330     },
24331
24332     /**
24333      * Selects the row immediately following the last selected row.
24334      * @param {Boolean} keepExisting (optional) True to keep existing selections
24335      */
24336     selectNext : function(keepExisting)
24337     {
24338             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24339             this.selectRow(this.last+1, keepExisting);
24340             this.grid.getView().focusRow(this.last);
24341         }
24342     },
24343
24344     /**
24345      * Selects the row that precedes the last selected row.
24346      * @param {Boolean} keepExisting (optional) True to keep existing selections
24347      */
24348     selectPrevious : function(keepExisting){
24349         if(this.last){
24350             this.selectRow(this.last-1, keepExisting);
24351             this.grid.getView().focusRow(this.last);
24352         }
24353     },
24354
24355     /**
24356      * Returns the selected records
24357      * @return {Array} Array of selected records
24358      */
24359     getSelections : function(){
24360         return [].concat(this.selections.items);
24361     },
24362
24363     /**
24364      * Returns the first selected record.
24365      * @return {Record}
24366      */
24367     getSelected : function(){
24368         return this.selections.itemAt(0);
24369     },
24370
24371
24372     /**
24373      * Clears all selections.
24374      */
24375     clearSelections : function(fast)
24376     {
24377         if(this.locked) {
24378             return;
24379         }
24380         if(fast !== true){
24381                 var ds = this.grid.store;
24382             var s = this.selections;
24383             s.each(function(r){
24384                 this.deselectRow(ds.indexOfId(r.id));
24385             }, this);
24386             s.clear();
24387         }else{
24388             this.selections.clear();
24389         }
24390         this.last = false;
24391     },
24392
24393
24394     /**
24395      * Selects all rows.
24396      */
24397     selectAll : function(){
24398         if(this.locked) {
24399             return;
24400         }
24401         this.selections.clear();
24402         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24403             this.selectRow(i, true);
24404         }
24405     },
24406
24407     /**
24408      * Returns True if there is a selection.
24409      * @return {Boolean}
24410      */
24411     hasSelection : function(){
24412         return this.selections.length > 0;
24413     },
24414
24415     /**
24416      * Returns True if the specified row is selected.
24417      * @param {Number/Record} record The record or index of the record to check
24418      * @return {Boolean}
24419      */
24420     isSelected : function(index){
24421             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24422         return (r && this.selections.key(r.id) ? true : false);
24423     },
24424
24425     /**
24426      * Returns True if the specified record id is selected.
24427      * @param {String} id The id of record to check
24428      * @return {Boolean}
24429      */
24430     isIdSelected : function(id){
24431         return (this.selections.key(id) ? true : false);
24432     },
24433
24434
24435     // private
24436     handleMouseDBClick : function(e, t){
24437         
24438     },
24439     // private
24440     handleMouseDown : function(e, t)
24441     {
24442             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24443         if(this.isLocked() || rowIndex < 0 ){
24444             return;
24445         };
24446         if(e.shiftKey && this.last !== false){
24447             var last = this.last;
24448             this.selectRange(last, rowIndex, e.ctrlKey);
24449             this.last = last; // reset the last
24450             t.focus();
24451     
24452         }else{
24453             var isSelected = this.isSelected(rowIndex);
24454             //Roo.log("select row:" + rowIndex);
24455             if(isSelected){
24456                 this.deselectRow(rowIndex);
24457             } else {
24458                         this.selectRow(rowIndex, true);
24459             }
24460     
24461             /*
24462                 if(e.button !== 0 && isSelected){
24463                 alert('rowIndex 2: ' + rowIndex);
24464                     view.focusRow(rowIndex);
24465                 }else if(e.ctrlKey && isSelected){
24466                     this.deselectRow(rowIndex);
24467                 }else if(!isSelected){
24468                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24469                     view.focusRow(rowIndex);
24470                 }
24471             */
24472         }
24473         this.fireEvent("afterselectionchange", this);
24474     },
24475     // private
24476     handleDragableRowClick :  function(grid, rowIndex, e) 
24477     {
24478         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24479             this.selectRow(rowIndex, false);
24480             grid.view.focusRow(rowIndex);
24481              this.fireEvent("afterselectionchange", this);
24482         }
24483     },
24484     
24485     /**
24486      * Selects multiple rows.
24487      * @param {Array} rows Array of the indexes of the row to select
24488      * @param {Boolean} keepExisting (optional) True to keep existing selections
24489      */
24490     selectRows : function(rows, keepExisting){
24491         if(!keepExisting){
24492             this.clearSelections();
24493         }
24494         for(var i = 0, len = rows.length; i < len; i++){
24495             this.selectRow(rows[i], true);
24496         }
24497     },
24498
24499     /**
24500      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24501      * @param {Number} startRow The index of the first row in the range
24502      * @param {Number} endRow The index of the last row in the range
24503      * @param {Boolean} keepExisting (optional) True to retain existing selections
24504      */
24505     selectRange : function(startRow, endRow, keepExisting){
24506         if(this.locked) {
24507             return;
24508         }
24509         if(!keepExisting){
24510             this.clearSelections();
24511         }
24512         if(startRow <= endRow){
24513             for(var i = startRow; i <= endRow; i++){
24514                 this.selectRow(i, true);
24515             }
24516         }else{
24517             for(var i = startRow; i >= endRow; i--){
24518                 this.selectRow(i, true);
24519             }
24520         }
24521     },
24522
24523     /**
24524      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24525      * @param {Number} startRow The index of the first row in the range
24526      * @param {Number} endRow The index of the last row in the range
24527      */
24528     deselectRange : function(startRow, endRow, preventViewNotify){
24529         if(this.locked) {
24530             return;
24531         }
24532         for(var i = startRow; i <= endRow; i++){
24533             this.deselectRow(i, preventViewNotify);
24534         }
24535     },
24536
24537     /**
24538      * Selects a row.
24539      * @param {Number} row The index of the row to select
24540      * @param {Boolean} keepExisting (optional) True to keep existing selections
24541      */
24542     selectRow : function(index, keepExisting, preventViewNotify)
24543     {
24544             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24545             return;
24546         }
24547         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24548             if(!keepExisting || this.singleSelect){
24549                 this.clearSelections();
24550             }
24551             
24552             var r = this.grid.store.getAt(index);
24553             //console.log('selectRow - record id :' + r.id);
24554             
24555             this.selections.add(r);
24556             this.last = this.lastActive = index;
24557             if(!preventViewNotify){
24558                 var proxy = new Roo.Element(
24559                                 this.grid.getRowDom(index)
24560                 );
24561                 proxy.addClass('bg-info info');
24562             }
24563             this.fireEvent("rowselect", this, index, r);
24564             this.fireEvent("selectionchange", this);
24565         }
24566     },
24567
24568     /**
24569      * Deselects a row.
24570      * @param {Number} row The index of the row to deselect
24571      */
24572     deselectRow : function(index, preventViewNotify)
24573     {
24574         if(this.locked) {
24575             return;
24576         }
24577         if(this.last == index){
24578             this.last = false;
24579         }
24580         if(this.lastActive == index){
24581             this.lastActive = false;
24582         }
24583         
24584         var r = this.grid.store.getAt(index);
24585         if (!r) {
24586             return;
24587         }
24588         
24589         this.selections.remove(r);
24590         //.console.log('deselectRow - record id :' + r.id);
24591         if(!preventViewNotify){
24592         
24593             var proxy = new Roo.Element(
24594                 this.grid.getRowDom(index)
24595             );
24596             proxy.removeClass('bg-info info');
24597         }
24598         this.fireEvent("rowdeselect", this, index);
24599         this.fireEvent("selectionchange", this);
24600     },
24601
24602     // private
24603     restoreLast : function(){
24604         if(this._last){
24605             this.last = this._last;
24606         }
24607     },
24608
24609     // private
24610     acceptsNav : function(row, col, cm){
24611         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24612     },
24613
24614     // private
24615     onEditorKey : function(field, e){
24616         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24617         if(k == e.TAB){
24618             e.stopEvent();
24619             ed.completeEdit();
24620             if(e.shiftKey){
24621                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24622             }else{
24623                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24624             }
24625         }else if(k == e.ENTER && !e.ctrlKey){
24626             e.stopEvent();
24627             ed.completeEdit();
24628             if(e.shiftKey){
24629                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24630             }else{
24631                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24632             }
24633         }else if(k == e.ESC){
24634             ed.cancelEdit();
24635         }
24636         if(newCell){
24637             g.startEditing(newCell[0], newCell[1]);
24638         }
24639     }
24640 });
24641 /*
24642  * Based on:
24643  * Ext JS Library 1.1.1
24644  * Copyright(c) 2006-2007, Ext JS, LLC.
24645  *
24646  * Originally Released Under LGPL - original licence link has changed is not relivant.
24647  *
24648  * Fork - LGPL
24649  * <script type="text/javascript">
24650  */
24651  
24652 /**
24653  * @class Roo.bootstrap.PagingToolbar
24654  * @extends Roo.bootstrap.NavSimplebar
24655  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24656  * @constructor
24657  * Create a new PagingToolbar
24658  * @param {Object} config The config object
24659  * @param {Roo.data.Store} store
24660  */
24661 Roo.bootstrap.PagingToolbar = function(config)
24662 {
24663     // old args format still supported... - xtype is prefered..
24664         // created from xtype...
24665     
24666     this.ds = config.dataSource;
24667     
24668     if (config.store && !this.ds) {
24669         this.store= Roo.factory(config.store, Roo.data);
24670         this.ds = this.store;
24671         this.ds.xmodule = this.xmodule || false;
24672     }
24673     
24674     this.toolbarItems = [];
24675     if (config.items) {
24676         this.toolbarItems = config.items;
24677     }
24678     
24679     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24680     
24681     this.cursor = 0;
24682     
24683     if (this.ds) { 
24684         this.bind(this.ds);
24685     }
24686     
24687     if (Roo.bootstrap.version == 4) {
24688         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24689     } else {
24690         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24691     }
24692     
24693 };
24694
24695 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24696     /**
24697      * @cfg {Roo.data.Store} dataSource
24698      * The underlying data store providing the paged data
24699      */
24700     /**
24701      * @cfg {String/HTMLElement/Element} container
24702      * container The id or element that will contain the toolbar
24703      */
24704     /**
24705      * @cfg {Boolean} displayInfo
24706      * True to display the displayMsg (defaults to false)
24707      */
24708     /**
24709      * @cfg {Number} pageSize
24710      * The number of records to display per page (defaults to 20)
24711      */
24712     pageSize: 20,
24713     /**
24714      * @cfg {String} displayMsg
24715      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24716      */
24717     displayMsg : 'Displaying {0} - {1} of {2}',
24718     /**
24719      * @cfg {String} emptyMsg
24720      * The message to display when no records are found (defaults to "No data to display")
24721      */
24722     emptyMsg : 'No data to display',
24723     /**
24724      * Customizable piece of the default paging text (defaults to "Page")
24725      * @type String
24726      */
24727     beforePageText : "Page",
24728     /**
24729      * Customizable piece of the default paging text (defaults to "of %0")
24730      * @type String
24731      */
24732     afterPageText : "of {0}",
24733     /**
24734      * Customizable piece of the default paging text (defaults to "First Page")
24735      * @type String
24736      */
24737     firstText : "First Page",
24738     /**
24739      * Customizable piece of the default paging text (defaults to "Previous Page")
24740      * @type String
24741      */
24742     prevText : "Previous Page",
24743     /**
24744      * Customizable piece of the default paging text (defaults to "Next Page")
24745      * @type String
24746      */
24747     nextText : "Next Page",
24748     /**
24749      * Customizable piece of the default paging text (defaults to "Last Page")
24750      * @type String
24751      */
24752     lastText : "Last Page",
24753     /**
24754      * Customizable piece of the default paging text (defaults to "Refresh")
24755      * @type String
24756      */
24757     refreshText : "Refresh",
24758
24759     buttons : false,
24760     // private
24761     onRender : function(ct, position) 
24762     {
24763         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24764         this.navgroup.parentId = this.id;
24765         this.navgroup.onRender(this.el, null);
24766         // add the buttons to the navgroup
24767         
24768         if(this.displayInfo){
24769             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24770             this.displayEl = this.el.select('.x-paging-info', true).first();
24771 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24772 //            this.displayEl = navel.el.select('span',true).first();
24773         }
24774         
24775         var _this = this;
24776         
24777         if(this.buttons){
24778             Roo.each(_this.buttons, function(e){ // this might need to use render????
24779                Roo.factory(e).render(_this.el);
24780             });
24781         }
24782             
24783         Roo.each(_this.toolbarItems, function(e) {
24784             _this.navgroup.addItem(e);
24785         });
24786         
24787         
24788         this.first = this.navgroup.addItem({
24789             tooltip: this.firstText,
24790             cls: "prev btn-outline-secondary",
24791             html : ' <i class="fa fa-step-backward"></i>',
24792             disabled: true,
24793             preventDefault: true,
24794             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24795         });
24796         
24797         this.prev =  this.navgroup.addItem({
24798             tooltip: this.prevText,
24799             cls: "prev btn-outline-secondary",
24800             html : ' <i class="fa fa-backward"></i>',
24801             disabled: true,
24802             preventDefault: true,
24803             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24804         });
24805     //this.addSeparator();
24806         
24807         
24808         var field = this.navgroup.addItem( {
24809             tagtype : 'span',
24810             cls : 'x-paging-position  btn-outline-secondary',
24811              disabled: true,
24812             html : this.beforePageText  +
24813                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24814                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24815          } ); //?? escaped?
24816         
24817         this.field = field.el.select('input', true).first();
24818         this.field.on("keydown", this.onPagingKeydown, this);
24819         this.field.on("focus", function(){this.dom.select();});
24820     
24821     
24822         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24823         //this.field.setHeight(18);
24824         //this.addSeparator();
24825         this.next = this.navgroup.addItem({
24826             tooltip: this.nextText,
24827             cls: "next btn-outline-secondary",
24828             html : ' <i class="fa fa-forward"></i>',
24829             disabled: true,
24830             preventDefault: true,
24831             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24832         });
24833         this.last = this.navgroup.addItem({
24834             tooltip: this.lastText,
24835             html : ' <i class="fa fa-step-forward"></i>',
24836             cls: "next btn-outline-secondary",
24837             disabled: true,
24838             preventDefault: true,
24839             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24840         });
24841     //this.addSeparator();
24842         this.loading = this.navgroup.addItem({
24843             tooltip: this.refreshText,
24844             cls: "btn-outline-secondary",
24845             html : ' <i class="fa fa-refresh"></i>',
24846             preventDefault: true,
24847             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24848         });
24849         
24850     },
24851
24852     // private
24853     updateInfo : function(){
24854         if(this.displayEl){
24855             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24856             var msg = count == 0 ?
24857                 this.emptyMsg :
24858                 String.format(
24859                     this.displayMsg,
24860                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24861                 );
24862             this.displayEl.update(msg);
24863         }
24864     },
24865
24866     // private
24867     onLoad : function(ds, r, o)
24868     {
24869         this.cursor = o.params.start ? o.params.start : 0;
24870         
24871         var d = this.getPageData(),
24872             ap = d.activePage,
24873             ps = d.pages;
24874         
24875         
24876         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24877         this.field.dom.value = ap;
24878         this.first.setDisabled(ap == 1);
24879         this.prev.setDisabled(ap == 1);
24880         this.next.setDisabled(ap == ps);
24881         this.last.setDisabled(ap == ps);
24882         this.loading.enable();
24883         this.updateInfo();
24884     },
24885
24886     // private
24887     getPageData : function(){
24888         var total = this.ds.getTotalCount();
24889         return {
24890             total : total,
24891             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24892             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24893         };
24894     },
24895
24896     // private
24897     onLoadError : function(){
24898         this.loading.enable();
24899     },
24900
24901     // private
24902     onPagingKeydown : function(e){
24903         var k = e.getKey();
24904         var d = this.getPageData();
24905         if(k == e.RETURN){
24906             var v = this.field.dom.value, pageNum;
24907             if(!v || isNaN(pageNum = parseInt(v, 10))){
24908                 this.field.dom.value = d.activePage;
24909                 return;
24910             }
24911             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24912             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24913             e.stopEvent();
24914         }
24915         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))
24916         {
24917           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24918           this.field.dom.value = pageNum;
24919           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24920           e.stopEvent();
24921         }
24922         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24923         {
24924           var v = this.field.dom.value, pageNum; 
24925           var increment = (e.shiftKey) ? 10 : 1;
24926           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24927                 increment *= -1;
24928           }
24929           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24930             this.field.dom.value = d.activePage;
24931             return;
24932           }
24933           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24934           {
24935             this.field.dom.value = parseInt(v, 10) + increment;
24936             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24937             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24938           }
24939           e.stopEvent();
24940         }
24941     },
24942
24943     // private
24944     beforeLoad : function(){
24945         if(this.loading){
24946             this.loading.disable();
24947         }
24948     },
24949
24950     // private
24951     onClick : function(which){
24952         
24953         var ds = this.ds;
24954         if (!ds) {
24955             return;
24956         }
24957         
24958         switch(which){
24959             case "first":
24960                 ds.load({params:{start: 0, limit: this.pageSize}});
24961             break;
24962             case "prev":
24963                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24964             break;
24965             case "next":
24966                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24967             break;
24968             case "last":
24969                 var total = ds.getTotalCount();
24970                 var extra = total % this.pageSize;
24971                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24972                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24973             break;
24974             case "refresh":
24975                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24976             break;
24977         }
24978     },
24979
24980     /**
24981      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24982      * @param {Roo.data.Store} store The data store to unbind
24983      */
24984     unbind : function(ds){
24985         ds.un("beforeload", this.beforeLoad, this);
24986         ds.un("load", this.onLoad, this);
24987         ds.un("loadexception", this.onLoadError, this);
24988         ds.un("remove", this.updateInfo, this);
24989         ds.un("add", this.updateInfo, this);
24990         this.ds = undefined;
24991     },
24992
24993     /**
24994      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24995      * @param {Roo.data.Store} store The data store to bind
24996      */
24997     bind : function(ds){
24998         ds.on("beforeload", this.beforeLoad, this);
24999         ds.on("load", this.onLoad, this);
25000         ds.on("loadexception", this.onLoadError, this);
25001         ds.on("remove", this.updateInfo, this);
25002         ds.on("add", this.updateInfo, this);
25003         this.ds = ds;
25004     }
25005 });/*
25006  * - LGPL
25007  *
25008  * element
25009  * 
25010  */
25011
25012 /**
25013  * @class Roo.bootstrap.MessageBar
25014  * @extends Roo.bootstrap.Component
25015  * Bootstrap MessageBar class
25016  * @cfg {String} html contents of the MessageBar
25017  * @cfg {String} weight (info | success | warning | danger) default info
25018  * @cfg {String} beforeClass insert the bar before the given class
25019  * @cfg {Boolean} closable (true | false) default false
25020  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25021  * 
25022  * @constructor
25023  * Create a new Element
25024  * @param {Object} config The config object
25025  */
25026
25027 Roo.bootstrap.MessageBar = function(config){
25028     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25029 };
25030
25031 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25032     
25033     html: '',
25034     weight: 'info',
25035     closable: false,
25036     fixed: false,
25037     beforeClass: 'bootstrap-sticky-wrap',
25038     
25039     getAutoCreate : function(){
25040         
25041         var cfg = {
25042             tag: 'div',
25043             cls: 'alert alert-dismissable alert-' + this.weight,
25044             cn: [
25045                 {
25046                     tag: 'span',
25047                     cls: 'message',
25048                     html: this.html || ''
25049                 }
25050             ]
25051         };
25052         
25053         if(this.fixed){
25054             cfg.cls += ' alert-messages-fixed';
25055         }
25056         
25057         if(this.closable){
25058             cfg.cn.push({
25059                 tag: 'button',
25060                 cls: 'close',
25061                 html: 'x'
25062             });
25063         }
25064         
25065         return cfg;
25066     },
25067     
25068     onRender : function(ct, position)
25069     {
25070         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25071         
25072         if(!this.el){
25073             var cfg = Roo.apply({},  this.getAutoCreate());
25074             cfg.id = Roo.id();
25075             
25076             if (this.cls) {
25077                 cfg.cls += ' ' + this.cls;
25078             }
25079             if (this.style) {
25080                 cfg.style = this.style;
25081             }
25082             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25083             
25084             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25085         }
25086         
25087         this.el.select('>button.close').on('click', this.hide, this);
25088         
25089     },
25090     
25091     show : function()
25092     {
25093         if (!this.rendered) {
25094             this.render();
25095         }
25096         
25097         this.el.show();
25098         
25099         this.fireEvent('show', this);
25100         
25101     },
25102     
25103     hide : function()
25104     {
25105         if (!this.rendered) {
25106             this.render();
25107         }
25108         
25109         this.el.hide();
25110         
25111         this.fireEvent('hide', this);
25112     },
25113     
25114     update : function()
25115     {
25116 //        var e = this.el.dom.firstChild;
25117 //        
25118 //        if(this.closable){
25119 //            e = e.nextSibling;
25120 //        }
25121 //        
25122 //        e.data = this.html || '';
25123
25124         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25125     }
25126    
25127 });
25128
25129  
25130
25131      /*
25132  * - LGPL
25133  *
25134  * Graph
25135  * 
25136  */
25137
25138
25139 /**
25140  * @class Roo.bootstrap.Graph
25141  * @extends Roo.bootstrap.Component
25142  * Bootstrap Graph class
25143 > Prameters
25144  -sm {number} sm 4
25145  -md {number} md 5
25146  @cfg {String} graphtype  bar | vbar | pie
25147  @cfg {number} g_x coodinator | centre x (pie)
25148  @cfg {number} g_y coodinator | centre y (pie)
25149  @cfg {number} g_r radius (pie)
25150  @cfg {number} g_height height of the chart (respected by all elements in the set)
25151  @cfg {number} g_width width of the chart (respected by all elements in the set)
25152  @cfg {Object} title The title of the chart
25153     
25154  -{Array}  values
25155  -opts (object) options for the chart 
25156      o {
25157      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25158      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25159      o vgutter (number)
25160      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.
25161      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25162      o to
25163      o stretch (boolean)
25164      o }
25165  -opts (object) options for the pie
25166      o{
25167      o cut
25168      o startAngle (number)
25169      o endAngle (number)
25170      } 
25171  *
25172  * @constructor
25173  * Create a new Input
25174  * @param {Object} config The config object
25175  */
25176
25177 Roo.bootstrap.Graph = function(config){
25178     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25179     
25180     this.addEvents({
25181         // img events
25182         /**
25183          * @event click
25184          * The img click event for the img.
25185          * @param {Roo.EventObject} e
25186          */
25187         "click" : true
25188     });
25189 };
25190
25191 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25192     
25193     sm: 4,
25194     md: 5,
25195     graphtype: 'bar',
25196     g_height: 250,
25197     g_width: 400,
25198     g_x: 50,
25199     g_y: 50,
25200     g_r: 30,
25201     opts:{
25202         //g_colors: this.colors,
25203         g_type: 'soft',
25204         g_gutter: '20%'
25205
25206     },
25207     title : false,
25208
25209     getAutoCreate : function(){
25210         
25211         var cfg = {
25212             tag: 'div',
25213             html : null
25214         };
25215         
25216         
25217         return  cfg;
25218     },
25219
25220     onRender : function(ct,position){
25221         
25222         
25223         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25224         
25225         if (typeof(Raphael) == 'undefined') {
25226             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25227             return;
25228         }
25229         
25230         this.raphael = Raphael(this.el.dom);
25231         
25232                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25233                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25234                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25235                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25236                 /*
25237                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25238                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25239                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25240                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25241                 
25242                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25243                 r.barchart(330, 10, 300, 220, data1);
25244                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25245                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25246                 */
25247                 
25248                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25249                 // r.barchart(30, 30, 560, 250,  xdata, {
25250                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25251                 //     axis : "0 0 1 1",
25252                 //     axisxlabels :  xdata
25253                 //     //yvalues : cols,
25254                    
25255                 // });
25256 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25257 //        
25258 //        this.load(null,xdata,{
25259 //                axis : "0 0 1 1",
25260 //                axisxlabels :  xdata
25261 //                });
25262
25263     },
25264
25265     load : function(graphtype,xdata,opts)
25266     {
25267         this.raphael.clear();
25268         if(!graphtype) {
25269             graphtype = this.graphtype;
25270         }
25271         if(!opts){
25272             opts = this.opts;
25273         }
25274         var r = this.raphael,
25275             fin = function () {
25276                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25277             },
25278             fout = function () {
25279                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25280             },
25281             pfin = function() {
25282                 this.sector.stop();
25283                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25284
25285                 if (this.label) {
25286                     this.label[0].stop();
25287                     this.label[0].attr({ r: 7.5 });
25288                     this.label[1].attr({ "font-weight": 800 });
25289                 }
25290             },
25291             pfout = function() {
25292                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25293
25294                 if (this.label) {
25295                     this.label[0].animate({ r: 5 }, 500, "bounce");
25296                     this.label[1].attr({ "font-weight": 400 });
25297                 }
25298             };
25299
25300         switch(graphtype){
25301             case 'bar':
25302                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25303                 break;
25304             case 'hbar':
25305                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25306                 break;
25307             case 'pie':
25308 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25309 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25310 //            
25311                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25312                 
25313                 break;
25314
25315         }
25316         
25317         if(this.title){
25318             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25319         }
25320         
25321     },
25322     
25323     setTitle: function(o)
25324     {
25325         this.title = o;
25326     },
25327     
25328     initEvents: function() {
25329         
25330         if(!this.href){
25331             this.el.on('click', this.onClick, this);
25332         }
25333     },
25334     
25335     onClick : function(e)
25336     {
25337         Roo.log('img onclick');
25338         this.fireEvent('click', this, e);
25339     }
25340    
25341 });
25342
25343  
25344 /*
25345  * - LGPL
25346  *
25347  * numberBox
25348  * 
25349  */
25350 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25351
25352 /**
25353  * @class Roo.bootstrap.dash.NumberBox
25354  * @extends Roo.bootstrap.Component
25355  * Bootstrap NumberBox class
25356  * @cfg {String} headline Box headline
25357  * @cfg {String} content Box content
25358  * @cfg {String} icon Box icon
25359  * @cfg {String} footer Footer text
25360  * @cfg {String} fhref Footer href
25361  * 
25362  * @constructor
25363  * Create a new NumberBox
25364  * @param {Object} config The config object
25365  */
25366
25367
25368 Roo.bootstrap.dash.NumberBox = function(config){
25369     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25370     
25371 };
25372
25373 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25374     
25375     headline : '',
25376     content : '',
25377     icon : '',
25378     footer : '',
25379     fhref : '',
25380     ficon : '',
25381     
25382     getAutoCreate : function(){
25383         
25384         var cfg = {
25385             tag : 'div',
25386             cls : 'small-box ',
25387             cn : [
25388                 {
25389                     tag : 'div',
25390                     cls : 'inner',
25391                     cn :[
25392                         {
25393                             tag : 'h3',
25394                             cls : 'roo-headline',
25395                             html : this.headline
25396                         },
25397                         {
25398                             tag : 'p',
25399                             cls : 'roo-content',
25400                             html : this.content
25401                         }
25402                     ]
25403                 }
25404             ]
25405         };
25406         
25407         if(this.icon){
25408             cfg.cn.push({
25409                 tag : 'div',
25410                 cls : 'icon',
25411                 cn :[
25412                     {
25413                         tag : 'i',
25414                         cls : 'ion ' + this.icon
25415                     }
25416                 ]
25417             });
25418         }
25419         
25420         if(this.footer){
25421             var footer = {
25422                 tag : 'a',
25423                 cls : 'small-box-footer',
25424                 href : this.fhref || '#',
25425                 html : this.footer
25426             };
25427             
25428             cfg.cn.push(footer);
25429             
25430         }
25431         
25432         return  cfg;
25433     },
25434
25435     onRender : function(ct,position){
25436         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25437
25438
25439        
25440                 
25441     },
25442
25443     setHeadline: function (value)
25444     {
25445         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25446     },
25447     
25448     setFooter: function (value, href)
25449     {
25450         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25451         
25452         if(href){
25453             this.el.select('a.small-box-footer',true).first().attr('href', href);
25454         }
25455         
25456     },
25457
25458     setContent: function (value)
25459     {
25460         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25461     },
25462
25463     initEvents: function() 
25464     {   
25465         
25466     }
25467     
25468 });
25469
25470  
25471 /*
25472  * - LGPL
25473  *
25474  * TabBox
25475  * 
25476  */
25477 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25478
25479 /**
25480  * @class Roo.bootstrap.dash.TabBox
25481  * @extends Roo.bootstrap.Component
25482  * Bootstrap TabBox class
25483  * @cfg {String} title Title of the TabBox
25484  * @cfg {String} icon Icon of the TabBox
25485  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25486  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25487  * 
25488  * @constructor
25489  * Create a new TabBox
25490  * @param {Object} config The config object
25491  */
25492
25493
25494 Roo.bootstrap.dash.TabBox = function(config){
25495     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25496     this.addEvents({
25497         // raw events
25498         /**
25499          * @event addpane
25500          * When a pane is added
25501          * @param {Roo.bootstrap.dash.TabPane} pane
25502          */
25503         "addpane" : true,
25504         /**
25505          * @event activatepane
25506          * When a pane is activated
25507          * @param {Roo.bootstrap.dash.TabPane} pane
25508          */
25509         "activatepane" : true
25510         
25511          
25512     });
25513     
25514     this.panes = [];
25515 };
25516
25517 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25518
25519     title : '',
25520     icon : false,
25521     showtabs : true,
25522     tabScrollable : false,
25523     
25524     getChildContainer : function()
25525     {
25526         return this.el.select('.tab-content', true).first();
25527     },
25528     
25529     getAutoCreate : function(){
25530         
25531         var header = {
25532             tag: 'li',
25533             cls: 'pull-left header',
25534             html: this.title,
25535             cn : []
25536         };
25537         
25538         if(this.icon){
25539             header.cn.push({
25540                 tag: 'i',
25541                 cls: 'fa ' + this.icon
25542             });
25543         }
25544         
25545         var h = {
25546             tag: 'ul',
25547             cls: 'nav nav-tabs pull-right',
25548             cn: [
25549                 header
25550             ]
25551         };
25552         
25553         if(this.tabScrollable){
25554             h = {
25555                 tag: 'div',
25556                 cls: 'tab-header',
25557                 cn: [
25558                     {
25559                         tag: 'ul',
25560                         cls: 'nav nav-tabs pull-right',
25561                         cn: [
25562                             header
25563                         ]
25564                     }
25565                 ]
25566             };
25567         }
25568         
25569         var cfg = {
25570             tag: 'div',
25571             cls: 'nav-tabs-custom',
25572             cn: [
25573                 h,
25574                 {
25575                     tag: 'div',
25576                     cls: 'tab-content no-padding',
25577                     cn: []
25578                 }
25579             ]
25580         };
25581
25582         return  cfg;
25583     },
25584     initEvents : function()
25585     {
25586         //Roo.log('add add pane handler');
25587         this.on('addpane', this.onAddPane, this);
25588     },
25589      /**
25590      * Updates the box title
25591      * @param {String} html to set the title to.
25592      */
25593     setTitle : function(value)
25594     {
25595         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25596     },
25597     onAddPane : function(pane)
25598     {
25599         this.panes.push(pane);
25600         //Roo.log('addpane');
25601         //Roo.log(pane);
25602         // tabs are rendere left to right..
25603         if(!this.showtabs){
25604             return;
25605         }
25606         
25607         var ctr = this.el.select('.nav-tabs', true).first();
25608          
25609          
25610         var existing = ctr.select('.nav-tab',true);
25611         var qty = existing.getCount();;
25612         
25613         
25614         var tab = ctr.createChild({
25615             tag : 'li',
25616             cls : 'nav-tab' + (qty ? '' : ' active'),
25617             cn : [
25618                 {
25619                     tag : 'a',
25620                     href:'#',
25621                     html : pane.title
25622                 }
25623             ]
25624         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25625         pane.tab = tab;
25626         
25627         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25628         if (!qty) {
25629             pane.el.addClass('active');
25630         }
25631         
25632                 
25633     },
25634     onTabClick : function(ev,un,ob,pane)
25635     {
25636         //Roo.log('tab - prev default');
25637         ev.preventDefault();
25638         
25639         
25640         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25641         pane.tab.addClass('active');
25642         //Roo.log(pane.title);
25643         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25644         // technically we should have a deactivate event.. but maybe add later.
25645         // and it should not de-activate the selected tab...
25646         this.fireEvent('activatepane', pane);
25647         pane.el.addClass('active');
25648         pane.fireEvent('activate');
25649         
25650         
25651     },
25652     
25653     getActivePane : function()
25654     {
25655         var r = false;
25656         Roo.each(this.panes, function(p) {
25657             if(p.el.hasClass('active')){
25658                 r = p;
25659                 return false;
25660             }
25661             
25662             return;
25663         });
25664         
25665         return r;
25666     }
25667     
25668     
25669 });
25670
25671  
25672 /*
25673  * - LGPL
25674  *
25675  * Tab pane
25676  * 
25677  */
25678 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25679 /**
25680  * @class Roo.bootstrap.TabPane
25681  * @extends Roo.bootstrap.Component
25682  * Bootstrap TabPane class
25683  * @cfg {Boolean} active (false | true) Default false
25684  * @cfg {String} title title of panel
25685
25686  * 
25687  * @constructor
25688  * Create a new TabPane
25689  * @param {Object} config The config object
25690  */
25691
25692 Roo.bootstrap.dash.TabPane = function(config){
25693     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25694     
25695     this.addEvents({
25696         // raw events
25697         /**
25698          * @event activate
25699          * When a pane is activated
25700          * @param {Roo.bootstrap.dash.TabPane} pane
25701          */
25702         "activate" : true
25703          
25704     });
25705 };
25706
25707 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25708     
25709     active : false,
25710     title : '',
25711     
25712     // the tabBox that this is attached to.
25713     tab : false,
25714      
25715     getAutoCreate : function() 
25716     {
25717         var cfg = {
25718             tag: 'div',
25719             cls: 'tab-pane'
25720         };
25721         
25722         if(this.active){
25723             cfg.cls += ' active';
25724         }
25725         
25726         return cfg;
25727     },
25728     initEvents  : function()
25729     {
25730         //Roo.log('trigger add pane handler');
25731         this.parent().fireEvent('addpane', this)
25732     },
25733     
25734      /**
25735      * Updates the tab title 
25736      * @param {String} html to set the title to.
25737      */
25738     setTitle: function(str)
25739     {
25740         if (!this.tab) {
25741             return;
25742         }
25743         this.title = str;
25744         this.tab.select('a', true).first().dom.innerHTML = str;
25745         
25746     }
25747     
25748     
25749     
25750 });
25751
25752  
25753
25754
25755  /*
25756  * - LGPL
25757  *
25758  * menu
25759  * 
25760  */
25761 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25762
25763 /**
25764  * @class Roo.bootstrap.menu.Menu
25765  * @extends Roo.bootstrap.Component
25766  * Bootstrap Menu class - container for Menu
25767  * @cfg {String} html Text of the menu
25768  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25769  * @cfg {String} icon Font awesome icon
25770  * @cfg {String} pos Menu align to (top | bottom) default bottom
25771  * 
25772  * 
25773  * @constructor
25774  * Create a new Menu
25775  * @param {Object} config The config object
25776  */
25777
25778
25779 Roo.bootstrap.menu.Menu = function(config){
25780     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25781     
25782     this.addEvents({
25783         /**
25784          * @event beforeshow
25785          * Fires before this menu is displayed
25786          * @param {Roo.bootstrap.menu.Menu} this
25787          */
25788         beforeshow : true,
25789         /**
25790          * @event beforehide
25791          * Fires before this menu is hidden
25792          * @param {Roo.bootstrap.menu.Menu} this
25793          */
25794         beforehide : true,
25795         /**
25796          * @event show
25797          * Fires after this menu is displayed
25798          * @param {Roo.bootstrap.menu.Menu} this
25799          */
25800         show : true,
25801         /**
25802          * @event hide
25803          * Fires after this menu is hidden
25804          * @param {Roo.bootstrap.menu.Menu} this
25805          */
25806         hide : true,
25807         /**
25808          * @event click
25809          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25810          * @param {Roo.bootstrap.menu.Menu} this
25811          * @param {Roo.EventObject} e
25812          */
25813         click : true
25814     });
25815     
25816 };
25817
25818 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25819     
25820     submenu : false,
25821     html : '',
25822     weight : 'default',
25823     icon : false,
25824     pos : 'bottom',
25825     
25826     
25827     getChildContainer : function() {
25828         if(this.isSubMenu){
25829             return this.el;
25830         }
25831         
25832         return this.el.select('ul.dropdown-menu', true).first();  
25833     },
25834     
25835     getAutoCreate : function()
25836     {
25837         var text = [
25838             {
25839                 tag : 'span',
25840                 cls : 'roo-menu-text',
25841                 html : this.html
25842             }
25843         ];
25844         
25845         if(this.icon){
25846             text.unshift({
25847                 tag : 'i',
25848                 cls : 'fa ' + this.icon
25849             })
25850         }
25851         
25852         
25853         var cfg = {
25854             tag : 'div',
25855             cls : 'btn-group',
25856             cn : [
25857                 {
25858                     tag : 'button',
25859                     cls : 'dropdown-button btn btn-' + this.weight,
25860                     cn : text
25861                 },
25862                 {
25863                     tag : 'button',
25864                     cls : 'dropdown-toggle btn btn-' + this.weight,
25865                     cn : [
25866                         {
25867                             tag : 'span',
25868                             cls : 'caret'
25869                         }
25870                     ]
25871                 },
25872                 {
25873                     tag : 'ul',
25874                     cls : 'dropdown-menu'
25875                 }
25876             ]
25877             
25878         };
25879         
25880         if(this.pos == 'top'){
25881             cfg.cls += ' dropup';
25882         }
25883         
25884         if(this.isSubMenu){
25885             cfg = {
25886                 tag : 'ul',
25887                 cls : 'dropdown-menu'
25888             }
25889         }
25890         
25891         return cfg;
25892     },
25893     
25894     onRender : function(ct, position)
25895     {
25896         this.isSubMenu = ct.hasClass('dropdown-submenu');
25897         
25898         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25899     },
25900     
25901     initEvents : function() 
25902     {
25903         if(this.isSubMenu){
25904             return;
25905         }
25906         
25907         this.hidden = true;
25908         
25909         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25910         this.triggerEl.on('click', this.onTriggerPress, this);
25911         
25912         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25913         this.buttonEl.on('click', this.onClick, this);
25914         
25915     },
25916     
25917     list : function()
25918     {
25919         if(this.isSubMenu){
25920             return this.el;
25921         }
25922         
25923         return this.el.select('ul.dropdown-menu', true).first();
25924     },
25925     
25926     onClick : function(e)
25927     {
25928         this.fireEvent("click", this, e);
25929     },
25930     
25931     onTriggerPress  : function(e)
25932     {   
25933         if (this.isVisible()) {
25934             this.hide();
25935         } else {
25936             this.show();
25937         }
25938     },
25939     
25940     isVisible : function(){
25941         return !this.hidden;
25942     },
25943     
25944     show : function()
25945     {
25946         this.fireEvent("beforeshow", this);
25947         
25948         this.hidden = false;
25949         this.el.addClass('open');
25950         
25951         Roo.get(document).on("mouseup", this.onMouseUp, this);
25952         
25953         this.fireEvent("show", this);
25954         
25955         
25956     },
25957     
25958     hide : function()
25959     {
25960         this.fireEvent("beforehide", this);
25961         
25962         this.hidden = true;
25963         this.el.removeClass('open');
25964         
25965         Roo.get(document).un("mouseup", this.onMouseUp);
25966         
25967         this.fireEvent("hide", this);
25968     },
25969     
25970     onMouseUp : function()
25971     {
25972         this.hide();
25973     }
25974     
25975 });
25976
25977  
25978  /*
25979  * - LGPL
25980  *
25981  * menu item
25982  * 
25983  */
25984 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25985
25986 /**
25987  * @class Roo.bootstrap.menu.Item
25988  * @extends Roo.bootstrap.Component
25989  * Bootstrap MenuItem class
25990  * @cfg {Boolean} submenu (true | false) default false
25991  * @cfg {String} html text of the item
25992  * @cfg {String} href the link
25993  * @cfg {Boolean} disable (true | false) default false
25994  * @cfg {Boolean} preventDefault (true | false) default true
25995  * @cfg {String} icon Font awesome icon
25996  * @cfg {String} pos Submenu align to (left | right) default right 
25997  * 
25998  * 
25999  * @constructor
26000  * Create a new Item
26001  * @param {Object} config The config object
26002  */
26003
26004
26005 Roo.bootstrap.menu.Item = function(config){
26006     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26007     this.addEvents({
26008         /**
26009          * @event mouseover
26010          * Fires when the mouse is hovering over this menu
26011          * @param {Roo.bootstrap.menu.Item} this
26012          * @param {Roo.EventObject} e
26013          */
26014         mouseover : true,
26015         /**
26016          * @event mouseout
26017          * Fires when the mouse exits this menu
26018          * @param {Roo.bootstrap.menu.Item} this
26019          * @param {Roo.EventObject} e
26020          */
26021         mouseout : true,
26022         // raw events
26023         /**
26024          * @event click
26025          * The raw click event for the entire grid.
26026          * @param {Roo.EventObject} e
26027          */
26028         click : true
26029     });
26030 };
26031
26032 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26033     
26034     submenu : false,
26035     href : '',
26036     html : '',
26037     preventDefault: true,
26038     disable : false,
26039     icon : false,
26040     pos : 'right',
26041     
26042     getAutoCreate : function()
26043     {
26044         var text = [
26045             {
26046                 tag : 'span',
26047                 cls : 'roo-menu-item-text',
26048                 html : this.html
26049             }
26050         ];
26051         
26052         if(this.icon){
26053             text.unshift({
26054                 tag : 'i',
26055                 cls : 'fa ' + this.icon
26056             })
26057         }
26058         
26059         var cfg = {
26060             tag : 'li',
26061             cn : [
26062                 {
26063                     tag : 'a',
26064                     href : this.href || '#',
26065                     cn : text
26066                 }
26067             ]
26068         };
26069         
26070         if(this.disable){
26071             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26072         }
26073         
26074         if(this.submenu){
26075             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26076             
26077             if(this.pos == 'left'){
26078                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26079             }
26080         }
26081         
26082         return cfg;
26083     },
26084     
26085     initEvents : function() 
26086     {
26087         this.el.on('mouseover', this.onMouseOver, this);
26088         this.el.on('mouseout', this.onMouseOut, this);
26089         
26090         this.el.select('a', true).first().on('click', this.onClick, this);
26091         
26092     },
26093     
26094     onClick : function(e)
26095     {
26096         if(this.preventDefault){
26097             e.preventDefault();
26098         }
26099         
26100         this.fireEvent("click", this, e);
26101     },
26102     
26103     onMouseOver : function(e)
26104     {
26105         if(this.submenu && this.pos == 'left'){
26106             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26107         }
26108         
26109         this.fireEvent("mouseover", this, e);
26110     },
26111     
26112     onMouseOut : function(e)
26113     {
26114         this.fireEvent("mouseout", this, e);
26115     }
26116 });
26117
26118  
26119
26120  /*
26121  * - LGPL
26122  *
26123  * menu separator
26124  * 
26125  */
26126 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26127
26128 /**
26129  * @class Roo.bootstrap.menu.Separator
26130  * @extends Roo.bootstrap.Component
26131  * Bootstrap Separator class
26132  * 
26133  * @constructor
26134  * Create a new Separator
26135  * @param {Object} config The config object
26136  */
26137
26138
26139 Roo.bootstrap.menu.Separator = function(config){
26140     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26141 };
26142
26143 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26144     
26145     getAutoCreate : function(){
26146         var cfg = {
26147             tag : 'li',
26148             cls: 'divider'
26149         };
26150         
26151         return cfg;
26152     }
26153    
26154 });
26155
26156  
26157
26158  /*
26159  * - LGPL
26160  *
26161  * Tooltip
26162  * 
26163  */
26164
26165 /**
26166  * @class Roo.bootstrap.Tooltip
26167  * Bootstrap Tooltip class
26168  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26169  * to determine which dom element triggers the tooltip.
26170  * 
26171  * It needs to add support for additional attributes like tooltip-position
26172  * 
26173  * @constructor
26174  * Create a new Toolti
26175  * @param {Object} config The config object
26176  */
26177
26178 Roo.bootstrap.Tooltip = function(config){
26179     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26180     
26181     this.alignment = Roo.bootstrap.Tooltip.alignment;
26182     
26183     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26184         this.alignment = config.alignment;
26185     }
26186     
26187 };
26188
26189 Roo.apply(Roo.bootstrap.Tooltip, {
26190     /**
26191      * @function init initialize tooltip monitoring.
26192      * @static
26193      */
26194     currentEl : false,
26195     currentTip : false,
26196     currentRegion : false,
26197     
26198     //  init : delay?
26199     
26200     init : function()
26201     {
26202         Roo.get(document).on('mouseover', this.enter ,this);
26203         Roo.get(document).on('mouseout', this.leave, this);
26204          
26205         
26206         this.currentTip = new Roo.bootstrap.Tooltip();
26207     },
26208     
26209     enter : function(ev)
26210     {
26211         var dom = ev.getTarget();
26212         
26213         //Roo.log(['enter',dom]);
26214         var el = Roo.fly(dom);
26215         if (this.currentEl) {
26216             //Roo.log(dom);
26217             //Roo.log(this.currentEl);
26218             //Roo.log(this.currentEl.contains(dom));
26219             if (this.currentEl == el) {
26220                 return;
26221             }
26222             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26223                 return;
26224             }
26225
26226         }
26227         
26228         if (this.currentTip.el) {
26229             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26230         }    
26231         //Roo.log(ev);
26232         
26233         if(!el || el.dom == document){
26234             return;
26235         }
26236         
26237         var bindEl = el;
26238         
26239         // you can not look for children, as if el is the body.. then everythign is the child..
26240         if (!el.attr('tooltip')) { //
26241             if (!el.select("[tooltip]").elements.length) {
26242                 return;
26243             }
26244             // is the mouse over this child...?
26245             bindEl = el.select("[tooltip]").first();
26246             var xy = ev.getXY();
26247             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26248                 //Roo.log("not in region.");
26249                 return;
26250             }
26251             //Roo.log("child element over..");
26252             
26253         }
26254         this.currentEl = bindEl;
26255         this.currentTip.bind(bindEl);
26256         this.currentRegion = Roo.lib.Region.getRegion(dom);
26257         this.currentTip.enter();
26258         
26259     },
26260     leave : function(ev)
26261     {
26262         var dom = ev.getTarget();
26263         //Roo.log(['leave',dom]);
26264         if (!this.currentEl) {
26265             return;
26266         }
26267         
26268         
26269         if (dom != this.currentEl.dom) {
26270             return;
26271         }
26272         var xy = ev.getXY();
26273         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26274             return;
26275         }
26276         // only activate leave if mouse cursor is outside... bounding box..
26277         
26278         
26279         
26280         
26281         if (this.currentTip) {
26282             this.currentTip.leave();
26283         }
26284         //Roo.log('clear currentEl');
26285         this.currentEl = false;
26286         
26287         
26288     },
26289     alignment : {
26290         'left' : ['r-l', [-2,0], 'right'],
26291         'right' : ['l-r', [2,0], 'left'],
26292         'bottom' : ['t-b', [0,2], 'top'],
26293         'top' : [ 'b-t', [0,-2], 'bottom']
26294     }
26295     
26296 });
26297
26298
26299 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26300     
26301     
26302     bindEl : false,
26303     
26304     delay : null, // can be { show : 300 , hide: 500}
26305     
26306     timeout : null,
26307     
26308     hoverState : null, //???
26309     
26310     placement : 'bottom', 
26311     
26312     alignment : false,
26313     
26314     getAutoCreate : function(){
26315     
26316         var cfg = {
26317            cls : 'tooltip',
26318            role : 'tooltip',
26319            cn : [
26320                 {
26321                     cls : 'tooltip-arrow'
26322                 },
26323                 {
26324                     cls : 'tooltip-inner'
26325                 }
26326            ]
26327         };
26328         
26329         return cfg;
26330     },
26331     bind : function(el)
26332     {
26333         this.bindEl = el;
26334     },
26335       
26336     
26337     enter : function () {
26338        
26339         if (this.timeout != null) {
26340             clearTimeout(this.timeout);
26341         }
26342         
26343         this.hoverState = 'in';
26344          //Roo.log("enter - show");
26345         if (!this.delay || !this.delay.show) {
26346             this.show();
26347             return;
26348         }
26349         var _t = this;
26350         this.timeout = setTimeout(function () {
26351             if (_t.hoverState == 'in') {
26352                 _t.show();
26353             }
26354         }, this.delay.show);
26355     },
26356     leave : function()
26357     {
26358         clearTimeout(this.timeout);
26359     
26360         this.hoverState = 'out';
26361          if (!this.delay || !this.delay.hide) {
26362             this.hide();
26363             return;
26364         }
26365        
26366         var _t = this;
26367         this.timeout = setTimeout(function () {
26368             //Roo.log("leave - timeout");
26369             
26370             if (_t.hoverState == 'out') {
26371                 _t.hide();
26372                 Roo.bootstrap.Tooltip.currentEl = false;
26373             }
26374         }, delay);
26375     },
26376     
26377     show : function (msg)
26378     {
26379         if (!this.el) {
26380             this.render(document.body);
26381         }
26382         // set content.
26383         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26384         
26385         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26386         
26387         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26388         
26389         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26390         
26391         var placement = typeof this.placement == 'function' ?
26392             this.placement.call(this, this.el, on_el) :
26393             this.placement;
26394             
26395         var autoToken = /\s?auto?\s?/i;
26396         var autoPlace = autoToken.test(placement);
26397         if (autoPlace) {
26398             placement = placement.replace(autoToken, '') || 'top';
26399         }
26400         
26401         //this.el.detach()
26402         //this.el.setXY([0,0]);
26403         this.el.show();
26404         //this.el.dom.style.display='block';
26405         
26406         //this.el.appendTo(on_el);
26407         
26408         var p = this.getPosition();
26409         var box = this.el.getBox();
26410         
26411         if (autoPlace) {
26412             // fixme..
26413         }
26414         
26415         var align = this.alignment[placement];
26416         
26417         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26418         
26419         if(placement == 'top' || placement == 'bottom'){
26420             if(xy[0] < 0){
26421                 placement = 'right';
26422             }
26423             
26424             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26425                 placement = 'left';
26426             }
26427             
26428             var scroll = Roo.select('body', true).first().getScroll();
26429             
26430             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26431                 placement = 'top';
26432             }
26433             
26434             align = this.alignment[placement];
26435         }
26436         
26437         this.el.alignTo(this.bindEl, align[0],align[1]);
26438         //var arrow = this.el.select('.arrow',true).first();
26439         //arrow.set(align[2], 
26440         
26441         this.el.addClass(placement);
26442         
26443         this.el.addClass('in fade');
26444         
26445         this.hoverState = null;
26446         
26447         if (this.el.hasClass('fade')) {
26448             // fade it?
26449         }
26450         
26451     },
26452     hide : function()
26453     {
26454          
26455         if (!this.el) {
26456             return;
26457         }
26458         //this.el.setXY([0,0]);
26459         this.el.removeClass('in');
26460         //this.el.hide();
26461         
26462     }
26463     
26464 });
26465  
26466
26467  /*
26468  * - LGPL
26469  *
26470  * Location Picker
26471  * 
26472  */
26473
26474 /**
26475  * @class Roo.bootstrap.LocationPicker
26476  * @extends Roo.bootstrap.Component
26477  * Bootstrap LocationPicker class
26478  * @cfg {Number} latitude Position when init default 0
26479  * @cfg {Number} longitude Position when init default 0
26480  * @cfg {Number} zoom default 15
26481  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26482  * @cfg {Boolean} mapTypeControl default false
26483  * @cfg {Boolean} disableDoubleClickZoom default false
26484  * @cfg {Boolean} scrollwheel default true
26485  * @cfg {Boolean} streetViewControl default false
26486  * @cfg {Number} radius default 0
26487  * @cfg {String} locationName
26488  * @cfg {Boolean} draggable default true
26489  * @cfg {Boolean} enableAutocomplete default false
26490  * @cfg {Boolean} enableReverseGeocode default true
26491  * @cfg {String} markerTitle
26492  * 
26493  * @constructor
26494  * Create a new LocationPicker
26495  * @param {Object} config The config object
26496  */
26497
26498
26499 Roo.bootstrap.LocationPicker = function(config){
26500     
26501     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26502     
26503     this.addEvents({
26504         /**
26505          * @event initial
26506          * Fires when the picker initialized.
26507          * @param {Roo.bootstrap.LocationPicker} this
26508          * @param {Google Location} location
26509          */
26510         initial : true,
26511         /**
26512          * @event positionchanged
26513          * Fires when the picker position changed.
26514          * @param {Roo.bootstrap.LocationPicker} this
26515          * @param {Google Location} location
26516          */
26517         positionchanged : true,
26518         /**
26519          * @event resize
26520          * Fires when the map resize.
26521          * @param {Roo.bootstrap.LocationPicker} this
26522          */
26523         resize : true,
26524         /**
26525          * @event show
26526          * Fires when the map show.
26527          * @param {Roo.bootstrap.LocationPicker} this
26528          */
26529         show : true,
26530         /**
26531          * @event hide
26532          * Fires when the map hide.
26533          * @param {Roo.bootstrap.LocationPicker} this
26534          */
26535         hide : true,
26536         /**
26537          * @event mapClick
26538          * Fires when click the map.
26539          * @param {Roo.bootstrap.LocationPicker} this
26540          * @param {Map event} e
26541          */
26542         mapClick : true,
26543         /**
26544          * @event mapRightClick
26545          * Fires when right click the map.
26546          * @param {Roo.bootstrap.LocationPicker} this
26547          * @param {Map event} e
26548          */
26549         mapRightClick : true,
26550         /**
26551          * @event markerClick
26552          * Fires when click the marker.
26553          * @param {Roo.bootstrap.LocationPicker} this
26554          * @param {Map event} e
26555          */
26556         markerClick : true,
26557         /**
26558          * @event markerRightClick
26559          * Fires when right click the marker.
26560          * @param {Roo.bootstrap.LocationPicker} this
26561          * @param {Map event} e
26562          */
26563         markerRightClick : true,
26564         /**
26565          * @event OverlayViewDraw
26566          * Fires when OverlayView Draw
26567          * @param {Roo.bootstrap.LocationPicker} this
26568          */
26569         OverlayViewDraw : true,
26570         /**
26571          * @event OverlayViewOnAdd
26572          * Fires when OverlayView Draw
26573          * @param {Roo.bootstrap.LocationPicker} this
26574          */
26575         OverlayViewOnAdd : true,
26576         /**
26577          * @event OverlayViewOnRemove
26578          * Fires when OverlayView Draw
26579          * @param {Roo.bootstrap.LocationPicker} this
26580          */
26581         OverlayViewOnRemove : true,
26582         /**
26583          * @event OverlayViewShow
26584          * Fires when OverlayView Draw
26585          * @param {Roo.bootstrap.LocationPicker} this
26586          * @param {Pixel} cpx
26587          */
26588         OverlayViewShow : true,
26589         /**
26590          * @event OverlayViewHide
26591          * Fires when OverlayView Draw
26592          * @param {Roo.bootstrap.LocationPicker} this
26593          */
26594         OverlayViewHide : true,
26595         /**
26596          * @event loadexception
26597          * Fires when load google lib failed.
26598          * @param {Roo.bootstrap.LocationPicker} this
26599          */
26600         loadexception : true
26601     });
26602         
26603 };
26604
26605 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26606     
26607     gMapContext: false,
26608     
26609     latitude: 0,
26610     longitude: 0,
26611     zoom: 15,
26612     mapTypeId: false,
26613     mapTypeControl: false,
26614     disableDoubleClickZoom: false,
26615     scrollwheel: true,
26616     streetViewControl: false,
26617     radius: 0,
26618     locationName: '',
26619     draggable: true,
26620     enableAutocomplete: false,
26621     enableReverseGeocode: true,
26622     markerTitle: '',
26623     
26624     getAutoCreate: function()
26625     {
26626
26627         var cfg = {
26628             tag: 'div',
26629             cls: 'roo-location-picker'
26630         };
26631         
26632         return cfg
26633     },
26634     
26635     initEvents: function(ct, position)
26636     {       
26637         if(!this.el.getWidth() || this.isApplied()){
26638             return;
26639         }
26640         
26641         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26642         
26643         this.initial();
26644     },
26645     
26646     initial: function()
26647     {
26648         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26649             this.fireEvent('loadexception', this);
26650             return;
26651         }
26652         
26653         if(!this.mapTypeId){
26654             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26655         }
26656         
26657         this.gMapContext = this.GMapContext();
26658         
26659         this.initOverlayView();
26660         
26661         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26662         
26663         var _this = this;
26664                 
26665         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26666             _this.setPosition(_this.gMapContext.marker.position);
26667         });
26668         
26669         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26670             _this.fireEvent('mapClick', this, event);
26671             
26672         });
26673
26674         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26675             _this.fireEvent('mapRightClick', this, event);
26676             
26677         });
26678         
26679         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26680             _this.fireEvent('markerClick', this, event);
26681             
26682         });
26683
26684         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26685             _this.fireEvent('markerRightClick', this, event);
26686             
26687         });
26688         
26689         this.setPosition(this.gMapContext.location);
26690         
26691         this.fireEvent('initial', this, this.gMapContext.location);
26692     },
26693     
26694     initOverlayView: function()
26695     {
26696         var _this = this;
26697         
26698         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26699             
26700             draw: function()
26701             {
26702                 _this.fireEvent('OverlayViewDraw', _this);
26703             },
26704             
26705             onAdd: function()
26706             {
26707                 _this.fireEvent('OverlayViewOnAdd', _this);
26708             },
26709             
26710             onRemove: function()
26711             {
26712                 _this.fireEvent('OverlayViewOnRemove', _this);
26713             },
26714             
26715             show: function(cpx)
26716             {
26717                 _this.fireEvent('OverlayViewShow', _this, cpx);
26718             },
26719             
26720             hide: function()
26721             {
26722                 _this.fireEvent('OverlayViewHide', _this);
26723             }
26724             
26725         });
26726     },
26727     
26728     fromLatLngToContainerPixel: function(event)
26729     {
26730         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26731     },
26732     
26733     isApplied: function() 
26734     {
26735         return this.getGmapContext() == false ? false : true;
26736     },
26737     
26738     getGmapContext: function() 
26739     {
26740         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26741     },
26742     
26743     GMapContext: function() 
26744     {
26745         var position = new google.maps.LatLng(this.latitude, this.longitude);
26746         
26747         var _map = new google.maps.Map(this.el.dom, {
26748             center: position,
26749             zoom: this.zoom,
26750             mapTypeId: this.mapTypeId,
26751             mapTypeControl: this.mapTypeControl,
26752             disableDoubleClickZoom: this.disableDoubleClickZoom,
26753             scrollwheel: this.scrollwheel,
26754             streetViewControl: this.streetViewControl,
26755             locationName: this.locationName,
26756             draggable: this.draggable,
26757             enableAutocomplete: this.enableAutocomplete,
26758             enableReverseGeocode: this.enableReverseGeocode
26759         });
26760         
26761         var _marker = new google.maps.Marker({
26762             position: position,
26763             map: _map,
26764             title: this.markerTitle,
26765             draggable: this.draggable
26766         });
26767         
26768         return {
26769             map: _map,
26770             marker: _marker,
26771             circle: null,
26772             location: position,
26773             radius: this.radius,
26774             locationName: this.locationName,
26775             addressComponents: {
26776                 formatted_address: null,
26777                 addressLine1: null,
26778                 addressLine2: null,
26779                 streetName: null,
26780                 streetNumber: null,
26781                 city: null,
26782                 district: null,
26783                 state: null,
26784                 stateOrProvince: null
26785             },
26786             settings: this,
26787             domContainer: this.el.dom,
26788             geodecoder: new google.maps.Geocoder()
26789         };
26790     },
26791     
26792     drawCircle: function(center, radius, options) 
26793     {
26794         if (this.gMapContext.circle != null) {
26795             this.gMapContext.circle.setMap(null);
26796         }
26797         if (radius > 0) {
26798             radius *= 1;
26799             options = Roo.apply({}, options, {
26800                 strokeColor: "#0000FF",
26801                 strokeOpacity: .35,
26802                 strokeWeight: 2,
26803                 fillColor: "#0000FF",
26804                 fillOpacity: .2
26805             });
26806             
26807             options.map = this.gMapContext.map;
26808             options.radius = radius;
26809             options.center = center;
26810             this.gMapContext.circle = new google.maps.Circle(options);
26811             return this.gMapContext.circle;
26812         }
26813         
26814         return null;
26815     },
26816     
26817     setPosition: function(location) 
26818     {
26819         this.gMapContext.location = location;
26820         this.gMapContext.marker.setPosition(location);
26821         this.gMapContext.map.panTo(location);
26822         this.drawCircle(location, this.gMapContext.radius, {});
26823         
26824         var _this = this;
26825         
26826         if (this.gMapContext.settings.enableReverseGeocode) {
26827             this.gMapContext.geodecoder.geocode({
26828                 latLng: this.gMapContext.location
26829             }, function(results, status) {
26830                 
26831                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26832                     _this.gMapContext.locationName = results[0].formatted_address;
26833                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26834                     
26835                     _this.fireEvent('positionchanged', this, location);
26836                 }
26837             });
26838             
26839             return;
26840         }
26841         
26842         this.fireEvent('positionchanged', this, location);
26843     },
26844     
26845     resize: function()
26846     {
26847         google.maps.event.trigger(this.gMapContext.map, "resize");
26848         
26849         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26850         
26851         this.fireEvent('resize', this);
26852     },
26853     
26854     setPositionByLatLng: function(latitude, longitude)
26855     {
26856         this.setPosition(new google.maps.LatLng(latitude, longitude));
26857     },
26858     
26859     getCurrentPosition: function() 
26860     {
26861         return {
26862             latitude: this.gMapContext.location.lat(),
26863             longitude: this.gMapContext.location.lng()
26864         };
26865     },
26866     
26867     getAddressName: function() 
26868     {
26869         return this.gMapContext.locationName;
26870     },
26871     
26872     getAddressComponents: function() 
26873     {
26874         return this.gMapContext.addressComponents;
26875     },
26876     
26877     address_component_from_google_geocode: function(address_components) 
26878     {
26879         var result = {};
26880         
26881         for (var i = 0; i < address_components.length; i++) {
26882             var component = address_components[i];
26883             if (component.types.indexOf("postal_code") >= 0) {
26884                 result.postalCode = component.short_name;
26885             } else if (component.types.indexOf("street_number") >= 0) {
26886                 result.streetNumber = component.short_name;
26887             } else if (component.types.indexOf("route") >= 0) {
26888                 result.streetName = component.short_name;
26889             } else if (component.types.indexOf("neighborhood") >= 0) {
26890                 result.city = component.short_name;
26891             } else if (component.types.indexOf("locality") >= 0) {
26892                 result.city = component.short_name;
26893             } else if (component.types.indexOf("sublocality") >= 0) {
26894                 result.district = component.short_name;
26895             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26896                 result.stateOrProvince = component.short_name;
26897             } else if (component.types.indexOf("country") >= 0) {
26898                 result.country = component.short_name;
26899             }
26900         }
26901         
26902         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26903         result.addressLine2 = "";
26904         return result;
26905     },
26906     
26907     setZoomLevel: function(zoom)
26908     {
26909         this.gMapContext.map.setZoom(zoom);
26910     },
26911     
26912     show: function()
26913     {
26914         if(!this.el){
26915             return;
26916         }
26917         
26918         this.el.show();
26919         
26920         this.resize();
26921         
26922         this.fireEvent('show', this);
26923     },
26924     
26925     hide: function()
26926     {
26927         if(!this.el){
26928             return;
26929         }
26930         
26931         this.el.hide();
26932         
26933         this.fireEvent('hide', this);
26934     }
26935     
26936 });
26937
26938 Roo.apply(Roo.bootstrap.LocationPicker, {
26939     
26940     OverlayView : function(map, options)
26941     {
26942         options = options || {};
26943         
26944         this.setMap(map);
26945     }
26946     
26947     
26948 });/*
26949  * - LGPL
26950  *
26951  * Alert
26952  * 
26953  */
26954
26955 /**
26956  * @class Roo.bootstrap.Alert
26957  * @extends Roo.bootstrap.Component
26958  * Bootstrap Alert class
26959  * @cfg {String} title The title of alert
26960  * @cfg {String} html The content of alert
26961  * @cfg {String} weight (  success | info | warning | danger )
26962  * @cfg {String} faicon font-awesomeicon
26963  * 
26964  * @constructor
26965  * Create a new alert
26966  * @param {Object} config The config object
26967  */
26968
26969
26970 Roo.bootstrap.Alert = function(config){
26971     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26972     
26973 };
26974
26975 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26976     
26977     title: '',
26978     html: '',
26979     weight: false,
26980     faicon: false,
26981     
26982     getAutoCreate : function()
26983     {
26984         
26985         var cfg = {
26986             tag : 'div',
26987             cls : 'alert',
26988             cn : [
26989                 {
26990                     tag : 'i',
26991                     cls : 'roo-alert-icon'
26992                     
26993                 },
26994                 {
26995                     tag : 'b',
26996                     cls : 'roo-alert-title',
26997                     html : this.title
26998                 },
26999                 {
27000                     tag : 'span',
27001                     cls : 'roo-alert-text',
27002                     html : this.html
27003                 }
27004             ]
27005         };
27006         
27007         if(this.faicon){
27008             cfg.cn[0].cls += ' fa ' + this.faicon;
27009         }
27010         
27011         if(this.weight){
27012             cfg.cls += ' alert-' + this.weight;
27013         }
27014         
27015         return cfg;
27016     },
27017     
27018     initEvents: function() 
27019     {
27020         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27021     },
27022     
27023     setTitle : function(str)
27024     {
27025         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27026     },
27027     
27028     setText : function(str)
27029     {
27030         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27031     },
27032     
27033     setWeight : function(weight)
27034     {
27035         if(this.weight){
27036             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27037         }
27038         
27039         this.weight = weight;
27040         
27041         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27042     },
27043     
27044     setIcon : function(icon)
27045     {
27046         if(this.faicon){
27047             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27048         }
27049         
27050         this.faicon = icon;
27051         
27052         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27053     },
27054     
27055     hide: function() 
27056     {
27057         this.el.hide();   
27058     },
27059     
27060     show: function() 
27061     {  
27062         this.el.show();   
27063     }
27064     
27065 });
27066
27067  
27068 /*
27069 * Licence: LGPL
27070 */
27071
27072 /**
27073  * @class Roo.bootstrap.UploadCropbox
27074  * @extends Roo.bootstrap.Component
27075  * Bootstrap UploadCropbox class
27076  * @cfg {String} emptyText show when image has been loaded
27077  * @cfg {String} rotateNotify show when image too small to rotate
27078  * @cfg {Number} errorTimeout default 3000
27079  * @cfg {Number} minWidth default 300
27080  * @cfg {Number} minHeight default 300
27081  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27082  * @cfg {Boolean} isDocument (true|false) default false
27083  * @cfg {String} url action url
27084  * @cfg {String} paramName default 'imageUpload'
27085  * @cfg {String} method default POST
27086  * @cfg {Boolean} loadMask (true|false) default true
27087  * @cfg {Boolean} loadingText default 'Loading...'
27088  * 
27089  * @constructor
27090  * Create a new UploadCropbox
27091  * @param {Object} config The config object
27092  */
27093
27094 Roo.bootstrap.UploadCropbox = function(config){
27095     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27096     
27097     this.addEvents({
27098         /**
27099          * @event beforeselectfile
27100          * Fire before select file
27101          * @param {Roo.bootstrap.UploadCropbox} this
27102          */
27103         "beforeselectfile" : true,
27104         /**
27105          * @event initial
27106          * Fire after initEvent
27107          * @param {Roo.bootstrap.UploadCropbox} this
27108          */
27109         "initial" : true,
27110         /**
27111          * @event crop
27112          * Fire after initEvent
27113          * @param {Roo.bootstrap.UploadCropbox} this
27114          * @param {String} data
27115          */
27116         "crop" : true,
27117         /**
27118          * @event prepare
27119          * Fire when preparing the file data
27120          * @param {Roo.bootstrap.UploadCropbox} this
27121          * @param {Object} file
27122          */
27123         "prepare" : true,
27124         /**
27125          * @event exception
27126          * Fire when get exception
27127          * @param {Roo.bootstrap.UploadCropbox} this
27128          * @param {XMLHttpRequest} xhr
27129          */
27130         "exception" : true,
27131         /**
27132          * @event beforeloadcanvas
27133          * Fire before load the canvas
27134          * @param {Roo.bootstrap.UploadCropbox} this
27135          * @param {String} src
27136          */
27137         "beforeloadcanvas" : true,
27138         /**
27139          * @event trash
27140          * Fire when trash image
27141          * @param {Roo.bootstrap.UploadCropbox} this
27142          */
27143         "trash" : true,
27144         /**
27145          * @event download
27146          * Fire when download the image
27147          * @param {Roo.bootstrap.UploadCropbox} this
27148          */
27149         "download" : true,
27150         /**
27151          * @event footerbuttonclick
27152          * Fire when footerbuttonclick
27153          * @param {Roo.bootstrap.UploadCropbox} this
27154          * @param {String} type
27155          */
27156         "footerbuttonclick" : true,
27157         /**
27158          * @event resize
27159          * Fire when resize
27160          * @param {Roo.bootstrap.UploadCropbox} this
27161          */
27162         "resize" : true,
27163         /**
27164          * @event rotate
27165          * Fire when rotate the image
27166          * @param {Roo.bootstrap.UploadCropbox} this
27167          * @param {String} pos
27168          */
27169         "rotate" : true,
27170         /**
27171          * @event inspect
27172          * Fire when inspect the file
27173          * @param {Roo.bootstrap.UploadCropbox} this
27174          * @param {Object} file
27175          */
27176         "inspect" : true,
27177         /**
27178          * @event upload
27179          * Fire when xhr upload the file
27180          * @param {Roo.bootstrap.UploadCropbox} this
27181          * @param {Object} data
27182          */
27183         "upload" : true,
27184         /**
27185          * @event arrange
27186          * Fire when arrange the file data
27187          * @param {Roo.bootstrap.UploadCropbox} this
27188          * @param {Object} formData
27189          */
27190         "arrange" : true
27191     });
27192     
27193     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27194 };
27195
27196 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27197     
27198     emptyText : 'Click to upload image',
27199     rotateNotify : 'Image is too small to rotate',
27200     errorTimeout : 3000,
27201     scale : 0,
27202     baseScale : 1,
27203     rotate : 0,
27204     dragable : false,
27205     pinching : false,
27206     mouseX : 0,
27207     mouseY : 0,
27208     cropData : false,
27209     minWidth : 300,
27210     minHeight : 300,
27211     file : false,
27212     exif : {},
27213     baseRotate : 1,
27214     cropType : 'image/jpeg',
27215     buttons : false,
27216     canvasLoaded : false,
27217     isDocument : false,
27218     method : 'POST',
27219     paramName : 'imageUpload',
27220     loadMask : true,
27221     loadingText : 'Loading...',
27222     maskEl : false,
27223     
27224     getAutoCreate : function()
27225     {
27226         var cfg = {
27227             tag : 'div',
27228             cls : 'roo-upload-cropbox',
27229             cn : [
27230                 {
27231                     tag : 'input',
27232                     cls : 'roo-upload-cropbox-selector',
27233                     type : 'file'
27234                 },
27235                 {
27236                     tag : 'div',
27237                     cls : 'roo-upload-cropbox-body',
27238                     style : 'cursor:pointer',
27239                     cn : [
27240                         {
27241                             tag : 'div',
27242                             cls : 'roo-upload-cropbox-preview'
27243                         },
27244                         {
27245                             tag : 'div',
27246                             cls : 'roo-upload-cropbox-thumb'
27247                         },
27248                         {
27249                             tag : 'div',
27250                             cls : 'roo-upload-cropbox-empty-notify',
27251                             html : this.emptyText
27252                         },
27253                         {
27254                             tag : 'div',
27255                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27256                             html : this.rotateNotify
27257                         }
27258                     ]
27259                 },
27260                 {
27261                     tag : 'div',
27262                     cls : 'roo-upload-cropbox-footer',
27263                     cn : {
27264                         tag : 'div',
27265                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27266                         cn : []
27267                     }
27268                 }
27269             ]
27270         };
27271         
27272         return cfg;
27273     },
27274     
27275     onRender : function(ct, position)
27276     {
27277         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27278         
27279         if (this.buttons.length) {
27280             
27281             Roo.each(this.buttons, function(bb) {
27282                 
27283                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27284                 
27285                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27286                 
27287             }, this);
27288         }
27289         
27290         if(this.loadMask){
27291             this.maskEl = this.el;
27292         }
27293     },
27294     
27295     initEvents : function()
27296     {
27297         this.urlAPI = (window.createObjectURL && window) || 
27298                                 (window.URL && URL.revokeObjectURL && URL) || 
27299                                 (window.webkitURL && webkitURL);
27300                         
27301         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27302         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27303         
27304         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27305         this.selectorEl.hide();
27306         
27307         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27308         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27309         
27310         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27311         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27312         this.thumbEl.hide();
27313         
27314         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27315         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27316         
27317         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27318         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27319         this.errorEl.hide();
27320         
27321         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27322         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27323         this.footerEl.hide();
27324         
27325         this.setThumbBoxSize();
27326         
27327         this.bind();
27328         
27329         this.resize();
27330         
27331         this.fireEvent('initial', this);
27332     },
27333
27334     bind : function()
27335     {
27336         var _this = this;
27337         
27338         window.addEventListener("resize", function() { _this.resize(); } );
27339         
27340         this.bodyEl.on('click', this.beforeSelectFile, this);
27341         
27342         if(Roo.isTouch){
27343             this.bodyEl.on('touchstart', this.onTouchStart, this);
27344             this.bodyEl.on('touchmove', this.onTouchMove, this);
27345             this.bodyEl.on('touchend', this.onTouchEnd, this);
27346         }
27347         
27348         if(!Roo.isTouch){
27349             this.bodyEl.on('mousedown', this.onMouseDown, this);
27350             this.bodyEl.on('mousemove', this.onMouseMove, this);
27351             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27352             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27353             Roo.get(document).on('mouseup', this.onMouseUp, this);
27354         }
27355         
27356         this.selectorEl.on('change', this.onFileSelected, this);
27357     },
27358     
27359     reset : function()
27360     {    
27361         this.scale = 0;
27362         this.baseScale = 1;
27363         this.rotate = 0;
27364         this.baseRotate = 1;
27365         this.dragable = false;
27366         this.pinching = false;
27367         this.mouseX = 0;
27368         this.mouseY = 0;
27369         this.cropData = false;
27370         this.notifyEl.dom.innerHTML = this.emptyText;
27371         
27372         this.selectorEl.dom.value = '';
27373         
27374     },
27375     
27376     resize : function()
27377     {
27378         if(this.fireEvent('resize', this) != false){
27379             this.setThumbBoxPosition();
27380             this.setCanvasPosition();
27381         }
27382     },
27383     
27384     onFooterButtonClick : function(e, el, o, type)
27385     {
27386         switch (type) {
27387             case 'rotate-left' :
27388                 this.onRotateLeft(e);
27389                 break;
27390             case 'rotate-right' :
27391                 this.onRotateRight(e);
27392                 break;
27393             case 'picture' :
27394                 this.beforeSelectFile(e);
27395                 break;
27396             case 'trash' :
27397                 this.trash(e);
27398                 break;
27399             case 'crop' :
27400                 this.crop(e);
27401                 break;
27402             case 'download' :
27403                 this.download(e);
27404                 break;
27405             default :
27406                 break;
27407         }
27408         
27409         this.fireEvent('footerbuttonclick', this, type);
27410     },
27411     
27412     beforeSelectFile : function(e)
27413     {
27414         e.preventDefault();
27415         
27416         if(this.fireEvent('beforeselectfile', this) != false){
27417             this.selectorEl.dom.click();
27418         }
27419     },
27420     
27421     onFileSelected : function(e)
27422     {
27423         e.preventDefault();
27424         
27425         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27426             return;
27427         }
27428         
27429         var file = this.selectorEl.dom.files[0];
27430         
27431         if(this.fireEvent('inspect', this, file) != false){
27432             this.prepare(file);
27433         }
27434         
27435     },
27436     
27437     trash : function(e)
27438     {
27439         this.fireEvent('trash', this);
27440     },
27441     
27442     download : function(e)
27443     {
27444         this.fireEvent('download', this);
27445     },
27446     
27447     loadCanvas : function(src)
27448     {   
27449         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27450             
27451             this.reset();
27452             
27453             this.imageEl = document.createElement('img');
27454             
27455             var _this = this;
27456             
27457             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27458             
27459             this.imageEl.src = src;
27460         }
27461     },
27462     
27463     onLoadCanvas : function()
27464     {   
27465         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27466         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27467         
27468         this.bodyEl.un('click', this.beforeSelectFile, this);
27469         
27470         this.notifyEl.hide();
27471         this.thumbEl.show();
27472         this.footerEl.show();
27473         
27474         this.baseRotateLevel();
27475         
27476         if(this.isDocument){
27477             this.setThumbBoxSize();
27478         }
27479         
27480         this.setThumbBoxPosition();
27481         
27482         this.baseScaleLevel();
27483         
27484         this.draw();
27485         
27486         this.resize();
27487         
27488         this.canvasLoaded = true;
27489         
27490         if(this.loadMask){
27491             this.maskEl.unmask();
27492         }
27493         
27494     },
27495     
27496     setCanvasPosition : function()
27497     {   
27498         if(!this.canvasEl){
27499             return;
27500         }
27501         
27502         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27503         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27504         
27505         this.previewEl.setLeft(pw);
27506         this.previewEl.setTop(ph);
27507         
27508     },
27509     
27510     onMouseDown : function(e)
27511     {   
27512         e.stopEvent();
27513         
27514         this.dragable = true;
27515         this.pinching = false;
27516         
27517         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27518             this.dragable = false;
27519             return;
27520         }
27521         
27522         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27523         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27524         
27525     },
27526     
27527     onMouseMove : function(e)
27528     {   
27529         e.stopEvent();
27530         
27531         if(!this.canvasLoaded){
27532             return;
27533         }
27534         
27535         if (!this.dragable){
27536             return;
27537         }
27538         
27539         var minX = Math.ceil(this.thumbEl.getLeft(true));
27540         var minY = Math.ceil(this.thumbEl.getTop(true));
27541         
27542         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27543         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27544         
27545         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27546         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27547         
27548         x = x - this.mouseX;
27549         y = y - this.mouseY;
27550         
27551         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27552         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27553         
27554         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27555         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27556         
27557         this.previewEl.setLeft(bgX);
27558         this.previewEl.setTop(bgY);
27559         
27560         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27561         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27562     },
27563     
27564     onMouseUp : function(e)
27565     {   
27566         e.stopEvent();
27567         
27568         this.dragable = false;
27569     },
27570     
27571     onMouseWheel : function(e)
27572     {   
27573         e.stopEvent();
27574         
27575         this.startScale = this.scale;
27576         
27577         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27578         
27579         if(!this.zoomable()){
27580             this.scale = this.startScale;
27581             return;
27582         }
27583         
27584         this.draw();
27585         
27586         return;
27587     },
27588     
27589     zoomable : function()
27590     {
27591         var minScale = this.thumbEl.getWidth() / this.minWidth;
27592         
27593         if(this.minWidth < this.minHeight){
27594             minScale = this.thumbEl.getHeight() / this.minHeight;
27595         }
27596         
27597         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27598         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27599         
27600         if(
27601                 this.isDocument &&
27602                 (this.rotate == 0 || this.rotate == 180) && 
27603                 (
27604                     width > this.imageEl.OriginWidth || 
27605                     height > this.imageEl.OriginHeight ||
27606                     (width < this.minWidth && height < this.minHeight)
27607                 )
27608         ){
27609             return false;
27610         }
27611         
27612         if(
27613                 this.isDocument &&
27614                 (this.rotate == 90 || this.rotate == 270) && 
27615                 (
27616                     width > this.imageEl.OriginWidth || 
27617                     height > this.imageEl.OriginHeight ||
27618                     (width < this.minHeight && height < this.minWidth)
27619                 )
27620         ){
27621             return false;
27622         }
27623         
27624         if(
27625                 !this.isDocument &&
27626                 (this.rotate == 0 || this.rotate == 180) && 
27627                 (
27628                     width < this.minWidth || 
27629                     width > this.imageEl.OriginWidth || 
27630                     height < this.minHeight || 
27631                     height > this.imageEl.OriginHeight
27632                 )
27633         ){
27634             return false;
27635         }
27636         
27637         if(
27638                 !this.isDocument &&
27639                 (this.rotate == 90 || this.rotate == 270) && 
27640                 (
27641                     width < this.minHeight || 
27642                     width > this.imageEl.OriginWidth || 
27643                     height < this.minWidth || 
27644                     height > this.imageEl.OriginHeight
27645                 )
27646         ){
27647             return false;
27648         }
27649         
27650         return true;
27651         
27652     },
27653     
27654     onRotateLeft : function(e)
27655     {   
27656         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27657             
27658             var minScale = this.thumbEl.getWidth() / this.minWidth;
27659             
27660             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27661             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27662             
27663             this.startScale = this.scale;
27664             
27665             while (this.getScaleLevel() < minScale){
27666             
27667                 this.scale = this.scale + 1;
27668                 
27669                 if(!this.zoomable()){
27670                     break;
27671                 }
27672                 
27673                 if(
27674                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27675                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27676                 ){
27677                     continue;
27678                 }
27679                 
27680                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27681
27682                 this.draw();
27683                 
27684                 return;
27685             }
27686             
27687             this.scale = this.startScale;
27688             
27689             this.onRotateFail();
27690             
27691             return false;
27692         }
27693         
27694         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27695
27696         if(this.isDocument){
27697             this.setThumbBoxSize();
27698             this.setThumbBoxPosition();
27699             this.setCanvasPosition();
27700         }
27701         
27702         this.draw();
27703         
27704         this.fireEvent('rotate', this, 'left');
27705         
27706     },
27707     
27708     onRotateRight : function(e)
27709     {
27710         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27711             
27712             var minScale = this.thumbEl.getWidth() / this.minWidth;
27713         
27714             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27715             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27716             
27717             this.startScale = this.scale;
27718             
27719             while (this.getScaleLevel() < minScale){
27720             
27721                 this.scale = this.scale + 1;
27722                 
27723                 if(!this.zoomable()){
27724                     break;
27725                 }
27726                 
27727                 if(
27728                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27729                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27730                 ){
27731                     continue;
27732                 }
27733                 
27734                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27735
27736                 this.draw();
27737                 
27738                 return;
27739             }
27740             
27741             this.scale = this.startScale;
27742             
27743             this.onRotateFail();
27744             
27745             return false;
27746         }
27747         
27748         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27749
27750         if(this.isDocument){
27751             this.setThumbBoxSize();
27752             this.setThumbBoxPosition();
27753             this.setCanvasPosition();
27754         }
27755         
27756         this.draw();
27757         
27758         this.fireEvent('rotate', this, 'right');
27759     },
27760     
27761     onRotateFail : function()
27762     {
27763         this.errorEl.show(true);
27764         
27765         var _this = this;
27766         
27767         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27768     },
27769     
27770     draw : function()
27771     {
27772         this.previewEl.dom.innerHTML = '';
27773         
27774         var canvasEl = document.createElement("canvas");
27775         
27776         var contextEl = canvasEl.getContext("2d");
27777         
27778         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27779         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27780         var center = this.imageEl.OriginWidth / 2;
27781         
27782         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27783             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27784             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27785             center = this.imageEl.OriginHeight / 2;
27786         }
27787         
27788         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27789         
27790         contextEl.translate(center, center);
27791         contextEl.rotate(this.rotate * Math.PI / 180);
27792
27793         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27794         
27795         this.canvasEl = document.createElement("canvas");
27796         
27797         this.contextEl = this.canvasEl.getContext("2d");
27798         
27799         switch (this.rotate) {
27800             case 0 :
27801                 
27802                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27803                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27804                 
27805                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27806                 
27807                 break;
27808             case 90 : 
27809                 
27810                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27811                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27812                 
27813                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27814                     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);
27815                     break;
27816                 }
27817                 
27818                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27819                 
27820                 break;
27821             case 180 :
27822                 
27823                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27824                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27825                 
27826                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27827                     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);
27828                     break;
27829                 }
27830                 
27831                 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);
27832                 
27833                 break;
27834             case 270 :
27835                 
27836                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27837                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27838         
27839                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27840                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27841                     break;
27842                 }
27843                 
27844                 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);
27845                 
27846                 break;
27847             default : 
27848                 break;
27849         }
27850         
27851         this.previewEl.appendChild(this.canvasEl);
27852         
27853         this.setCanvasPosition();
27854     },
27855     
27856     crop : function()
27857     {
27858         if(!this.canvasLoaded){
27859             return;
27860         }
27861         
27862         var imageCanvas = document.createElement("canvas");
27863         
27864         var imageContext = imageCanvas.getContext("2d");
27865         
27866         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27867         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27868         
27869         var center = imageCanvas.width / 2;
27870         
27871         imageContext.translate(center, center);
27872         
27873         imageContext.rotate(this.rotate * Math.PI / 180);
27874         
27875         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27876         
27877         var canvas = document.createElement("canvas");
27878         
27879         var context = canvas.getContext("2d");
27880                 
27881         canvas.width = this.minWidth;
27882         canvas.height = this.minHeight;
27883
27884         switch (this.rotate) {
27885             case 0 :
27886                 
27887                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27888                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27889                 
27890                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27891                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27892                 
27893                 var targetWidth = this.minWidth - 2 * x;
27894                 var targetHeight = this.minHeight - 2 * y;
27895                 
27896                 var scale = 1;
27897                 
27898                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27899                     scale = targetWidth / width;
27900                 }
27901                 
27902                 if(x > 0 && y == 0){
27903                     scale = targetHeight / height;
27904                 }
27905                 
27906                 if(x > 0 && y > 0){
27907                     scale = targetWidth / width;
27908                     
27909                     if(width < height){
27910                         scale = targetHeight / height;
27911                     }
27912                 }
27913                 
27914                 context.scale(scale, scale);
27915                 
27916                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27917                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27918
27919                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27920                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27921
27922                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27923                 
27924                 break;
27925             case 90 : 
27926                 
27927                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27928                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27929                 
27930                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27931                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27932                 
27933                 var targetWidth = this.minWidth - 2 * x;
27934                 var targetHeight = this.minHeight - 2 * y;
27935                 
27936                 var scale = 1;
27937                 
27938                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27939                     scale = targetWidth / width;
27940                 }
27941                 
27942                 if(x > 0 && y == 0){
27943                     scale = targetHeight / height;
27944                 }
27945                 
27946                 if(x > 0 && y > 0){
27947                     scale = targetWidth / width;
27948                     
27949                     if(width < height){
27950                         scale = targetHeight / height;
27951                     }
27952                 }
27953                 
27954                 context.scale(scale, scale);
27955                 
27956                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27957                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27958
27959                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27960                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27961                 
27962                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27963                 
27964                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27965                 
27966                 break;
27967             case 180 :
27968                 
27969                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27970                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27971                 
27972                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27973                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27974                 
27975                 var targetWidth = this.minWidth - 2 * x;
27976                 var targetHeight = this.minHeight - 2 * y;
27977                 
27978                 var scale = 1;
27979                 
27980                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27981                     scale = targetWidth / width;
27982                 }
27983                 
27984                 if(x > 0 && y == 0){
27985                     scale = targetHeight / height;
27986                 }
27987                 
27988                 if(x > 0 && y > 0){
27989                     scale = targetWidth / width;
27990                     
27991                     if(width < height){
27992                         scale = targetHeight / height;
27993                     }
27994                 }
27995                 
27996                 context.scale(scale, scale);
27997                 
27998                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27999                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28000
28001                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28002                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28003
28004                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28005                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28006                 
28007                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28008                 
28009                 break;
28010             case 270 :
28011                 
28012                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28013                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28014                 
28015                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28016                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28017                 
28018                 var targetWidth = this.minWidth - 2 * x;
28019                 var targetHeight = this.minHeight - 2 * y;
28020                 
28021                 var scale = 1;
28022                 
28023                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28024                     scale = targetWidth / width;
28025                 }
28026                 
28027                 if(x > 0 && y == 0){
28028                     scale = targetHeight / height;
28029                 }
28030                 
28031                 if(x > 0 && y > 0){
28032                     scale = targetWidth / width;
28033                     
28034                     if(width < height){
28035                         scale = targetHeight / height;
28036                     }
28037                 }
28038                 
28039                 context.scale(scale, scale);
28040                 
28041                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28042                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28043
28044                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28045                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28046                 
28047                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28048                 
28049                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28050                 
28051                 break;
28052             default : 
28053                 break;
28054         }
28055         
28056         this.cropData = canvas.toDataURL(this.cropType);
28057         
28058         if(this.fireEvent('crop', this, this.cropData) !== false){
28059             this.process(this.file, this.cropData);
28060         }
28061         
28062         return;
28063         
28064     },
28065     
28066     setThumbBoxSize : function()
28067     {
28068         var width, height;
28069         
28070         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28071             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28072             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28073             
28074             this.minWidth = width;
28075             this.minHeight = height;
28076             
28077             if(this.rotate == 90 || this.rotate == 270){
28078                 this.minWidth = height;
28079                 this.minHeight = width;
28080             }
28081         }
28082         
28083         height = 300;
28084         width = Math.ceil(this.minWidth * height / this.minHeight);
28085         
28086         if(this.minWidth > this.minHeight){
28087             width = 300;
28088             height = Math.ceil(this.minHeight * width / this.minWidth);
28089         }
28090         
28091         this.thumbEl.setStyle({
28092             width : width + 'px',
28093             height : height + 'px'
28094         });
28095
28096         return;
28097             
28098     },
28099     
28100     setThumbBoxPosition : function()
28101     {
28102         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28103         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28104         
28105         this.thumbEl.setLeft(x);
28106         this.thumbEl.setTop(y);
28107         
28108     },
28109     
28110     baseRotateLevel : function()
28111     {
28112         this.baseRotate = 1;
28113         
28114         if(
28115                 typeof(this.exif) != 'undefined' &&
28116                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28117                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28118         ){
28119             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28120         }
28121         
28122         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28123         
28124     },
28125     
28126     baseScaleLevel : function()
28127     {
28128         var width, height;
28129         
28130         if(this.isDocument){
28131             
28132             if(this.baseRotate == 6 || this.baseRotate == 8){
28133             
28134                 height = this.thumbEl.getHeight();
28135                 this.baseScale = height / this.imageEl.OriginWidth;
28136
28137                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28138                     width = this.thumbEl.getWidth();
28139                     this.baseScale = width / this.imageEl.OriginHeight;
28140                 }
28141
28142                 return;
28143             }
28144
28145             height = this.thumbEl.getHeight();
28146             this.baseScale = height / this.imageEl.OriginHeight;
28147
28148             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28149                 width = this.thumbEl.getWidth();
28150                 this.baseScale = width / this.imageEl.OriginWidth;
28151             }
28152
28153             return;
28154         }
28155         
28156         if(this.baseRotate == 6 || this.baseRotate == 8){
28157             
28158             width = this.thumbEl.getHeight();
28159             this.baseScale = width / this.imageEl.OriginHeight;
28160             
28161             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28162                 height = this.thumbEl.getWidth();
28163                 this.baseScale = height / this.imageEl.OriginHeight;
28164             }
28165             
28166             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28167                 height = this.thumbEl.getWidth();
28168                 this.baseScale = height / this.imageEl.OriginHeight;
28169                 
28170                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28171                     width = this.thumbEl.getHeight();
28172                     this.baseScale = width / this.imageEl.OriginWidth;
28173                 }
28174             }
28175             
28176             return;
28177         }
28178         
28179         width = this.thumbEl.getWidth();
28180         this.baseScale = width / this.imageEl.OriginWidth;
28181         
28182         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28183             height = this.thumbEl.getHeight();
28184             this.baseScale = height / this.imageEl.OriginHeight;
28185         }
28186         
28187         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28188             
28189             height = this.thumbEl.getHeight();
28190             this.baseScale = height / this.imageEl.OriginHeight;
28191             
28192             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28193                 width = this.thumbEl.getWidth();
28194                 this.baseScale = width / this.imageEl.OriginWidth;
28195             }
28196             
28197         }
28198         
28199         return;
28200     },
28201     
28202     getScaleLevel : function()
28203     {
28204         return this.baseScale * Math.pow(1.1, this.scale);
28205     },
28206     
28207     onTouchStart : function(e)
28208     {
28209         if(!this.canvasLoaded){
28210             this.beforeSelectFile(e);
28211             return;
28212         }
28213         
28214         var touches = e.browserEvent.touches;
28215         
28216         if(!touches){
28217             return;
28218         }
28219         
28220         if(touches.length == 1){
28221             this.onMouseDown(e);
28222             return;
28223         }
28224         
28225         if(touches.length != 2){
28226             return;
28227         }
28228         
28229         var coords = [];
28230         
28231         for(var i = 0, finger; finger = touches[i]; i++){
28232             coords.push(finger.pageX, finger.pageY);
28233         }
28234         
28235         var x = Math.pow(coords[0] - coords[2], 2);
28236         var y = Math.pow(coords[1] - coords[3], 2);
28237         
28238         this.startDistance = Math.sqrt(x + y);
28239         
28240         this.startScale = this.scale;
28241         
28242         this.pinching = true;
28243         this.dragable = false;
28244         
28245     },
28246     
28247     onTouchMove : function(e)
28248     {
28249         if(!this.pinching && !this.dragable){
28250             return;
28251         }
28252         
28253         var touches = e.browserEvent.touches;
28254         
28255         if(!touches){
28256             return;
28257         }
28258         
28259         if(this.dragable){
28260             this.onMouseMove(e);
28261             return;
28262         }
28263         
28264         var coords = [];
28265         
28266         for(var i = 0, finger; finger = touches[i]; i++){
28267             coords.push(finger.pageX, finger.pageY);
28268         }
28269         
28270         var x = Math.pow(coords[0] - coords[2], 2);
28271         var y = Math.pow(coords[1] - coords[3], 2);
28272         
28273         this.endDistance = Math.sqrt(x + y);
28274         
28275         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28276         
28277         if(!this.zoomable()){
28278             this.scale = this.startScale;
28279             return;
28280         }
28281         
28282         this.draw();
28283         
28284     },
28285     
28286     onTouchEnd : function(e)
28287     {
28288         this.pinching = false;
28289         this.dragable = false;
28290         
28291     },
28292     
28293     process : function(file, crop)
28294     {
28295         if(this.loadMask){
28296             this.maskEl.mask(this.loadingText);
28297         }
28298         
28299         this.xhr = new XMLHttpRequest();
28300         
28301         file.xhr = this.xhr;
28302
28303         this.xhr.open(this.method, this.url, true);
28304         
28305         var headers = {
28306             "Accept": "application/json",
28307             "Cache-Control": "no-cache",
28308             "X-Requested-With": "XMLHttpRequest"
28309         };
28310         
28311         for (var headerName in headers) {
28312             var headerValue = headers[headerName];
28313             if (headerValue) {
28314                 this.xhr.setRequestHeader(headerName, headerValue);
28315             }
28316         }
28317         
28318         var _this = this;
28319         
28320         this.xhr.onload = function()
28321         {
28322             _this.xhrOnLoad(_this.xhr);
28323         }
28324         
28325         this.xhr.onerror = function()
28326         {
28327             _this.xhrOnError(_this.xhr);
28328         }
28329         
28330         var formData = new FormData();
28331
28332         formData.append('returnHTML', 'NO');
28333         
28334         if(crop){
28335             formData.append('crop', crop);
28336         }
28337         
28338         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28339             formData.append(this.paramName, file, file.name);
28340         }
28341         
28342         if(typeof(file.filename) != 'undefined'){
28343             formData.append('filename', file.filename);
28344         }
28345         
28346         if(typeof(file.mimetype) != 'undefined'){
28347             formData.append('mimetype', file.mimetype);
28348         }
28349         
28350         if(this.fireEvent('arrange', this, formData) != false){
28351             this.xhr.send(formData);
28352         };
28353     },
28354     
28355     xhrOnLoad : function(xhr)
28356     {
28357         if(this.loadMask){
28358             this.maskEl.unmask();
28359         }
28360         
28361         if (xhr.readyState !== 4) {
28362             this.fireEvent('exception', this, xhr);
28363             return;
28364         }
28365
28366         var response = Roo.decode(xhr.responseText);
28367         
28368         if(!response.success){
28369             this.fireEvent('exception', this, xhr);
28370             return;
28371         }
28372         
28373         var response = Roo.decode(xhr.responseText);
28374         
28375         this.fireEvent('upload', this, response);
28376         
28377     },
28378     
28379     xhrOnError : function()
28380     {
28381         if(this.loadMask){
28382             this.maskEl.unmask();
28383         }
28384         
28385         Roo.log('xhr on error');
28386         
28387         var response = Roo.decode(xhr.responseText);
28388           
28389         Roo.log(response);
28390         
28391     },
28392     
28393     prepare : function(file)
28394     {   
28395         if(this.loadMask){
28396             this.maskEl.mask(this.loadingText);
28397         }
28398         
28399         this.file = false;
28400         this.exif = {};
28401         
28402         if(typeof(file) === 'string'){
28403             this.loadCanvas(file);
28404             return;
28405         }
28406         
28407         if(!file || !this.urlAPI){
28408             return;
28409         }
28410         
28411         this.file = file;
28412         this.cropType = file.type;
28413         
28414         var _this = this;
28415         
28416         if(this.fireEvent('prepare', this, this.file) != false){
28417             
28418             var reader = new FileReader();
28419             
28420             reader.onload = function (e) {
28421                 if (e.target.error) {
28422                     Roo.log(e.target.error);
28423                     return;
28424                 }
28425                 
28426                 var buffer = e.target.result,
28427                     dataView = new DataView(buffer),
28428                     offset = 2,
28429                     maxOffset = dataView.byteLength - 4,
28430                     markerBytes,
28431                     markerLength;
28432                 
28433                 if (dataView.getUint16(0) === 0xffd8) {
28434                     while (offset < maxOffset) {
28435                         markerBytes = dataView.getUint16(offset);
28436                         
28437                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28438                             markerLength = dataView.getUint16(offset + 2) + 2;
28439                             if (offset + markerLength > dataView.byteLength) {
28440                                 Roo.log('Invalid meta data: Invalid segment size.');
28441                                 break;
28442                             }
28443                             
28444                             if(markerBytes == 0xffe1){
28445                                 _this.parseExifData(
28446                                     dataView,
28447                                     offset,
28448                                     markerLength
28449                                 );
28450                             }
28451                             
28452                             offset += markerLength;
28453                             
28454                             continue;
28455                         }
28456                         
28457                         break;
28458                     }
28459                     
28460                 }
28461                 
28462                 var url = _this.urlAPI.createObjectURL(_this.file);
28463                 
28464                 _this.loadCanvas(url);
28465                 
28466                 return;
28467             }
28468             
28469             reader.readAsArrayBuffer(this.file);
28470             
28471         }
28472         
28473     },
28474     
28475     parseExifData : function(dataView, offset, length)
28476     {
28477         var tiffOffset = offset + 10,
28478             littleEndian,
28479             dirOffset;
28480     
28481         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28482             // No Exif data, might be XMP data instead
28483             return;
28484         }
28485         
28486         // Check for the ASCII code for "Exif" (0x45786966):
28487         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28488             // No Exif data, might be XMP data instead
28489             return;
28490         }
28491         if (tiffOffset + 8 > dataView.byteLength) {
28492             Roo.log('Invalid Exif data: Invalid segment size.');
28493             return;
28494         }
28495         // Check for the two null bytes:
28496         if (dataView.getUint16(offset + 8) !== 0x0000) {
28497             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28498             return;
28499         }
28500         // Check the byte alignment:
28501         switch (dataView.getUint16(tiffOffset)) {
28502         case 0x4949:
28503             littleEndian = true;
28504             break;
28505         case 0x4D4D:
28506             littleEndian = false;
28507             break;
28508         default:
28509             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28510             return;
28511         }
28512         // Check for the TIFF tag marker (0x002A):
28513         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28514             Roo.log('Invalid Exif data: Missing TIFF marker.');
28515             return;
28516         }
28517         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28518         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28519         
28520         this.parseExifTags(
28521             dataView,
28522             tiffOffset,
28523             tiffOffset + dirOffset,
28524             littleEndian
28525         );
28526     },
28527     
28528     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28529     {
28530         var tagsNumber,
28531             dirEndOffset,
28532             i;
28533         if (dirOffset + 6 > dataView.byteLength) {
28534             Roo.log('Invalid Exif data: Invalid directory offset.');
28535             return;
28536         }
28537         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28538         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28539         if (dirEndOffset + 4 > dataView.byteLength) {
28540             Roo.log('Invalid Exif data: Invalid directory size.');
28541             return;
28542         }
28543         for (i = 0; i < tagsNumber; i += 1) {
28544             this.parseExifTag(
28545                 dataView,
28546                 tiffOffset,
28547                 dirOffset + 2 + 12 * i, // tag offset
28548                 littleEndian
28549             );
28550         }
28551         // Return the offset to the next directory:
28552         return dataView.getUint32(dirEndOffset, littleEndian);
28553     },
28554     
28555     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28556     {
28557         var tag = dataView.getUint16(offset, littleEndian);
28558         
28559         this.exif[tag] = this.getExifValue(
28560             dataView,
28561             tiffOffset,
28562             offset,
28563             dataView.getUint16(offset + 2, littleEndian), // tag type
28564             dataView.getUint32(offset + 4, littleEndian), // tag length
28565             littleEndian
28566         );
28567     },
28568     
28569     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28570     {
28571         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28572             tagSize,
28573             dataOffset,
28574             values,
28575             i,
28576             str,
28577             c;
28578     
28579         if (!tagType) {
28580             Roo.log('Invalid Exif data: Invalid tag type.');
28581             return;
28582         }
28583         
28584         tagSize = tagType.size * length;
28585         // Determine if the value is contained in the dataOffset bytes,
28586         // or if the value at the dataOffset is a pointer to the actual data:
28587         dataOffset = tagSize > 4 ?
28588                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28589         if (dataOffset + tagSize > dataView.byteLength) {
28590             Roo.log('Invalid Exif data: Invalid data offset.');
28591             return;
28592         }
28593         if (length === 1) {
28594             return tagType.getValue(dataView, dataOffset, littleEndian);
28595         }
28596         values = [];
28597         for (i = 0; i < length; i += 1) {
28598             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28599         }
28600         
28601         if (tagType.ascii) {
28602             str = '';
28603             // Concatenate the chars:
28604             for (i = 0; i < values.length; i += 1) {
28605                 c = values[i];
28606                 // Ignore the terminating NULL byte(s):
28607                 if (c === '\u0000') {
28608                     break;
28609                 }
28610                 str += c;
28611             }
28612             return str;
28613         }
28614         return values;
28615     }
28616     
28617 });
28618
28619 Roo.apply(Roo.bootstrap.UploadCropbox, {
28620     tags : {
28621         'Orientation': 0x0112
28622     },
28623     
28624     Orientation: {
28625             1: 0, //'top-left',
28626 //            2: 'top-right',
28627             3: 180, //'bottom-right',
28628 //            4: 'bottom-left',
28629 //            5: 'left-top',
28630             6: 90, //'right-top',
28631 //            7: 'right-bottom',
28632             8: 270 //'left-bottom'
28633     },
28634     
28635     exifTagTypes : {
28636         // byte, 8-bit unsigned int:
28637         1: {
28638             getValue: function (dataView, dataOffset) {
28639                 return dataView.getUint8(dataOffset);
28640             },
28641             size: 1
28642         },
28643         // ascii, 8-bit byte:
28644         2: {
28645             getValue: function (dataView, dataOffset) {
28646                 return String.fromCharCode(dataView.getUint8(dataOffset));
28647             },
28648             size: 1,
28649             ascii: true
28650         },
28651         // short, 16 bit int:
28652         3: {
28653             getValue: function (dataView, dataOffset, littleEndian) {
28654                 return dataView.getUint16(dataOffset, littleEndian);
28655             },
28656             size: 2
28657         },
28658         // long, 32 bit int:
28659         4: {
28660             getValue: function (dataView, dataOffset, littleEndian) {
28661                 return dataView.getUint32(dataOffset, littleEndian);
28662             },
28663             size: 4
28664         },
28665         // rational = two long values, first is numerator, second is denominator:
28666         5: {
28667             getValue: function (dataView, dataOffset, littleEndian) {
28668                 return dataView.getUint32(dataOffset, littleEndian) /
28669                     dataView.getUint32(dataOffset + 4, littleEndian);
28670             },
28671             size: 8
28672         },
28673         // slong, 32 bit signed int:
28674         9: {
28675             getValue: function (dataView, dataOffset, littleEndian) {
28676                 return dataView.getInt32(dataOffset, littleEndian);
28677             },
28678             size: 4
28679         },
28680         // srational, two slongs, first is numerator, second is denominator:
28681         10: {
28682             getValue: function (dataView, dataOffset, littleEndian) {
28683                 return dataView.getInt32(dataOffset, littleEndian) /
28684                     dataView.getInt32(dataOffset + 4, littleEndian);
28685             },
28686             size: 8
28687         }
28688     },
28689     
28690     footer : {
28691         STANDARD : [
28692             {
28693                 tag : 'div',
28694                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28695                 action : 'rotate-left',
28696                 cn : [
28697                     {
28698                         tag : 'button',
28699                         cls : 'btn btn-default',
28700                         html : '<i class="fa fa-undo"></i>'
28701                     }
28702                 ]
28703             },
28704             {
28705                 tag : 'div',
28706                 cls : 'btn-group roo-upload-cropbox-picture',
28707                 action : 'picture',
28708                 cn : [
28709                     {
28710                         tag : 'button',
28711                         cls : 'btn btn-default',
28712                         html : '<i class="fa fa-picture-o"></i>'
28713                     }
28714                 ]
28715             },
28716             {
28717                 tag : 'div',
28718                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28719                 action : 'rotate-right',
28720                 cn : [
28721                     {
28722                         tag : 'button',
28723                         cls : 'btn btn-default',
28724                         html : '<i class="fa fa-repeat"></i>'
28725                     }
28726                 ]
28727             }
28728         ],
28729         DOCUMENT : [
28730             {
28731                 tag : 'div',
28732                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28733                 action : 'rotate-left',
28734                 cn : [
28735                     {
28736                         tag : 'button',
28737                         cls : 'btn btn-default',
28738                         html : '<i class="fa fa-undo"></i>'
28739                     }
28740                 ]
28741             },
28742             {
28743                 tag : 'div',
28744                 cls : 'btn-group roo-upload-cropbox-download',
28745                 action : 'download',
28746                 cn : [
28747                     {
28748                         tag : 'button',
28749                         cls : 'btn btn-default',
28750                         html : '<i class="fa fa-download"></i>'
28751                     }
28752                 ]
28753             },
28754             {
28755                 tag : 'div',
28756                 cls : 'btn-group roo-upload-cropbox-crop',
28757                 action : 'crop',
28758                 cn : [
28759                     {
28760                         tag : 'button',
28761                         cls : 'btn btn-default',
28762                         html : '<i class="fa fa-crop"></i>'
28763                     }
28764                 ]
28765             },
28766             {
28767                 tag : 'div',
28768                 cls : 'btn-group roo-upload-cropbox-trash',
28769                 action : 'trash',
28770                 cn : [
28771                     {
28772                         tag : 'button',
28773                         cls : 'btn btn-default',
28774                         html : '<i class="fa fa-trash"></i>'
28775                     }
28776                 ]
28777             },
28778             {
28779                 tag : 'div',
28780                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28781                 action : 'rotate-right',
28782                 cn : [
28783                     {
28784                         tag : 'button',
28785                         cls : 'btn btn-default',
28786                         html : '<i class="fa fa-repeat"></i>'
28787                     }
28788                 ]
28789             }
28790         ],
28791         ROTATOR : [
28792             {
28793                 tag : 'div',
28794                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28795                 action : 'rotate-left',
28796                 cn : [
28797                     {
28798                         tag : 'button',
28799                         cls : 'btn btn-default',
28800                         html : '<i class="fa fa-undo"></i>'
28801                     }
28802                 ]
28803             },
28804             {
28805                 tag : 'div',
28806                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28807                 action : 'rotate-right',
28808                 cn : [
28809                     {
28810                         tag : 'button',
28811                         cls : 'btn btn-default',
28812                         html : '<i class="fa fa-repeat"></i>'
28813                     }
28814                 ]
28815             }
28816         ]
28817     }
28818 });
28819
28820 /*
28821 * Licence: LGPL
28822 */
28823
28824 /**
28825  * @class Roo.bootstrap.DocumentManager
28826  * @extends Roo.bootstrap.Component
28827  * Bootstrap DocumentManager class
28828  * @cfg {String} paramName default 'imageUpload'
28829  * @cfg {String} toolTipName default 'filename'
28830  * @cfg {String} method default POST
28831  * @cfg {String} url action url
28832  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28833  * @cfg {Boolean} multiple multiple upload default true
28834  * @cfg {Number} thumbSize default 300
28835  * @cfg {String} fieldLabel
28836  * @cfg {Number} labelWidth default 4
28837  * @cfg {String} labelAlign (left|top) default left
28838  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28839 * @cfg {Number} labellg set the width of label (1-12)
28840  * @cfg {Number} labelmd set the width of label (1-12)
28841  * @cfg {Number} labelsm set the width of label (1-12)
28842  * @cfg {Number} labelxs set the width of label (1-12)
28843  * 
28844  * @constructor
28845  * Create a new DocumentManager
28846  * @param {Object} config The config object
28847  */
28848
28849 Roo.bootstrap.DocumentManager = function(config){
28850     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28851     
28852     this.files = [];
28853     this.delegates = [];
28854     
28855     this.addEvents({
28856         /**
28857          * @event initial
28858          * Fire when initial the DocumentManager
28859          * @param {Roo.bootstrap.DocumentManager} this
28860          */
28861         "initial" : true,
28862         /**
28863          * @event inspect
28864          * inspect selected file
28865          * @param {Roo.bootstrap.DocumentManager} this
28866          * @param {File} file
28867          */
28868         "inspect" : true,
28869         /**
28870          * @event exception
28871          * Fire when xhr load exception
28872          * @param {Roo.bootstrap.DocumentManager} this
28873          * @param {XMLHttpRequest} xhr
28874          */
28875         "exception" : true,
28876         /**
28877          * @event afterupload
28878          * Fire when xhr load exception
28879          * @param {Roo.bootstrap.DocumentManager} this
28880          * @param {XMLHttpRequest} xhr
28881          */
28882         "afterupload" : true,
28883         /**
28884          * @event prepare
28885          * prepare the form data
28886          * @param {Roo.bootstrap.DocumentManager} this
28887          * @param {Object} formData
28888          */
28889         "prepare" : true,
28890         /**
28891          * @event remove
28892          * Fire when remove the file
28893          * @param {Roo.bootstrap.DocumentManager} this
28894          * @param {Object} file
28895          */
28896         "remove" : true,
28897         /**
28898          * @event refresh
28899          * Fire after refresh the file
28900          * @param {Roo.bootstrap.DocumentManager} this
28901          */
28902         "refresh" : true,
28903         /**
28904          * @event click
28905          * Fire after click the image
28906          * @param {Roo.bootstrap.DocumentManager} this
28907          * @param {Object} file
28908          */
28909         "click" : true,
28910         /**
28911          * @event edit
28912          * Fire when upload a image and editable set to true
28913          * @param {Roo.bootstrap.DocumentManager} this
28914          * @param {Object} file
28915          */
28916         "edit" : true,
28917         /**
28918          * @event beforeselectfile
28919          * Fire before select file
28920          * @param {Roo.bootstrap.DocumentManager} this
28921          */
28922         "beforeselectfile" : true,
28923         /**
28924          * @event process
28925          * Fire before process file
28926          * @param {Roo.bootstrap.DocumentManager} this
28927          * @param {Object} file
28928          */
28929         "process" : true,
28930         /**
28931          * @event previewrendered
28932          * Fire when preview rendered
28933          * @param {Roo.bootstrap.DocumentManager} this
28934          * @param {Object} file
28935          */
28936         "previewrendered" : true,
28937         /**
28938          */
28939         "previewResize" : true
28940         
28941     });
28942 };
28943
28944 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28945     
28946     boxes : 0,
28947     inputName : '',
28948     thumbSize : 300,
28949     multiple : true,
28950     files : false,
28951     method : 'POST',
28952     url : '',
28953     paramName : 'imageUpload',
28954     toolTipName : 'filename',
28955     fieldLabel : '',
28956     labelWidth : 4,
28957     labelAlign : 'left',
28958     editable : true,
28959     delegates : false,
28960     xhr : false, 
28961     
28962     labellg : 0,
28963     labelmd : 0,
28964     labelsm : 0,
28965     labelxs : 0,
28966     
28967     getAutoCreate : function()
28968     {   
28969         var managerWidget = {
28970             tag : 'div',
28971             cls : 'roo-document-manager',
28972             cn : [
28973                 {
28974                     tag : 'input',
28975                     cls : 'roo-document-manager-selector',
28976                     type : 'file'
28977                 },
28978                 {
28979                     tag : 'div',
28980                     cls : 'roo-document-manager-uploader',
28981                     cn : [
28982                         {
28983                             tag : 'div',
28984                             cls : 'roo-document-manager-upload-btn',
28985                             html : '<i class="fa fa-plus"></i>'
28986                         }
28987                     ]
28988                     
28989                 }
28990             ]
28991         };
28992         
28993         var content = [
28994             {
28995                 tag : 'div',
28996                 cls : 'column col-md-12',
28997                 cn : managerWidget
28998             }
28999         ];
29000         
29001         if(this.fieldLabel.length){
29002             
29003             content = [
29004                 {
29005                     tag : 'div',
29006                     cls : 'column col-md-12',
29007                     html : this.fieldLabel
29008                 },
29009                 {
29010                     tag : 'div',
29011                     cls : 'column col-md-12',
29012                     cn : managerWidget
29013                 }
29014             ];
29015
29016             if(this.labelAlign == 'left'){
29017                 content = [
29018                     {
29019                         tag : 'div',
29020                         cls : 'column',
29021                         html : this.fieldLabel
29022                     },
29023                     {
29024                         tag : 'div',
29025                         cls : 'column',
29026                         cn : managerWidget
29027                     }
29028                 ];
29029                 
29030                 if(this.labelWidth > 12){
29031                     content[0].style = "width: " + this.labelWidth + 'px';
29032                 }
29033
29034                 if(this.labelWidth < 13 && this.labelmd == 0){
29035                     this.labelmd = this.labelWidth;
29036                 }
29037
29038                 if(this.labellg > 0){
29039                     content[0].cls += ' col-lg-' + this.labellg;
29040                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29041                 }
29042
29043                 if(this.labelmd > 0){
29044                     content[0].cls += ' col-md-' + this.labelmd;
29045                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29046                 }
29047
29048                 if(this.labelsm > 0){
29049                     content[0].cls += ' col-sm-' + this.labelsm;
29050                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29051                 }
29052
29053                 if(this.labelxs > 0){
29054                     content[0].cls += ' col-xs-' + this.labelxs;
29055                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29056                 }
29057                 
29058             }
29059         }
29060         
29061         var cfg = {
29062             tag : 'div',
29063             cls : 'row clearfix',
29064             cn : content
29065         };
29066         
29067         return cfg;
29068         
29069     },
29070     
29071     initEvents : function()
29072     {
29073         this.managerEl = this.el.select('.roo-document-manager', true).first();
29074         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29075         
29076         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29077         this.selectorEl.hide();
29078         
29079         if(this.multiple){
29080             this.selectorEl.attr('multiple', 'multiple');
29081         }
29082         
29083         this.selectorEl.on('change', this.onFileSelected, this);
29084         
29085         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29086         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29087         
29088         this.uploader.on('click', this.onUploaderClick, this);
29089         
29090         this.renderProgressDialog();
29091         
29092         var _this = this;
29093         
29094         window.addEventListener("resize", function() { _this.refresh(); } );
29095         
29096         this.fireEvent('initial', this);
29097     },
29098     
29099     renderProgressDialog : function()
29100     {
29101         var _this = this;
29102         
29103         this.progressDialog = new Roo.bootstrap.Modal({
29104             cls : 'roo-document-manager-progress-dialog',
29105             allow_close : false,
29106             title : '',
29107             buttons : [
29108                 {
29109                     name  :'cancel',
29110                     weight : 'danger',
29111                     html : 'Cancel'
29112                 }
29113             ], 
29114             listeners : { 
29115                 btnclick : function() {
29116                     _this.uploadCancel();
29117                     this.hide();
29118                 }
29119             }
29120         });
29121          
29122         this.progressDialog.render(Roo.get(document.body));
29123          
29124         this.progress = new Roo.bootstrap.Progress({
29125             cls : 'roo-document-manager-progress',
29126             active : true,
29127             striped : true
29128         });
29129         
29130         this.progress.render(this.progressDialog.getChildContainer());
29131         
29132         this.progressBar = new Roo.bootstrap.ProgressBar({
29133             cls : 'roo-document-manager-progress-bar',
29134             aria_valuenow : 0,
29135             aria_valuemin : 0,
29136             aria_valuemax : 12,
29137             panel : 'success'
29138         });
29139         
29140         this.progressBar.render(this.progress.getChildContainer());
29141     },
29142     
29143     onUploaderClick : function(e)
29144     {
29145         e.preventDefault();
29146      
29147         if(this.fireEvent('beforeselectfile', this) != false){
29148             this.selectorEl.dom.click();
29149         }
29150         
29151     },
29152     
29153     onFileSelected : function(e)
29154     {
29155         e.preventDefault();
29156         
29157         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29158             return;
29159         }
29160         
29161         Roo.each(this.selectorEl.dom.files, function(file){
29162             if(this.fireEvent('inspect', this, file) != false){
29163                 this.files.push(file);
29164             }
29165         }, this);
29166         
29167         this.queue();
29168         
29169     },
29170     
29171     queue : function()
29172     {
29173         this.selectorEl.dom.value = '';
29174         
29175         if(!this.files || !this.files.length){
29176             return;
29177         }
29178         
29179         if(this.boxes > 0 && this.files.length > this.boxes){
29180             this.files = this.files.slice(0, this.boxes);
29181         }
29182         
29183         this.uploader.show();
29184         
29185         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29186             this.uploader.hide();
29187         }
29188         
29189         var _this = this;
29190         
29191         var files = [];
29192         
29193         var docs = [];
29194         
29195         Roo.each(this.files, function(file){
29196             
29197             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29198                 var f = this.renderPreview(file);
29199                 files.push(f);
29200                 return;
29201             }
29202             
29203             if(file.type.indexOf('image') != -1){
29204                 this.delegates.push(
29205                     (function(){
29206                         _this.process(file);
29207                     }).createDelegate(this)
29208                 );
29209         
29210                 return;
29211             }
29212             
29213             docs.push(
29214                 (function(){
29215                     _this.process(file);
29216                 }).createDelegate(this)
29217             );
29218             
29219         }, this);
29220         
29221         this.files = files;
29222         
29223         this.delegates = this.delegates.concat(docs);
29224         
29225         if(!this.delegates.length){
29226             this.refresh();
29227             return;
29228         }
29229         
29230         this.progressBar.aria_valuemax = this.delegates.length;
29231         
29232         this.arrange();
29233         
29234         return;
29235     },
29236     
29237     arrange : function()
29238     {
29239         if(!this.delegates.length){
29240             this.progressDialog.hide();
29241             this.refresh();
29242             return;
29243         }
29244         
29245         var delegate = this.delegates.shift();
29246         
29247         this.progressDialog.show();
29248         
29249         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29250         
29251         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29252         
29253         delegate();
29254     },
29255     
29256     refresh : function()
29257     {
29258         this.uploader.show();
29259         
29260         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29261             this.uploader.hide();
29262         }
29263         
29264         Roo.isTouch ? this.closable(false) : this.closable(true);
29265         
29266         this.fireEvent('refresh', this);
29267     },
29268     
29269     onRemove : function(e, el, o)
29270     {
29271         e.preventDefault();
29272         
29273         this.fireEvent('remove', this, o);
29274         
29275     },
29276     
29277     remove : function(o)
29278     {
29279         var files = [];
29280         
29281         Roo.each(this.files, function(file){
29282             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29283                 files.push(file);
29284                 return;
29285             }
29286
29287             o.target.remove();
29288
29289         }, this);
29290         
29291         this.files = files;
29292         
29293         this.refresh();
29294     },
29295     
29296     clear : function()
29297     {
29298         Roo.each(this.files, function(file){
29299             if(!file.target){
29300                 return;
29301             }
29302             
29303             file.target.remove();
29304
29305         }, this);
29306         
29307         this.files = [];
29308         
29309         this.refresh();
29310     },
29311     
29312     onClick : function(e, el, o)
29313     {
29314         e.preventDefault();
29315         
29316         this.fireEvent('click', this, o);
29317         
29318     },
29319     
29320     closable : function(closable)
29321     {
29322         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29323             
29324             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29325             
29326             if(closable){
29327                 el.show();
29328                 return;
29329             }
29330             
29331             el.hide();
29332             
29333         }, this);
29334     },
29335     
29336     xhrOnLoad : function(xhr)
29337     {
29338         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29339             el.remove();
29340         }, this);
29341         
29342         if (xhr.readyState !== 4) {
29343             this.arrange();
29344             this.fireEvent('exception', this, xhr);
29345             return;
29346         }
29347
29348         var response = Roo.decode(xhr.responseText);
29349         
29350         if(!response.success){
29351             this.arrange();
29352             this.fireEvent('exception', this, xhr);
29353             return;
29354         }
29355         
29356         var file = this.renderPreview(response.data);
29357         
29358         this.files.push(file);
29359         
29360         this.arrange();
29361         
29362         this.fireEvent('afterupload', this, xhr);
29363         
29364     },
29365     
29366     xhrOnError : function(xhr)
29367     {
29368         Roo.log('xhr on error');
29369         
29370         var response = Roo.decode(xhr.responseText);
29371           
29372         Roo.log(response);
29373         
29374         this.arrange();
29375     },
29376     
29377     process : function(file)
29378     {
29379         if(this.fireEvent('process', this, file) !== false){
29380             if(this.editable && file.type.indexOf('image') != -1){
29381                 this.fireEvent('edit', this, file);
29382                 return;
29383             }
29384
29385             this.uploadStart(file, false);
29386
29387             return;
29388         }
29389         
29390     },
29391     
29392     uploadStart : function(file, crop)
29393     {
29394         this.xhr = new XMLHttpRequest();
29395         
29396         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29397             this.arrange();
29398             return;
29399         }
29400         
29401         file.xhr = this.xhr;
29402             
29403         this.managerEl.createChild({
29404             tag : 'div',
29405             cls : 'roo-document-manager-loading',
29406             cn : [
29407                 {
29408                     tag : 'div',
29409                     tooltip : file.name,
29410                     cls : 'roo-document-manager-thumb',
29411                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29412                 }
29413             ]
29414
29415         });
29416
29417         this.xhr.open(this.method, this.url, true);
29418         
29419         var headers = {
29420             "Accept": "application/json",
29421             "Cache-Control": "no-cache",
29422             "X-Requested-With": "XMLHttpRequest"
29423         };
29424         
29425         for (var headerName in headers) {
29426             var headerValue = headers[headerName];
29427             if (headerValue) {
29428                 this.xhr.setRequestHeader(headerName, headerValue);
29429             }
29430         }
29431         
29432         var _this = this;
29433         
29434         this.xhr.onload = function()
29435         {
29436             _this.xhrOnLoad(_this.xhr);
29437         }
29438         
29439         this.xhr.onerror = function()
29440         {
29441             _this.xhrOnError(_this.xhr);
29442         }
29443         
29444         var formData = new FormData();
29445
29446         formData.append('returnHTML', 'NO');
29447         
29448         if(crop){
29449             formData.append('crop', crop);
29450         }
29451         
29452         formData.append(this.paramName, file, file.name);
29453         
29454         var options = {
29455             file : file, 
29456             manually : false
29457         };
29458         
29459         if(this.fireEvent('prepare', this, formData, options) != false){
29460             
29461             if(options.manually){
29462                 return;
29463             }
29464             
29465             this.xhr.send(formData);
29466             return;
29467         };
29468         
29469         this.uploadCancel();
29470     },
29471     
29472     uploadCancel : function()
29473     {
29474         if (this.xhr) {
29475             this.xhr.abort();
29476         }
29477         
29478         this.delegates = [];
29479         
29480         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29481             el.remove();
29482         }, this);
29483         
29484         this.arrange();
29485     },
29486     
29487     renderPreview : function(file)
29488     {
29489         if(typeof(file.target) != 'undefined' && file.target){
29490             return file;
29491         }
29492         
29493         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29494         
29495         var previewEl = this.managerEl.createChild({
29496             tag : 'div',
29497             cls : 'roo-document-manager-preview',
29498             cn : [
29499                 {
29500                     tag : 'div',
29501                     tooltip : file[this.toolTipName],
29502                     cls : 'roo-document-manager-thumb',
29503                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29504                 },
29505                 {
29506                     tag : 'button',
29507                     cls : 'close',
29508                     html : '<i class="fa fa-times-circle"></i>'
29509                 }
29510             ]
29511         });
29512
29513         var close = previewEl.select('button.close', true).first();
29514
29515         close.on('click', this.onRemove, this, file);
29516
29517         file.target = previewEl;
29518
29519         var image = previewEl.select('img', true).first();
29520         
29521         var _this = this;
29522         
29523         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29524         
29525         image.on('click', this.onClick, this, file);
29526         
29527         this.fireEvent('previewrendered', this, file);
29528         
29529         return file;
29530         
29531     },
29532     
29533     onPreviewLoad : function(file, image)
29534     {
29535         if(typeof(file.target) == 'undefined' || !file.target){
29536             return;
29537         }
29538         
29539         var width = image.dom.naturalWidth || image.dom.width;
29540         var height = image.dom.naturalHeight || image.dom.height;
29541         
29542         if(!this.previewResize) {
29543             return;
29544         }
29545         
29546         if(width > height){
29547             file.target.addClass('wide');
29548             return;
29549         }
29550         
29551         file.target.addClass('tall');
29552         return;
29553         
29554     },
29555     
29556     uploadFromSource : function(file, crop)
29557     {
29558         this.xhr = new XMLHttpRequest();
29559         
29560         this.managerEl.createChild({
29561             tag : 'div',
29562             cls : 'roo-document-manager-loading',
29563             cn : [
29564                 {
29565                     tag : 'div',
29566                     tooltip : file.name,
29567                     cls : 'roo-document-manager-thumb',
29568                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29569                 }
29570             ]
29571
29572         });
29573
29574         this.xhr.open(this.method, this.url, true);
29575         
29576         var headers = {
29577             "Accept": "application/json",
29578             "Cache-Control": "no-cache",
29579             "X-Requested-With": "XMLHttpRequest"
29580         };
29581         
29582         for (var headerName in headers) {
29583             var headerValue = headers[headerName];
29584             if (headerValue) {
29585                 this.xhr.setRequestHeader(headerName, headerValue);
29586             }
29587         }
29588         
29589         var _this = this;
29590         
29591         this.xhr.onload = function()
29592         {
29593             _this.xhrOnLoad(_this.xhr);
29594         }
29595         
29596         this.xhr.onerror = function()
29597         {
29598             _this.xhrOnError(_this.xhr);
29599         }
29600         
29601         var formData = new FormData();
29602
29603         formData.append('returnHTML', 'NO');
29604         
29605         formData.append('crop', crop);
29606         
29607         if(typeof(file.filename) != 'undefined'){
29608             formData.append('filename', file.filename);
29609         }
29610         
29611         if(typeof(file.mimetype) != 'undefined'){
29612             formData.append('mimetype', file.mimetype);
29613         }
29614         
29615         Roo.log(formData);
29616         
29617         if(this.fireEvent('prepare', this, formData) != false){
29618             this.xhr.send(formData);
29619         };
29620     }
29621 });
29622
29623 /*
29624 * Licence: LGPL
29625 */
29626
29627 /**
29628  * @class Roo.bootstrap.DocumentViewer
29629  * @extends Roo.bootstrap.Component
29630  * Bootstrap DocumentViewer class
29631  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29632  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29633  * 
29634  * @constructor
29635  * Create a new DocumentViewer
29636  * @param {Object} config The config object
29637  */
29638
29639 Roo.bootstrap.DocumentViewer = function(config){
29640     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29641     
29642     this.addEvents({
29643         /**
29644          * @event initial
29645          * Fire after initEvent
29646          * @param {Roo.bootstrap.DocumentViewer} this
29647          */
29648         "initial" : true,
29649         /**
29650          * @event click
29651          * Fire after click
29652          * @param {Roo.bootstrap.DocumentViewer} this
29653          */
29654         "click" : true,
29655         /**
29656          * @event download
29657          * Fire after download button
29658          * @param {Roo.bootstrap.DocumentViewer} this
29659          */
29660         "download" : true,
29661         /**
29662          * @event trash
29663          * Fire after trash button
29664          * @param {Roo.bootstrap.DocumentViewer} this
29665          */
29666         "trash" : true
29667         
29668     });
29669 };
29670
29671 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29672     
29673     showDownload : true,
29674     
29675     showTrash : true,
29676     
29677     getAutoCreate : function()
29678     {
29679         var cfg = {
29680             tag : 'div',
29681             cls : 'roo-document-viewer',
29682             cn : [
29683                 {
29684                     tag : 'div',
29685                     cls : 'roo-document-viewer-body',
29686                     cn : [
29687                         {
29688                             tag : 'div',
29689                             cls : 'roo-document-viewer-thumb',
29690                             cn : [
29691                                 {
29692                                     tag : 'img',
29693                                     cls : 'roo-document-viewer-image'
29694                                 }
29695                             ]
29696                         }
29697                     ]
29698                 },
29699                 {
29700                     tag : 'div',
29701                     cls : 'roo-document-viewer-footer',
29702                     cn : {
29703                         tag : 'div',
29704                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29705                         cn : [
29706                             {
29707                                 tag : 'div',
29708                                 cls : 'btn-group roo-document-viewer-download',
29709                                 cn : [
29710                                     {
29711                                         tag : 'button',
29712                                         cls : 'btn btn-default',
29713                                         html : '<i class="fa fa-download"></i>'
29714                                     }
29715                                 ]
29716                             },
29717                             {
29718                                 tag : 'div',
29719                                 cls : 'btn-group roo-document-viewer-trash',
29720                                 cn : [
29721                                     {
29722                                         tag : 'button',
29723                                         cls : 'btn btn-default',
29724                                         html : '<i class="fa fa-trash"></i>'
29725                                     }
29726                                 ]
29727                             }
29728                         ]
29729                     }
29730                 }
29731             ]
29732         };
29733         
29734         return cfg;
29735     },
29736     
29737     initEvents : function()
29738     {
29739         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29740         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29741         
29742         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29743         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29744         
29745         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29746         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29747         
29748         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29749         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29750         
29751         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29752         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29753         
29754         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29755         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29756         
29757         this.bodyEl.on('click', this.onClick, this);
29758         this.downloadBtn.on('click', this.onDownload, this);
29759         this.trashBtn.on('click', this.onTrash, this);
29760         
29761         this.downloadBtn.hide();
29762         this.trashBtn.hide();
29763         
29764         if(this.showDownload){
29765             this.downloadBtn.show();
29766         }
29767         
29768         if(this.showTrash){
29769             this.trashBtn.show();
29770         }
29771         
29772         if(!this.showDownload && !this.showTrash) {
29773             this.footerEl.hide();
29774         }
29775         
29776     },
29777     
29778     initial : function()
29779     {
29780         this.fireEvent('initial', this);
29781         
29782     },
29783     
29784     onClick : function(e)
29785     {
29786         e.preventDefault();
29787         
29788         this.fireEvent('click', this);
29789     },
29790     
29791     onDownload : function(e)
29792     {
29793         e.preventDefault();
29794         
29795         this.fireEvent('download', this);
29796     },
29797     
29798     onTrash : function(e)
29799     {
29800         e.preventDefault();
29801         
29802         this.fireEvent('trash', this);
29803     }
29804     
29805 });
29806 /*
29807  * - LGPL
29808  *
29809  * nav progress bar
29810  * 
29811  */
29812
29813 /**
29814  * @class Roo.bootstrap.NavProgressBar
29815  * @extends Roo.bootstrap.Component
29816  * Bootstrap NavProgressBar class
29817  * 
29818  * @constructor
29819  * Create a new nav progress bar
29820  * @param {Object} config The config object
29821  */
29822
29823 Roo.bootstrap.NavProgressBar = function(config){
29824     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29825
29826     this.bullets = this.bullets || [];
29827    
29828 //    Roo.bootstrap.NavProgressBar.register(this);
29829      this.addEvents({
29830         /**
29831              * @event changed
29832              * Fires when the active item changes
29833              * @param {Roo.bootstrap.NavProgressBar} this
29834              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29835              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29836          */
29837         'changed': true
29838      });
29839     
29840 };
29841
29842 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29843     
29844     bullets : [],
29845     barItems : [],
29846     
29847     getAutoCreate : function()
29848     {
29849         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29850         
29851         cfg = {
29852             tag : 'div',
29853             cls : 'roo-navigation-bar-group',
29854             cn : [
29855                 {
29856                     tag : 'div',
29857                     cls : 'roo-navigation-top-bar'
29858                 },
29859                 {
29860                     tag : 'div',
29861                     cls : 'roo-navigation-bullets-bar',
29862                     cn : [
29863                         {
29864                             tag : 'ul',
29865                             cls : 'roo-navigation-bar'
29866                         }
29867                     ]
29868                 },
29869                 
29870                 {
29871                     tag : 'div',
29872                     cls : 'roo-navigation-bottom-bar'
29873                 }
29874             ]
29875             
29876         };
29877         
29878         return cfg;
29879         
29880     },
29881     
29882     initEvents: function() 
29883     {
29884         
29885     },
29886     
29887     onRender : function(ct, position) 
29888     {
29889         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29890         
29891         if(this.bullets.length){
29892             Roo.each(this.bullets, function(b){
29893                this.addItem(b);
29894             }, this);
29895         }
29896         
29897         this.format();
29898         
29899     },
29900     
29901     addItem : function(cfg)
29902     {
29903         var item = new Roo.bootstrap.NavProgressItem(cfg);
29904         
29905         item.parentId = this.id;
29906         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29907         
29908         if(cfg.html){
29909             var top = new Roo.bootstrap.Element({
29910                 tag : 'div',
29911                 cls : 'roo-navigation-bar-text'
29912             });
29913             
29914             var bottom = new Roo.bootstrap.Element({
29915                 tag : 'div',
29916                 cls : 'roo-navigation-bar-text'
29917             });
29918             
29919             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29920             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29921             
29922             var topText = new Roo.bootstrap.Element({
29923                 tag : 'span',
29924                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29925             });
29926             
29927             var bottomText = new Roo.bootstrap.Element({
29928                 tag : 'span',
29929                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29930             });
29931             
29932             topText.onRender(top.el, null);
29933             bottomText.onRender(bottom.el, null);
29934             
29935             item.topEl = top;
29936             item.bottomEl = bottom;
29937         }
29938         
29939         this.barItems.push(item);
29940         
29941         return item;
29942     },
29943     
29944     getActive : function()
29945     {
29946         var active = false;
29947         
29948         Roo.each(this.barItems, function(v){
29949             
29950             if (!v.isActive()) {
29951                 return;
29952             }
29953             
29954             active = v;
29955             return false;
29956             
29957         });
29958         
29959         return active;
29960     },
29961     
29962     setActiveItem : function(item)
29963     {
29964         var prev = false;
29965         
29966         Roo.each(this.barItems, function(v){
29967             if (v.rid == item.rid) {
29968                 return ;
29969             }
29970             
29971             if (v.isActive()) {
29972                 v.setActive(false);
29973                 prev = v;
29974             }
29975         });
29976
29977         item.setActive(true);
29978         
29979         this.fireEvent('changed', this, item, prev);
29980     },
29981     
29982     getBarItem: function(rid)
29983     {
29984         var ret = false;
29985         
29986         Roo.each(this.barItems, function(e) {
29987             if (e.rid != rid) {
29988                 return;
29989             }
29990             
29991             ret =  e;
29992             return false;
29993         });
29994         
29995         return ret;
29996     },
29997     
29998     indexOfItem : function(item)
29999     {
30000         var index = false;
30001         
30002         Roo.each(this.barItems, function(v, i){
30003             
30004             if (v.rid != item.rid) {
30005                 return;
30006             }
30007             
30008             index = i;
30009             return false
30010         });
30011         
30012         return index;
30013     },
30014     
30015     setActiveNext : function()
30016     {
30017         var i = this.indexOfItem(this.getActive());
30018         
30019         if (i > this.barItems.length) {
30020             return;
30021         }
30022         
30023         this.setActiveItem(this.barItems[i+1]);
30024     },
30025     
30026     setActivePrev : function()
30027     {
30028         var i = this.indexOfItem(this.getActive());
30029         
30030         if (i  < 1) {
30031             return;
30032         }
30033         
30034         this.setActiveItem(this.barItems[i-1]);
30035     },
30036     
30037     format : function()
30038     {
30039         if(!this.barItems.length){
30040             return;
30041         }
30042      
30043         var width = 100 / this.barItems.length;
30044         
30045         Roo.each(this.barItems, function(i){
30046             i.el.setStyle('width', width + '%');
30047             i.topEl.el.setStyle('width', width + '%');
30048             i.bottomEl.el.setStyle('width', width + '%');
30049         }, this);
30050         
30051     }
30052     
30053 });
30054 /*
30055  * - LGPL
30056  *
30057  * Nav Progress Item
30058  * 
30059  */
30060
30061 /**
30062  * @class Roo.bootstrap.NavProgressItem
30063  * @extends Roo.bootstrap.Component
30064  * Bootstrap NavProgressItem class
30065  * @cfg {String} rid the reference id
30066  * @cfg {Boolean} active (true|false) Is item active default false
30067  * @cfg {Boolean} disabled (true|false) Is item active default false
30068  * @cfg {String} html
30069  * @cfg {String} position (top|bottom) text position default bottom
30070  * @cfg {String} icon show icon instead of number
30071  * 
30072  * @constructor
30073  * Create a new NavProgressItem
30074  * @param {Object} config The config object
30075  */
30076 Roo.bootstrap.NavProgressItem = function(config){
30077     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30078     this.addEvents({
30079         // raw events
30080         /**
30081          * @event click
30082          * The raw click event for the entire grid.
30083          * @param {Roo.bootstrap.NavProgressItem} this
30084          * @param {Roo.EventObject} e
30085          */
30086         "click" : true
30087     });
30088    
30089 };
30090
30091 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30092     
30093     rid : '',
30094     active : false,
30095     disabled : false,
30096     html : '',
30097     position : 'bottom',
30098     icon : false,
30099     
30100     getAutoCreate : function()
30101     {
30102         var iconCls = 'roo-navigation-bar-item-icon';
30103         
30104         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30105         
30106         var cfg = {
30107             tag: 'li',
30108             cls: 'roo-navigation-bar-item',
30109             cn : [
30110                 {
30111                     tag : 'i',
30112                     cls : iconCls
30113                 }
30114             ]
30115         };
30116         
30117         if(this.active){
30118             cfg.cls += ' active';
30119         }
30120         if(this.disabled){
30121             cfg.cls += ' disabled';
30122         }
30123         
30124         return cfg;
30125     },
30126     
30127     disable : function()
30128     {
30129         this.setDisabled(true);
30130     },
30131     
30132     enable : function()
30133     {
30134         this.setDisabled(false);
30135     },
30136     
30137     initEvents: function() 
30138     {
30139         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30140         
30141         this.iconEl.on('click', this.onClick, this);
30142     },
30143     
30144     onClick : function(e)
30145     {
30146         e.preventDefault();
30147         
30148         if(this.disabled){
30149             return;
30150         }
30151         
30152         if(this.fireEvent('click', this, e) === false){
30153             return;
30154         };
30155         
30156         this.parent().setActiveItem(this);
30157     },
30158     
30159     isActive: function () 
30160     {
30161         return this.active;
30162     },
30163     
30164     setActive : function(state)
30165     {
30166         if(this.active == state){
30167             return;
30168         }
30169         
30170         this.active = state;
30171         
30172         if (state) {
30173             this.el.addClass('active');
30174             return;
30175         }
30176         
30177         this.el.removeClass('active');
30178         
30179         return;
30180     },
30181     
30182     setDisabled : function(state)
30183     {
30184         if(this.disabled == state){
30185             return;
30186         }
30187         
30188         this.disabled = state;
30189         
30190         if (state) {
30191             this.el.addClass('disabled');
30192             return;
30193         }
30194         
30195         this.el.removeClass('disabled');
30196     },
30197     
30198     tooltipEl : function()
30199     {
30200         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30201     }
30202 });
30203  
30204
30205  /*
30206  * - LGPL
30207  *
30208  * FieldLabel
30209  * 
30210  */
30211
30212 /**
30213  * @class Roo.bootstrap.FieldLabel
30214  * @extends Roo.bootstrap.Component
30215  * Bootstrap FieldLabel class
30216  * @cfg {String} html contents of the element
30217  * @cfg {String} tag tag of the element default label
30218  * @cfg {String} cls class of the element
30219  * @cfg {String} target label target 
30220  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30221  * @cfg {String} invalidClass default "text-warning"
30222  * @cfg {String} validClass default "text-success"
30223  * @cfg {String} iconTooltip default "This field is required"
30224  * @cfg {String} indicatorpos (left|right) default left
30225  * 
30226  * @constructor
30227  * Create a new FieldLabel
30228  * @param {Object} config The config object
30229  */
30230
30231 Roo.bootstrap.FieldLabel = function(config){
30232     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30233     
30234     this.addEvents({
30235             /**
30236              * @event invalid
30237              * Fires after the field has been marked as invalid.
30238              * @param {Roo.form.FieldLabel} this
30239              * @param {String} msg The validation message
30240              */
30241             invalid : true,
30242             /**
30243              * @event valid
30244              * Fires after the field has been validated with no errors.
30245              * @param {Roo.form.FieldLabel} this
30246              */
30247             valid : true
30248         });
30249 };
30250
30251 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30252     
30253     tag: 'label',
30254     cls: '',
30255     html: '',
30256     target: '',
30257     allowBlank : true,
30258     invalidClass : 'has-warning',
30259     validClass : 'has-success',
30260     iconTooltip : 'This field is required',
30261     indicatorpos : 'left',
30262     
30263     getAutoCreate : function(){
30264         
30265         var cls = "";
30266         if (!this.allowBlank) {
30267             cls  = "visible";
30268         }
30269         
30270         var cfg = {
30271             tag : this.tag,
30272             cls : 'roo-bootstrap-field-label ' + this.cls,
30273             for : this.target,
30274             cn : [
30275                 {
30276                     tag : 'i',
30277                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30278                     tooltip : this.iconTooltip
30279                 },
30280                 {
30281                     tag : 'span',
30282                     html : this.html
30283                 }
30284             ] 
30285         };
30286         
30287         if(this.indicatorpos == 'right'){
30288             var cfg = {
30289                 tag : this.tag,
30290                 cls : 'roo-bootstrap-field-label ' + this.cls,
30291                 for : this.target,
30292                 cn : [
30293                     {
30294                         tag : 'span',
30295                         html : this.html
30296                     },
30297                     {
30298                         tag : 'i',
30299                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30300                         tooltip : this.iconTooltip
30301                     }
30302                 ] 
30303             };
30304         }
30305         
30306         return cfg;
30307     },
30308     
30309     initEvents: function() 
30310     {
30311         Roo.bootstrap.Element.superclass.initEvents.call(this);
30312         
30313         this.indicator = this.indicatorEl();
30314         
30315         if(this.indicator){
30316             this.indicator.removeClass('visible');
30317             this.indicator.addClass('invisible');
30318         }
30319         
30320         Roo.bootstrap.FieldLabel.register(this);
30321     },
30322     
30323     indicatorEl : function()
30324     {
30325         var indicator = this.el.select('i.roo-required-indicator',true).first();
30326         
30327         if(!indicator){
30328             return false;
30329         }
30330         
30331         return indicator;
30332         
30333     },
30334     
30335     /**
30336      * Mark this field as valid
30337      */
30338     markValid : function()
30339     {
30340         if(this.indicator){
30341             this.indicator.removeClass('visible');
30342             this.indicator.addClass('invisible');
30343         }
30344         
30345         this.el.removeClass(this.invalidClass);
30346         
30347         this.el.addClass(this.validClass);
30348         
30349         this.fireEvent('valid', this);
30350     },
30351     
30352     /**
30353      * Mark this field as invalid
30354      * @param {String} msg The validation message
30355      */
30356     markInvalid : function(msg)
30357     {
30358         if(this.indicator){
30359             this.indicator.removeClass('invisible');
30360             this.indicator.addClass('visible');
30361         }
30362         
30363         this.el.removeClass(this.validClass);
30364         
30365         this.el.addClass(this.invalidClass);
30366         
30367         this.fireEvent('invalid', this, msg);
30368     }
30369     
30370    
30371 });
30372
30373 Roo.apply(Roo.bootstrap.FieldLabel, {
30374     
30375     groups: {},
30376     
30377      /**
30378     * register a FieldLabel Group
30379     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30380     */
30381     register : function(label)
30382     {
30383         if(this.groups.hasOwnProperty(label.target)){
30384             return;
30385         }
30386      
30387         this.groups[label.target] = label;
30388         
30389     },
30390     /**
30391     * fetch a FieldLabel Group based on the target
30392     * @param {string} target
30393     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30394     */
30395     get: function(target) {
30396         if (typeof(this.groups[target]) == 'undefined') {
30397             return false;
30398         }
30399         
30400         return this.groups[target] ;
30401     }
30402 });
30403
30404  
30405
30406  /*
30407  * - LGPL
30408  *
30409  * page DateSplitField.
30410  * 
30411  */
30412
30413
30414 /**
30415  * @class Roo.bootstrap.DateSplitField
30416  * @extends Roo.bootstrap.Component
30417  * Bootstrap DateSplitField class
30418  * @cfg {string} fieldLabel - the label associated
30419  * @cfg {Number} labelWidth set the width of label (0-12)
30420  * @cfg {String} labelAlign (top|left)
30421  * @cfg {Boolean} dayAllowBlank (true|false) default false
30422  * @cfg {Boolean} monthAllowBlank (true|false) default false
30423  * @cfg {Boolean} yearAllowBlank (true|false) default false
30424  * @cfg {string} dayPlaceholder 
30425  * @cfg {string} monthPlaceholder
30426  * @cfg {string} yearPlaceholder
30427  * @cfg {string} dayFormat default 'd'
30428  * @cfg {string} monthFormat default 'm'
30429  * @cfg {string} yearFormat default 'Y'
30430  * @cfg {Number} labellg set the width of label (1-12)
30431  * @cfg {Number} labelmd set the width of label (1-12)
30432  * @cfg {Number} labelsm set the width of label (1-12)
30433  * @cfg {Number} labelxs set the width of label (1-12)
30434
30435  *     
30436  * @constructor
30437  * Create a new DateSplitField
30438  * @param {Object} config The config object
30439  */
30440
30441 Roo.bootstrap.DateSplitField = function(config){
30442     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30443     
30444     this.addEvents({
30445         // raw events
30446          /**
30447          * @event years
30448          * getting the data of years
30449          * @param {Roo.bootstrap.DateSplitField} this
30450          * @param {Object} years
30451          */
30452         "years" : true,
30453         /**
30454          * @event days
30455          * getting the data of days
30456          * @param {Roo.bootstrap.DateSplitField} this
30457          * @param {Object} days
30458          */
30459         "days" : true,
30460         /**
30461          * @event invalid
30462          * Fires after the field has been marked as invalid.
30463          * @param {Roo.form.Field} this
30464          * @param {String} msg The validation message
30465          */
30466         invalid : true,
30467        /**
30468          * @event valid
30469          * Fires after the field has been validated with no errors.
30470          * @param {Roo.form.Field} this
30471          */
30472         valid : true
30473     });
30474 };
30475
30476 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30477     
30478     fieldLabel : '',
30479     labelAlign : 'top',
30480     labelWidth : 3,
30481     dayAllowBlank : false,
30482     monthAllowBlank : false,
30483     yearAllowBlank : false,
30484     dayPlaceholder : '',
30485     monthPlaceholder : '',
30486     yearPlaceholder : '',
30487     dayFormat : 'd',
30488     monthFormat : 'm',
30489     yearFormat : 'Y',
30490     isFormField : true,
30491     labellg : 0,
30492     labelmd : 0,
30493     labelsm : 0,
30494     labelxs : 0,
30495     
30496     getAutoCreate : function()
30497     {
30498         var cfg = {
30499             tag : 'div',
30500             cls : 'row roo-date-split-field-group',
30501             cn : [
30502                 {
30503                     tag : 'input',
30504                     type : 'hidden',
30505                     cls : 'form-hidden-field roo-date-split-field-group-value',
30506                     name : this.name
30507                 }
30508             ]
30509         };
30510         
30511         var labelCls = 'col-md-12';
30512         var contentCls = 'col-md-4';
30513         
30514         if(this.fieldLabel){
30515             
30516             var label = {
30517                 tag : 'div',
30518                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30519                 cn : [
30520                     {
30521                         tag : 'label',
30522                         html : this.fieldLabel
30523                     }
30524                 ]
30525             };
30526             
30527             if(this.labelAlign == 'left'){
30528             
30529                 if(this.labelWidth > 12){
30530                     label.style = "width: " + this.labelWidth + 'px';
30531                 }
30532
30533                 if(this.labelWidth < 13 && this.labelmd == 0){
30534                     this.labelmd = this.labelWidth;
30535                 }
30536
30537                 if(this.labellg > 0){
30538                     labelCls = ' col-lg-' + this.labellg;
30539                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30540                 }
30541
30542                 if(this.labelmd > 0){
30543                     labelCls = ' col-md-' + this.labelmd;
30544                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30545                 }
30546
30547                 if(this.labelsm > 0){
30548                     labelCls = ' col-sm-' + this.labelsm;
30549                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30550                 }
30551
30552                 if(this.labelxs > 0){
30553                     labelCls = ' col-xs-' + this.labelxs;
30554                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30555                 }
30556             }
30557             
30558             label.cls += ' ' + labelCls;
30559             
30560             cfg.cn.push(label);
30561         }
30562         
30563         Roo.each(['day', 'month', 'year'], function(t){
30564             cfg.cn.push({
30565                 tag : 'div',
30566                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30567             });
30568         }, this);
30569         
30570         return cfg;
30571     },
30572     
30573     inputEl: function ()
30574     {
30575         return this.el.select('.roo-date-split-field-group-value', true).first();
30576     },
30577     
30578     onRender : function(ct, position) 
30579     {
30580         var _this = this;
30581         
30582         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30583         
30584         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30585         
30586         this.dayField = new Roo.bootstrap.ComboBox({
30587             allowBlank : this.dayAllowBlank,
30588             alwaysQuery : true,
30589             displayField : 'value',
30590             editable : false,
30591             fieldLabel : '',
30592             forceSelection : true,
30593             mode : 'local',
30594             placeholder : this.dayPlaceholder,
30595             selectOnFocus : true,
30596             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30597             triggerAction : 'all',
30598             typeAhead : true,
30599             valueField : 'value',
30600             store : new Roo.data.SimpleStore({
30601                 data : (function() {    
30602                     var days = [];
30603                     _this.fireEvent('days', _this, days);
30604                     return days;
30605                 })(),
30606                 fields : [ 'value' ]
30607             }),
30608             listeners : {
30609                 select : function (_self, record, index)
30610                 {
30611                     _this.setValue(_this.getValue());
30612                 }
30613             }
30614         });
30615
30616         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30617         
30618         this.monthField = new Roo.bootstrap.MonthField({
30619             after : '<i class=\"fa fa-calendar\"></i>',
30620             allowBlank : this.monthAllowBlank,
30621             placeholder : this.monthPlaceholder,
30622             readOnly : true,
30623             listeners : {
30624                 render : function (_self)
30625                 {
30626                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30627                         e.preventDefault();
30628                         _self.focus();
30629                     });
30630                 },
30631                 select : function (_self, oldvalue, newvalue)
30632                 {
30633                     _this.setValue(_this.getValue());
30634                 }
30635             }
30636         });
30637         
30638         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30639         
30640         this.yearField = new Roo.bootstrap.ComboBox({
30641             allowBlank : this.yearAllowBlank,
30642             alwaysQuery : true,
30643             displayField : 'value',
30644             editable : false,
30645             fieldLabel : '',
30646             forceSelection : true,
30647             mode : 'local',
30648             placeholder : this.yearPlaceholder,
30649             selectOnFocus : true,
30650             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30651             triggerAction : 'all',
30652             typeAhead : true,
30653             valueField : 'value',
30654             store : new Roo.data.SimpleStore({
30655                 data : (function() {
30656                     var years = [];
30657                     _this.fireEvent('years', _this, years);
30658                     return years;
30659                 })(),
30660                 fields : [ 'value' ]
30661             }),
30662             listeners : {
30663                 select : function (_self, record, index)
30664                 {
30665                     _this.setValue(_this.getValue());
30666                 }
30667             }
30668         });
30669
30670         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30671     },
30672     
30673     setValue : function(v, format)
30674     {
30675         this.inputEl.dom.value = v;
30676         
30677         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30678         
30679         var d = Date.parseDate(v, f);
30680         
30681         if(!d){
30682             this.validate();
30683             return;
30684         }
30685         
30686         this.setDay(d.format(this.dayFormat));
30687         this.setMonth(d.format(this.monthFormat));
30688         this.setYear(d.format(this.yearFormat));
30689         
30690         this.validate();
30691         
30692         return;
30693     },
30694     
30695     setDay : function(v)
30696     {
30697         this.dayField.setValue(v);
30698         this.inputEl.dom.value = this.getValue();
30699         this.validate();
30700         return;
30701     },
30702     
30703     setMonth : function(v)
30704     {
30705         this.monthField.setValue(v, true);
30706         this.inputEl.dom.value = this.getValue();
30707         this.validate();
30708         return;
30709     },
30710     
30711     setYear : function(v)
30712     {
30713         this.yearField.setValue(v);
30714         this.inputEl.dom.value = this.getValue();
30715         this.validate();
30716         return;
30717     },
30718     
30719     getDay : function()
30720     {
30721         return this.dayField.getValue();
30722     },
30723     
30724     getMonth : function()
30725     {
30726         return this.monthField.getValue();
30727     },
30728     
30729     getYear : function()
30730     {
30731         return this.yearField.getValue();
30732     },
30733     
30734     getValue : function()
30735     {
30736         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30737         
30738         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30739         
30740         return date;
30741     },
30742     
30743     reset : function()
30744     {
30745         this.setDay('');
30746         this.setMonth('');
30747         this.setYear('');
30748         this.inputEl.dom.value = '';
30749         this.validate();
30750         return;
30751     },
30752     
30753     validate : function()
30754     {
30755         var d = this.dayField.validate();
30756         var m = this.monthField.validate();
30757         var y = this.yearField.validate();
30758         
30759         var valid = true;
30760         
30761         if(
30762                 (!this.dayAllowBlank && !d) ||
30763                 (!this.monthAllowBlank && !m) ||
30764                 (!this.yearAllowBlank && !y)
30765         ){
30766             valid = false;
30767         }
30768         
30769         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30770             return valid;
30771         }
30772         
30773         if(valid){
30774             this.markValid();
30775             return valid;
30776         }
30777         
30778         this.markInvalid();
30779         
30780         return valid;
30781     },
30782     
30783     markValid : function()
30784     {
30785         
30786         var label = this.el.select('label', true).first();
30787         var icon = this.el.select('i.fa-star', true).first();
30788
30789         if(label && icon){
30790             icon.remove();
30791         }
30792         
30793         this.fireEvent('valid', this);
30794     },
30795     
30796      /**
30797      * Mark this field as invalid
30798      * @param {String} msg The validation message
30799      */
30800     markInvalid : function(msg)
30801     {
30802         
30803         var label = this.el.select('label', true).first();
30804         var icon = this.el.select('i.fa-star', true).first();
30805
30806         if(label && !icon){
30807             this.el.select('.roo-date-split-field-label', true).createChild({
30808                 tag : 'i',
30809                 cls : 'text-danger fa fa-lg fa-star',
30810                 tooltip : 'This field is required',
30811                 style : 'margin-right:5px;'
30812             }, label, true);
30813         }
30814         
30815         this.fireEvent('invalid', this, msg);
30816     },
30817     
30818     clearInvalid : function()
30819     {
30820         var label = this.el.select('label', true).first();
30821         var icon = this.el.select('i.fa-star', true).first();
30822
30823         if(label && icon){
30824             icon.remove();
30825         }
30826         
30827         this.fireEvent('valid', this);
30828     },
30829     
30830     getName: function()
30831     {
30832         return this.name;
30833     }
30834     
30835 });
30836
30837  /**
30838  *
30839  * This is based on 
30840  * http://masonry.desandro.com
30841  *
30842  * The idea is to render all the bricks based on vertical width...
30843  *
30844  * The original code extends 'outlayer' - we might need to use that....
30845  * 
30846  */
30847
30848
30849 /**
30850  * @class Roo.bootstrap.LayoutMasonry
30851  * @extends Roo.bootstrap.Component
30852  * Bootstrap Layout Masonry class
30853  * 
30854  * @constructor
30855  * Create a new Element
30856  * @param {Object} config The config object
30857  */
30858
30859 Roo.bootstrap.LayoutMasonry = function(config){
30860     
30861     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30862     
30863     this.bricks = [];
30864     
30865     Roo.bootstrap.LayoutMasonry.register(this);
30866     
30867     this.addEvents({
30868         // raw events
30869         /**
30870          * @event layout
30871          * Fire after layout the items
30872          * @param {Roo.bootstrap.LayoutMasonry} this
30873          * @param {Roo.EventObject} e
30874          */
30875         "layout" : true
30876     });
30877     
30878 };
30879
30880 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30881     
30882     /**
30883      * @cfg {Boolean} isLayoutInstant = no animation?
30884      */   
30885     isLayoutInstant : false, // needed?
30886    
30887     /**
30888      * @cfg {Number} boxWidth  width of the columns
30889      */   
30890     boxWidth : 450,
30891     
30892       /**
30893      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30894      */   
30895     boxHeight : 0,
30896     
30897     /**
30898      * @cfg {Number} padWidth padding below box..
30899      */   
30900     padWidth : 10, 
30901     
30902     /**
30903      * @cfg {Number} gutter gutter width..
30904      */   
30905     gutter : 10,
30906     
30907      /**
30908      * @cfg {Number} maxCols maximum number of columns
30909      */   
30910     
30911     maxCols: 0,
30912     
30913     /**
30914      * @cfg {Boolean} isAutoInitial defalut true
30915      */   
30916     isAutoInitial : true, 
30917     
30918     containerWidth: 0,
30919     
30920     /**
30921      * @cfg {Boolean} isHorizontal defalut false
30922      */   
30923     isHorizontal : false, 
30924
30925     currentSize : null,
30926     
30927     tag: 'div',
30928     
30929     cls: '',
30930     
30931     bricks: null, //CompositeElement
30932     
30933     cols : 1,
30934     
30935     _isLayoutInited : false,
30936     
30937 //    isAlternative : false, // only use for vertical layout...
30938     
30939     /**
30940      * @cfg {Number} alternativePadWidth padding below box..
30941      */   
30942     alternativePadWidth : 50,
30943     
30944     selectedBrick : [],
30945     
30946     getAutoCreate : function(){
30947         
30948         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30949         
30950         var cfg = {
30951             tag: this.tag,
30952             cls: 'blog-masonary-wrapper ' + this.cls,
30953             cn : {
30954                 cls : 'mas-boxes masonary'
30955             }
30956         };
30957         
30958         return cfg;
30959     },
30960     
30961     getChildContainer: function( )
30962     {
30963         if (this.boxesEl) {
30964             return this.boxesEl;
30965         }
30966         
30967         this.boxesEl = this.el.select('.mas-boxes').first();
30968         
30969         return this.boxesEl;
30970     },
30971     
30972     
30973     initEvents : function()
30974     {
30975         var _this = this;
30976         
30977         if(this.isAutoInitial){
30978             Roo.log('hook children rendered');
30979             this.on('childrenrendered', function() {
30980                 Roo.log('children rendered');
30981                 _this.initial();
30982             } ,this);
30983         }
30984     },
30985     
30986     initial : function()
30987     {
30988         this.selectedBrick = [];
30989         
30990         this.currentSize = this.el.getBox(true);
30991         
30992         Roo.EventManager.onWindowResize(this.resize, this); 
30993
30994         if(!this.isAutoInitial){
30995             this.layout();
30996             return;
30997         }
30998         
30999         this.layout();
31000         
31001         return;
31002         //this.layout.defer(500,this);
31003         
31004     },
31005     
31006     resize : function()
31007     {
31008         var cs = this.el.getBox(true);
31009         
31010         if (
31011                 this.currentSize.width == cs.width && 
31012                 this.currentSize.x == cs.x && 
31013                 this.currentSize.height == cs.height && 
31014                 this.currentSize.y == cs.y 
31015         ) {
31016             Roo.log("no change in with or X or Y");
31017             return;
31018         }
31019         
31020         this.currentSize = cs;
31021         
31022         this.layout();
31023         
31024     },
31025     
31026     layout : function()
31027     {   
31028         this._resetLayout();
31029         
31030         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31031         
31032         this.layoutItems( isInstant );
31033       
31034         this._isLayoutInited = true;
31035         
31036         this.fireEvent('layout', this);
31037         
31038     },
31039     
31040     _resetLayout : function()
31041     {
31042         if(this.isHorizontal){
31043             this.horizontalMeasureColumns();
31044             return;
31045         }
31046         
31047         this.verticalMeasureColumns();
31048         
31049     },
31050     
31051     verticalMeasureColumns : function()
31052     {
31053         this.getContainerWidth();
31054         
31055 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31056 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31057 //            return;
31058 //        }
31059         
31060         var boxWidth = this.boxWidth + this.padWidth;
31061         
31062         if(this.containerWidth < this.boxWidth){
31063             boxWidth = this.containerWidth
31064         }
31065         
31066         var containerWidth = this.containerWidth;
31067         
31068         var cols = Math.floor(containerWidth / boxWidth);
31069         
31070         this.cols = Math.max( cols, 1 );
31071         
31072         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31073         
31074         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31075         
31076         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31077         
31078         this.colWidth = boxWidth + avail - this.padWidth;
31079         
31080         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31081         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31082     },
31083     
31084     horizontalMeasureColumns : function()
31085     {
31086         this.getContainerWidth();
31087         
31088         var boxWidth = this.boxWidth;
31089         
31090         if(this.containerWidth < boxWidth){
31091             boxWidth = this.containerWidth;
31092         }
31093         
31094         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31095         
31096         this.el.setHeight(boxWidth);
31097         
31098     },
31099     
31100     getContainerWidth : function()
31101     {
31102         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31103     },
31104     
31105     layoutItems : function( isInstant )
31106     {
31107         Roo.log(this.bricks);
31108         
31109         var items = Roo.apply([], this.bricks);
31110         
31111         if(this.isHorizontal){
31112             this._horizontalLayoutItems( items , isInstant );
31113             return;
31114         }
31115         
31116 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31117 //            this._verticalAlternativeLayoutItems( items , isInstant );
31118 //            return;
31119 //        }
31120         
31121         this._verticalLayoutItems( items , isInstant );
31122         
31123     },
31124     
31125     _verticalLayoutItems : function ( items , isInstant)
31126     {
31127         if ( !items || !items.length ) {
31128             return;
31129         }
31130         
31131         var standard = [
31132             ['xs', 'xs', 'xs', 'tall'],
31133             ['xs', 'xs', 'tall'],
31134             ['xs', 'xs', 'sm'],
31135             ['xs', 'xs', 'xs'],
31136             ['xs', 'tall'],
31137             ['xs', 'sm'],
31138             ['xs', 'xs'],
31139             ['xs'],
31140             
31141             ['sm', 'xs', 'xs'],
31142             ['sm', 'xs'],
31143             ['sm'],
31144             
31145             ['tall', 'xs', 'xs', 'xs'],
31146             ['tall', 'xs', 'xs'],
31147             ['tall', 'xs'],
31148             ['tall']
31149             
31150         ];
31151         
31152         var queue = [];
31153         
31154         var boxes = [];
31155         
31156         var box = [];
31157         
31158         Roo.each(items, function(item, k){
31159             
31160             switch (item.size) {
31161                 // these layouts take up a full box,
31162                 case 'md' :
31163                 case 'md-left' :
31164                 case 'md-right' :
31165                 case 'wide' :
31166                     
31167                     if(box.length){
31168                         boxes.push(box);
31169                         box = [];
31170                     }
31171                     
31172                     boxes.push([item]);
31173                     
31174                     break;
31175                     
31176                 case 'xs' :
31177                 case 'sm' :
31178                 case 'tall' :
31179                     
31180                     box.push(item);
31181                     
31182                     break;
31183                 default :
31184                     break;
31185                     
31186             }
31187             
31188         }, this);
31189         
31190         if(box.length){
31191             boxes.push(box);
31192             box = [];
31193         }
31194         
31195         var filterPattern = function(box, length)
31196         {
31197             if(!box.length){
31198                 return;
31199             }
31200             
31201             var match = false;
31202             
31203             var pattern = box.slice(0, length);
31204             
31205             var format = [];
31206             
31207             Roo.each(pattern, function(i){
31208                 format.push(i.size);
31209             }, this);
31210             
31211             Roo.each(standard, function(s){
31212                 
31213                 if(String(s) != String(format)){
31214                     return;
31215                 }
31216                 
31217                 match = true;
31218                 return false;
31219                 
31220             }, this);
31221             
31222             if(!match && length == 1){
31223                 return;
31224             }
31225             
31226             if(!match){
31227                 filterPattern(box, length - 1);
31228                 return;
31229             }
31230                 
31231             queue.push(pattern);
31232
31233             box = box.slice(length, box.length);
31234
31235             filterPattern(box, 4);
31236
31237             return;
31238             
31239         }
31240         
31241         Roo.each(boxes, function(box, k){
31242             
31243             if(!box.length){
31244                 return;
31245             }
31246             
31247             if(box.length == 1){
31248                 queue.push(box);
31249                 return;
31250             }
31251             
31252             filterPattern(box, 4);
31253             
31254         }, this);
31255         
31256         this._processVerticalLayoutQueue( queue, isInstant );
31257         
31258     },
31259     
31260 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31261 //    {
31262 //        if ( !items || !items.length ) {
31263 //            return;
31264 //        }
31265 //
31266 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31267 //        
31268 //    },
31269     
31270     _horizontalLayoutItems : function ( items , isInstant)
31271     {
31272         if ( !items || !items.length || items.length < 3) {
31273             return;
31274         }
31275         
31276         items.reverse();
31277         
31278         var eItems = items.slice(0, 3);
31279         
31280         items = items.slice(3, items.length);
31281         
31282         var standard = [
31283             ['xs', 'xs', 'xs', 'wide'],
31284             ['xs', 'xs', 'wide'],
31285             ['xs', 'xs', 'sm'],
31286             ['xs', 'xs', 'xs'],
31287             ['xs', 'wide'],
31288             ['xs', 'sm'],
31289             ['xs', 'xs'],
31290             ['xs'],
31291             
31292             ['sm', 'xs', 'xs'],
31293             ['sm', 'xs'],
31294             ['sm'],
31295             
31296             ['wide', 'xs', 'xs', 'xs'],
31297             ['wide', 'xs', 'xs'],
31298             ['wide', 'xs'],
31299             ['wide'],
31300             
31301             ['wide-thin']
31302         ];
31303         
31304         var queue = [];
31305         
31306         var boxes = [];
31307         
31308         var box = [];
31309         
31310         Roo.each(items, function(item, k){
31311             
31312             switch (item.size) {
31313                 case 'md' :
31314                 case 'md-left' :
31315                 case 'md-right' :
31316                 case 'tall' :
31317                     
31318                     if(box.length){
31319                         boxes.push(box);
31320                         box = [];
31321                     }
31322                     
31323                     boxes.push([item]);
31324                     
31325                     break;
31326                     
31327                 case 'xs' :
31328                 case 'sm' :
31329                 case 'wide' :
31330                 case 'wide-thin' :
31331                     
31332                     box.push(item);
31333                     
31334                     break;
31335                 default :
31336                     break;
31337                     
31338             }
31339             
31340         }, this);
31341         
31342         if(box.length){
31343             boxes.push(box);
31344             box = [];
31345         }
31346         
31347         var filterPattern = function(box, length)
31348         {
31349             if(!box.length){
31350                 return;
31351             }
31352             
31353             var match = false;
31354             
31355             var pattern = box.slice(0, length);
31356             
31357             var format = [];
31358             
31359             Roo.each(pattern, function(i){
31360                 format.push(i.size);
31361             }, this);
31362             
31363             Roo.each(standard, function(s){
31364                 
31365                 if(String(s) != String(format)){
31366                     return;
31367                 }
31368                 
31369                 match = true;
31370                 return false;
31371                 
31372             }, this);
31373             
31374             if(!match && length == 1){
31375                 return;
31376             }
31377             
31378             if(!match){
31379                 filterPattern(box, length - 1);
31380                 return;
31381             }
31382                 
31383             queue.push(pattern);
31384
31385             box = box.slice(length, box.length);
31386
31387             filterPattern(box, 4);
31388
31389             return;
31390             
31391         }
31392         
31393         Roo.each(boxes, function(box, k){
31394             
31395             if(!box.length){
31396                 return;
31397             }
31398             
31399             if(box.length == 1){
31400                 queue.push(box);
31401                 return;
31402             }
31403             
31404             filterPattern(box, 4);
31405             
31406         }, this);
31407         
31408         
31409         var prune = [];
31410         
31411         var pos = this.el.getBox(true);
31412         
31413         var minX = pos.x;
31414         
31415         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31416         
31417         var hit_end = false;
31418         
31419         Roo.each(queue, function(box){
31420             
31421             if(hit_end){
31422                 
31423                 Roo.each(box, function(b){
31424                 
31425                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31426                     b.el.hide();
31427
31428                 }, this);
31429
31430                 return;
31431             }
31432             
31433             var mx = 0;
31434             
31435             Roo.each(box, function(b){
31436                 
31437                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31438                 b.el.show();
31439
31440                 mx = Math.max(mx, b.x);
31441                 
31442             }, this);
31443             
31444             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31445             
31446             if(maxX < minX){
31447                 
31448                 Roo.each(box, function(b){
31449                 
31450                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31451                     b.el.hide();
31452                     
31453                 }, this);
31454                 
31455                 hit_end = true;
31456                 
31457                 return;
31458             }
31459             
31460             prune.push(box);
31461             
31462         }, this);
31463         
31464         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31465     },
31466     
31467     /** Sets position of item in DOM
31468     * @param {Element} item
31469     * @param {Number} x - horizontal position
31470     * @param {Number} y - vertical position
31471     * @param {Boolean} isInstant - disables transitions
31472     */
31473     _processVerticalLayoutQueue : function( queue, isInstant )
31474     {
31475         var pos = this.el.getBox(true);
31476         var x = pos.x;
31477         var y = pos.y;
31478         var maxY = [];
31479         
31480         for (var i = 0; i < this.cols; i++){
31481             maxY[i] = pos.y;
31482         }
31483         
31484         Roo.each(queue, function(box, k){
31485             
31486             var col = k % this.cols;
31487             
31488             Roo.each(box, function(b,kk){
31489                 
31490                 b.el.position('absolute');
31491                 
31492                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31493                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31494                 
31495                 if(b.size == 'md-left' || b.size == 'md-right'){
31496                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31497                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31498                 }
31499                 
31500                 b.el.setWidth(width);
31501                 b.el.setHeight(height);
31502                 // iframe?
31503                 b.el.select('iframe',true).setSize(width,height);
31504                 
31505             }, this);
31506             
31507             for (var i = 0; i < this.cols; i++){
31508                 
31509                 if(maxY[i] < maxY[col]){
31510                     col = i;
31511                     continue;
31512                 }
31513                 
31514                 col = Math.min(col, i);
31515                 
31516             }
31517             
31518             x = pos.x + col * (this.colWidth + this.padWidth);
31519             
31520             y = maxY[col];
31521             
31522             var positions = [];
31523             
31524             switch (box.length){
31525                 case 1 :
31526                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31527                     break;
31528                 case 2 :
31529                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31530                     break;
31531                 case 3 :
31532                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31533                     break;
31534                 case 4 :
31535                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31536                     break;
31537                 default :
31538                     break;
31539             }
31540             
31541             Roo.each(box, function(b,kk){
31542                 
31543                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31544                 
31545                 var sz = b.el.getSize();
31546                 
31547                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31548                 
31549             }, this);
31550             
31551         }, this);
31552         
31553         var mY = 0;
31554         
31555         for (var i = 0; i < this.cols; i++){
31556             mY = Math.max(mY, maxY[i]);
31557         }
31558         
31559         this.el.setHeight(mY - pos.y);
31560         
31561     },
31562     
31563 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31564 //    {
31565 //        var pos = this.el.getBox(true);
31566 //        var x = pos.x;
31567 //        var y = pos.y;
31568 //        var maxX = pos.right;
31569 //        
31570 //        var maxHeight = 0;
31571 //        
31572 //        Roo.each(items, function(item, k){
31573 //            
31574 //            var c = k % 2;
31575 //            
31576 //            item.el.position('absolute');
31577 //                
31578 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31579 //
31580 //            item.el.setWidth(width);
31581 //
31582 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31583 //
31584 //            item.el.setHeight(height);
31585 //            
31586 //            if(c == 0){
31587 //                item.el.setXY([x, y], isInstant ? false : true);
31588 //            } else {
31589 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31590 //            }
31591 //            
31592 //            y = y + height + this.alternativePadWidth;
31593 //            
31594 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31595 //            
31596 //        }, this);
31597 //        
31598 //        this.el.setHeight(maxHeight);
31599 //        
31600 //    },
31601     
31602     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31603     {
31604         var pos = this.el.getBox(true);
31605         
31606         var minX = pos.x;
31607         var minY = pos.y;
31608         
31609         var maxX = pos.right;
31610         
31611         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31612         
31613         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31614         
31615         Roo.each(queue, function(box, k){
31616             
31617             Roo.each(box, function(b, kk){
31618                 
31619                 b.el.position('absolute');
31620                 
31621                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31622                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31623                 
31624                 if(b.size == 'md-left' || b.size == 'md-right'){
31625                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31626                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31627                 }
31628                 
31629                 b.el.setWidth(width);
31630                 b.el.setHeight(height);
31631                 
31632             }, this);
31633             
31634             if(!box.length){
31635                 return;
31636             }
31637             
31638             var positions = [];
31639             
31640             switch (box.length){
31641                 case 1 :
31642                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31643                     break;
31644                 case 2 :
31645                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31646                     break;
31647                 case 3 :
31648                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31649                     break;
31650                 case 4 :
31651                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31652                     break;
31653                 default :
31654                     break;
31655             }
31656             
31657             Roo.each(box, function(b,kk){
31658                 
31659                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31660                 
31661                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31662                 
31663             }, this);
31664             
31665         }, this);
31666         
31667     },
31668     
31669     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31670     {
31671         Roo.each(eItems, function(b,k){
31672             
31673             b.size = (k == 0) ? 'sm' : 'xs';
31674             b.x = (k == 0) ? 2 : 1;
31675             b.y = (k == 0) ? 2 : 1;
31676             
31677             b.el.position('absolute');
31678             
31679             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31680                 
31681             b.el.setWidth(width);
31682             
31683             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31684             
31685             b.el.setHeight(height);
31686             
31687         }, this);
31688
31689         var positions = [];
31690         
31691         positions.push({
31692             x : maxX - this.unitWidth * 2 - this.gutter,
31693             y : minY
31694         });
31695         
31696         positions.push({
31697             x : maxX - this.unitWidth,
31698             y : minY + (this.unitWidth + this.gutter) * 2
31699         });
31700         
31701         positions.push({
31702             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31703             y : minY
31704         });
31705         
31706         Roo.each(eItems, function(b,k){
31707             
31708             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31709
31710         }, this);
31711         
31712     },
31713     
31714     getVerticalOneBoxColPositions : function(x, y, box)
31715     {
31716         var pos = [];
31717         
31718         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31719         
31720         if(box[0].size == 'md-left'){
31721             rand = 0;
31722         }
31723         
31724         if(box[0].size == 'md-right'){
31725             rand = 1;
31726         }
31727         
31728         pos.push({
31729             x : x + (this.unitWidth + this.gutter) * rand,
31730             y : y
31731         });
31732         
31733         return pos;
31734     },
31735     
31736     getVerticalTwoBoxColPositions : function(x, y, box)
31737     {
31738         var pos = [];
31739         
31740         if(box[0].size == 'xs'){
31741             
31742             pos.push({
31743                 x : x,
31744                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31745             });
31746
31747             pos.push({
31748                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31749                 y : y
31750             });
31751             
31752             return pos;
31753             
31754         }
31755         
31756         pos.push({
31757             x : x,
31758             y : y
31759         });
31760
31761         pos.push({
31762             x : x + (this.unitWidth + this.gutter) * 2,
31763             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31764         });
31765         
31766         return pos;
31767         
31768     },
31769     
31770     getVerticalThreeBoxColPositions : function(x, y, box)
31771     {
31772         var pos = [];
31773         
31774         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31775             
31776             pos.push({
31777                 x : x,
31778                 y : y
31779             });
31780
31781             pos.push({
31782                 x : x + (this.unitWidth + this.gutter) * 1,
31783                 y : y
31784             });
31785             
31786             pos.push({
31787                 x : x + (this.unitWidth + this.gutter) * 2,
31788                 y : y
31789             });
31790             
31791             return pos;
31792             
31793         }
31794         
31795         if(box[0].size == 'xs' && box[1].size == 'xs'){
31796             
31797             pos.push({
31798                 x : x,
31799                 y : y
31800             });
31801
31802             pos.push({
31803                 x : x,
31804                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31805             });
31806             
31807             pos.push({
31808                 x : x + (this.unitWidth + this.gutter) * 1,
31809                 y : y
31810             });
31811             
31812             return pos;
31813             
31814         }
31815         
31816         pos.push({
31817             x : x,
31818             y : y
31819         });
31820
31821         pos.push({
31822             x : x + (this.unitWidth + this.gutter) * 2,
31823             y : y
31824         });
31825
31826         pos.push({
31827             x : x + (this.unitWidth + this.gutter) * 2,
31828             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31829         });
31830             
31831         return pos;
31832         
31833     },
31834     
31835     getVerticalFourBoxColPositions : function(x, y, box)
31836     {
31837         var pos = [];
31838         
31839         if(box[0].size == 'xs'){
31840             
31841             pos.push({
31842                 x : x,
31843                 y : y
31844             });
31845
31846             pos.push({
31847                 x : x,
31848                 y : y + (this.unitHeight + this.gutter) * 1
31849             });
31850             
31851             pos.push({
31852                 x : x,
31853                 y : y + (this.unitHeight + this.gutter) * 2
31854             });
31855             
31856             pos.push({
31857                 x : x + (this.unitWidth + this.gutter) * 1,
31858                 y : y
31859             });
31860             
31861             return pos;
31862             
31863         }
31864         
31865         pos.push({
31866             x : x,
31867             y : y
31868         });
31869
31870         pos.push({
31871             x : x + (this.unitWidth + this.gutter) * 2,
31872             y : y
31873         });
31874
31875         pos.push({
31876             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31877             y : y + (this.unitHeight + this.gutter) * 1
31878         });
31879
31880         pos.push({
31881             x : x + (this.unitWidth + this.gutter) * 2,
31882             y : y + (this.unitWidth + this.gutter) * 2
31883         });
31884
31885         return pos;
31886         
31887     },
31888     
31889     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31890     {
31891         var pos = [];
31892         
31893         if(box[0].size == 'md-left'){
31894             pos.push({
31895                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31896                 y : minY
31897             });
31898             
31899             return pos;
31900         }
31901         
31902         if(box[0].size == 'md-right'){
31903             pos.push({
31904                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31905                 y : minY + (this.unitWidth + this.gutter) * 1
31906             });
31907             
31908             return pos;
31909         }
31910         
31911         var rand = Math.floor(Math.random() * (4 - box[0].y));
31912         
31913         pos.push({
31914             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31915             y : minY + (this.unitWidth + this.gutter) * rand
31916         });
31917         
31918         return pos;
31919         
31920     },
31921     
31922     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31923     {
31924         var pos = [];
31925         
31926         if(box[0].size == 'xs'){
31927             
31928             pos.push({
31929                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31930                 y : minY
31931             });
31932
31933             pos.push({
31934                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31935                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31936             });
31937             
31938             return pos;
31939             
31940         }
31941         
31942         pos.push({
31943             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31944             y : minY
31945         });
31946
31947         pos.push({
31948             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31949             y : minY + (this.unitWidth + this.gutter) * 2
31950         });
31951         
31952         return pos;
31953         
31954     },
31955     
31956     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31957     {
31958         var pos = [];
31959         
31960         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31961             
31962             pos.push({
31963                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31964                 y : minY
31965             });
31966
31967             pos.push({
31968                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31969                 y : minY + (this.unitWidth + this.gutter) * 1
31970             });
31971             
31972             pos.push({
31973                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31974                 y : minY + (this.unitWidth + this.gutter) * 2
31975             });
31976             
31977             return pos;
31978             
31979         }
31980         
31981         if(box[0].size == 'xs' && box[1].size == 'xs'){
31982             
31983             pos.push({
31984                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31985                 y : minY
31986             });
31987
31988             pos.push({
31989                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31990                 y : minY
31991             });
31992             
31993             pos.push({
31994                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31995                 y : minY + (this.unitWidth + this.gutter) * 1
31996             });
31997             
31998             return pos;
31999             
32000         }
32001         
32002         pos.push({
32003             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32004             y : minY
32005         });
32006
32007         pos.push({
32008             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32009             y : minY + (this.unitWidth + this.gutter) * 2
32010         });
32011
32012         pos.push({
32013             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32014             y : minY + (this.unitWidth + this.gutter) * 2
32015         });
32016             
32017         return pos;
32018         
32019     },
32020     
32021     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32022     {
32023         var pos = [];
32024         
32025         if(box[0].size == 'xs'){
32026             
32027             pos.push({
32028                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32029                 y : minY
32030             });
32031
32032             pos.push({
32033                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32034                 y : minY
32035             });
32036             
32037             pos.push({
32038                 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),
32039                 y : minY
32040             });
32041             
32042             pos.push({
32043                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32044                 y : minY + (this.unitWidth + this.gutter) * 1
32045             });
32046             
32047             return pos;
32048             
32049         }
32050         
32051         pos.push({
32052             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32053             y : minY
32054         });
32055         
32056         pos.push({
32057             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32058             y : minY + (this.unitWidth + this.gutter) * 2
32059         });
32060         
32061         pos.push({
32062             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32063             y : minY + (this.unitWidth + this.gutter) * 2
32064         });
32065         
32066         pos.push({
32067             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),
32068             y : minY + (this.unitWidth + this.gutter) * 2
32069         });
32070
32071         return pos;
32072         
32073     },
32074     
32075     /**
32076     * remove a Masonry Brick
32077     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32078     */
32079     removeBrick : function(brick_id)
32080     {
32081         if (!brick_id) {
32082             return;
32083         }
32084         
32085         for (var i = 0; i<this.bricks.length; i++) {
32086             if (this.bricks[i].id == brick_id) {
32087                 this.bricks.splice(i,1);
32088                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32089                 this.initial();
32090             }
32091         }
32092     },
32093     
32094     /**
32095     * adds a Masonry Brick
32096     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32097     */
32098     addBrick : function(cfg)
32099     {
32100         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32101         //this.register(cn);
32102         cn.parentId = this.id;
32103         cn.render(this.el);
32104         return cn;
32105     },
32106     
32107     /**
32108     * register a Masonry Brick
32109     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32110     */
32111     
32112     register : function(brick)
32113     {
32114         this.bricks.push(brick);
32115         brick.masonryId = this.id;
32116     },
32117     
32118     /**
32119     * clear all the Masonry Brick
32120     */
32121     clearAll : function()
32122     {
32123         this.bricks = [];
32124         //this.getChildContainer().dom.innerHTML = "";
32125         this.el.dom.innerHTML = '';
32126     },
32127     
32128     getSelected : function()
32129     {
32130         if (!this.selectedBrick) {
32131             return false;
32132         }
32133         
32134         return this.selectedBrick;
32135     }
32136 });
32137
32138 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32139     
32140     groups: {},
32141      /**
32142     * register a Masonry Layout
32143     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32144     */
32145     
32146     register : function(layout)
32147     {
32148         this.groups[layout.id] = layout;
32149     },
32150     /**
32151     * fetch a  Masonry Layout based on the masonry layout ID
32152     * @param {string} the masonry layout to add
32153     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32154     */
32155     
32156     get: function(layout_id) {
32157         if (typeof(this.groups[layout_id]) == 'undefined') {
32158             return false;
32159         }
32160         return this.groups[layout_id] ;
32161     }
32162     
32163     
32164     
32165 });
32166
32167  
32168
32169  /**
32170  *
32171  * This is based on 
32172  * http://masonry.desandro.com
32173  *
32174  * The idea is to render all the bricks based on vertical width...
32175  *
32176  * The original code extends 'outlayer' - we might need to use that....
32177  * 
32178  */
32179
32180
32181 /**
32182  * @class Roo.bootstrap.LayoutMasonryAuto
32183  * @extends Roo.bootstrap.Component
32184  * Bootstrap Layout Masonry class
32185  * 
32186  * @constructor
32187  * Create a new Element
32188  * @param {Object} config The config object
32189  */
32190
32191 Roo.bootstrap.LayoutMasonryAuto = function(config){
32192     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32193 };
32194
32195 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32196     
32197       /**
32198      * @cfg {Boolean} isFitWidth  - resize the width..
32199      */   
32200     isFitWidth : false,  // options..
32201     /**
32202      * @cfg {Boolean} isOriginLeft = left align?
32203      */   
32204     isOriginLeft : true,
32205     /**
32206      * @cfg {Boolean} isOriginTop = top align?
32207      */   
32208     isOriginTop : false,
32209     /**
32210      * @cfg {Boolean} isLayoutInstant = no animation?
32211      */   
32212     isLayoutInstant : false, // needed?
32213     /**
32214      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32215      */   
32216     isResizingContainer : true,
32217     /**
32218      * @cfg {Number} columnWidth  width of the columns 
32219      */   
32220     
32221     columnWidth : 0,
32222     
32223     /**
32224      * @cfg {Number} maxCols maximum number of columns
32225      */   
32226     
32227     maxCols: 0,
32228     /**
32229      * @cfg {Number} padHeight padding below box..
32230      */   
32231     
32232     padHeight : 10, 
32233     
32234     /**
32235      * @cfg {Boolean} isAutoInitial defalut true
32236      */   
32237     
32238     isAutoInitial : true, 
32239     
32240     // private?
32241     gutter : 0,
32242     
32243     containerWidth: 0,
32244     initialColumnWidth : 0,
32245     currentSize : null,
32246     
32247     colYs : null, // array.
32248     maxY : 0,
32249     padWidth: 10,
32250     
32251     
32252     tag: 'div',
32253     cls: '',
32254     bricks: null, //CompositeElement
32255     cols : 0, // array?
32256     // element : null, // wrapped now this.el
32257     _isLayoutInited : null, 
32258     
32259     
32260     getAutoCreate : function(){
32261         
32262         var cfg = {
32263             tag: this.tag,
32264             cls: 'blog-masonary-wrapper ' + this.cls,
32265             cn : {
32266                 cls : 'mas-boxes masonary'
32267             }
32268         };
32269         
32270         return cfg;
32271     },
32272     
32273     getChildContainer: function( )
32274     {
32275         if (this.boxesEl) {
32276             return this.boxesEl;
32277         }
32278         
32279         this.boxesEl = this.el.select('.mas-boxes').first();
32280         
32281         return this.boxesEl;
32282     },
32283     
32284     
32285     initEvents : function()
32286     {
32287         var _this = this;
32288         
32289         if(this.isAutoInitial){
32290             Roo.log('hook children rendered');
32291             this.on('childrenrendered', function() {
32292                 Roo.log('children rendered');
32293                 _this.initial();
32294             } ,this);
32295         }
32296         
32297     },
32298     
32299     initial : function()
32300     {
32301         this.reloadItems();
32302
32303         this.currentSize = this.el.getBox(true);
32304
32305         /// was window resize... - let's see if this works..
32306         Roo.EventManager.onWindowResize(this.resize, this); 
32307
32308         if(!this.isAutoInitial){
32309             this.layout();
32310             return;
32311         }
32312         
32313         this.layout.defer(500,this);
32314     },
32315     
32316     reloadItems: function()
32317     {
32318         this.bricks = this.el.select('.masonry-brick', true);
32319         
32320         this.bricks.each(function(b) {
32321             //Roo.log(b.getSize());
32322             if (!b.attr('originalwidth')) {
32323                 b.attr('originalwidth',  b.getSize().width);
32324             }
32325             
32326         });
32327         
32328         Roo.log(this.bricks.elements.length);
32329     },
32330     
32331     resize : function()
32332     {
32333         Roo.log('resize');
32334         var cs = this.el.getBox(true);
32335         
32336         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32337             Roo.log("no change in with or X");
32338             return;
32339         }
32340         this.currentSize = cs;
32341         this.layout();
32342     },
32343     
32344     layout : function()
32345     {
32346          Roo.log('layout');
32347         this._resetLayout();
32348         //this._manageStamps();
32349       
32350         // don't animate first layout
32351         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32352         this.layoutItems( isInstant );
32353       
32354         // flag for initalized
32355         this._isLayoutInited = true;
32356     },
32357     
32358     layoutItems : function( isInstant )
32359     {
32360         //var items = this._getItemsForLayout( this.items );
32361         // original code supports filtering layout items.. we just ignore it..
32362         
32363         this._layoutItems( this.bricks , isInstant );
32364       
32365         this._postLayout();
32366     },
32367     _layoutItems : function ( items , isInstant)
32368     {
32369        //this.fireEvent( 'layout', this, items );
32370     
32371
32372         if ( !items || !items.elements.length ) {
32373           // no items, emit event with empty array
32374             return;
32375         }
32376
32377         var queue = [];
32378         items.each(function(item) {
32379             Roo.log("layout item");
32380             Roo.log(item);
32381             // get x/y object from method
32382             var position = this._getItemLayoutPosition( item );
32383             // enqueue
32384             position.item = item;
32385             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32386             queue.push( position );
32387         }, this);
32388       
32389         this._processLayoutQueue( queue );
32390     },
32391     /** Sets position of item in DOM
32392     * @param {Element} item
32393     * @param {Number} x - horizontal position
32394     * @param {Number} y - vertical position
32395     * @param {Boolean} isInstant - disables transitions
32396     */
32397     _processLayoutQueue : function( queue )
32398     {
32399         for ( var i=0, len = queue.length; i < len; i++ ) {
32400             var obj = queue[i];
32401             obj.item.position('absolute');
32402             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32403         }
32404     },
32405       
32406     
32407     /**
32408     * Any logic you want to do after each layout,
32409     * i.e. size the container
32410     */
32411     _postLayout : function()
32412     {
32413         this.resizeContainer();
32414     },
32415     
32416     resizeContainer : function()
32417     {
32418         if ( !this.isResizingContainer ) {
32419             return;
32420         }
32421         var size = this._getContainerSize();
32422         if ( size ) {
32423             this.el.setSize(size.width,size.height);
32424             this.boxesEl.setSize(size.width,size.height);
32425         }
32426     },
32427     
32428     
32429     
32430     _resetLayout : function()
32431     {
32432         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32433         this.colWidth = this.el.getWidth();
32434         //this.gutter = this.el.getWidth(); 
32435         
32436         this.measureColumns();
32437
32438         // reset column Y
32439         var i = this.cols;
32440         this.colYs = [];
32441         while (i--) {
32442             this.colYs.push( 0 );
32443         }
32444     
32445         this.maxY = 0;
32446     },
32447
32448     measureColumns : function()
32449     {
32450         this.getContainerWidth();
32451       // if columnWidth is 0, default to outerWidth of first item
32452         if ( !this.columnWidth ) {
32453             var firstItem = this.bricks.first();
32454             Roo.log(firstItem);
32455             this.columnWidth  = this.containerWidth;
32456             if (firstItem && firstItem.attr('originalwidth') ) {
32457                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32458             }
32459             // columnWidth fall back to item of first element
32460             Roo.log("set column width?");
32461                         this.initialColumnWidth = this.columnWidth  ;
32462
32463             // if first elem has no width, default to size of container
32464             
32465         }
32466         
32467         
32468         if (this.initialColumnWidth) {
32469             this.columnWidth = this.initialColumnWidth;
32470         }
32471         
32472         
32473             
32474         // column width is fixed at the top - however if container width get's smaller we should
32475         // reduce it...
32476         
32477         // this bit calcs how man columns..
32478             
32479         var columnWidth = this.columnWidth += this.gutter;
32480       
32481         // calculate columns
32482         var containerWidth = this.containerWidth + this.gutter;
32483         
32484         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32485         // fix rounding errors, typically with gutters
32486         var excess = columnWidth - containerWidth % columnWidth;
32487         
32488         
32489         // if overshoot is less than a pixel, round up, otherwise floor it
32490         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32491         cols = Math[ mathMethod ]( cols );
32492         this.cols = Math.max( cols, 1 );
32493         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32494         
32495          // padding positioning..
32496         var totalColWidth = this.cols * this.columnWidth;
32497         var padavail = this.containerWidth - totalColWidth;
32498         // so for 2 columns - we need 3 'pads'
32499         
32500         var padNeeded = (1+this.cols) * this.padWidth;
32501         
32502         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32503         
32504         this.columnWidth += padExtra
32505         //this.padWidth = Math.floor(padavail /  ( this.cols));
32506         
32507         // adjust colum width so that padding is fixed??
32508         
32509         // we have 3 columns ... total = width * 3
32510         // we have X left over... that should be used by 
32511         
32512         //if (this.expandC) {
32513             
32514         //}
32515         
32516         
32517         
32518     },
32519     
32520     getContainerWidth : function()
32521     {
32522        /* // container is parent if fit width
32523         var container = this.isFitWidth ? this.element.parentNode : this.element;
32524         // check that this.size and size are there
32525         // IE8 triggers resize on body size change, so they might not be
32526         
32527         var size = getSize( container );  //FIXME
32528         this.containerWidth = size && size.innerWidth; //FIXME
32529         */
32530          
32531         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32532         
32533     },
32534     
32535     _getItemLayoutPosition : function( item )  // what is item?
32536     {
32537         // we resize the item to our columnWidth..
32538       
32539         item.setWidth(this.columnWidth);
32540         item.autoBoxAdjust  = false;
32541         
32542         var sz = item.getSize();
32543  
32544         // how many columns does this brick span
32545         var remainder = this.containerWidth % this.columnWidth;
32546         
32547         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32548         // round if off by 1 pixel, otherwise use ceil
32549         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32550         colSpan = Math.min( colSpan, this.cols );
32551         
32552         // normally this should be '1' as we dont' currently allow multi width columns..
32553         
32554         var colGroup = this._getColGroup( colSpan );
32555         // get the minimum Y value from the columns
32556         var minimumY = Math.min.apply( Math, colGroup );
32557         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32558         
32559         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32560          
32561         // position the brick
32562         var position = {
32563             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32564             y: this.currentSize.y + minimumY + this.padHeight
32565         };
32566         
32567         Roo.log(position);
32568         // apply setHeight to necessary columns
32569         var setHeight = minimumY + sz.height + this.padHeight;
32570         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32571         
32572         var setSpan = this.cols + 1 - colGroup.length;
32573         for ( var i = 0; i < setSpan; i++ ) {
32574           this.colYs[ shortColIndex + i ] = setHeight ;
32575         }
32576       
32577         return position;
32578     },
32579     
32580     /**
32581      * @param {Number} colSpan - number of columns the element spans
32582      * @returns {Array} colGroup
32583      */
32584     _getColGroup : function( colSpan )
32585     {
32586         if ( colSpan < 2 ) {
32587           // if brick spans only one column, use all the column Ys
32588           return this.colYs;
32589         }
32590       
32591         var colGroup = [];
32592         // how many different places could this brick fit horizontally
32593         var groupCount = this.cols + 1 - colSpan;
32594         // for each group potential horizontal position
32595         for ( var i = 0; i < groupCount; i++ ) {
32596           // make an array of colY values for that one group
32597           var groupColYs = this.colYs.slice( i, i + colSpan );
32598           // and get the max value of the array
32599           colGroup[i] = Math.max.apply( Math, groupColYs );
32600         }
32601         return colGroup;
32602     },
32603     /*
32604     _manageStamp : function( stamp )
32605     {
32606         var stampSize =  stamp.getSize();
32607         var offset = stamp.getBox();
32608         // get the columns that this stamp affects
32609         var firstX = this.isOriginLeft ? offset.x : offset.right;
32610         var lastX = firstX + stampSize.width;
32611         var firstCol = Math.floor( firstX / this.columnWidth );
32612         firstCol = Math.max( 0, firstCol );
32613         
32614         var lastCol = Math.floor( lastX / this.columnWidth );
32615         // lastCol should not go over if multiple of columnWidth #425
32616         lastCol -= lastX % this.columnWidth ? 0 : 1;
32617         lastCol = Math.min( this.cols - 1, lastCol );
32618         
32619         // set colYs to bottom of the stamp
32620         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32621             stampSize.height;
32622             
32623         for ( var i = firstCol; i <= lastCol; i++ ) {
32624           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32625         }
32626     },
32627     */
32628     
32629     _getContainerSize : function()
32630     {
32631         this.maxY = Math.max.apply( Math, this.colYs );
32632         var size = {
32633             height: this.maxY
32634         };
32635       
32636         if ( this.isFitWidth ) {
32637             size.width = this._getContainerFitWidth();
32638         }
32639       
32640         return size;
32641     },
32642     
32643     _getContainerFitWidth : function()
32644     {
32645         var unusedCols = 0;
32646         // count unused columns
32647         var i = this.cols;
32648         while ( --i ) {
32649           if ( this.colYs[i] !== 0 ) {
32650             break;
32651           }
32652           unusedCols++;
32653         }
32654         // fit container to columns that have been used
32655         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32656     },
32657     
32658     needsResizeLayout : function()
32659     {
32660         var previousWidth = this.containerWidth;
32661         this.getContainerWidth();
32662         return previousWidth !== this.containerWidth;
32663     }
32664  
32665 });
32666
32667  
32668
32669  /*
32670  * - LGPL
32671  *
32672  * element
32673  * 
32674  */
32675
32676 /**
32677  * @class Roo.bootstrap.MasonryBrick
32678  * @extends Roo.bootstrap.Component
32679  * Bootstrap MasonryBrick class
32680  * 
32681  * @constructor
32682  * Create a new MasonryBrick
32683  * @param {Object} config The config object
32684  */
32685
32686 Roo.bootstrap.MasonryBrick = function(config){
32687     
32688     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32689     
32690     Roo.bootstrap.MasonryBrick.register(this);
32691     
32692     this.addEvents({
32693         // raw events
32694         /**
32695          * @event click
32696          * When a MasonryBrick is clcik
32697          * @param {Roo.bootstrap.MasonryBrick} this
32698          * @param {Roo.EventObject} e
32699          */
32700         "click" : true
32701     });
32702 };
32703
32704 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32705     
32706     /**
32707      * @cfg {String} title
32708      */   
32709     title : '',
32710     /**
32711      * @cfg {String} html
32712      */   
32713     html : '',
32714     /**
32715      * @cfg {String} bgimage
32716      */   
32717     bgimage : '',
32718     /**
32719      * @cfg {String} videourl
32720      */   
32721     videourl : '',
32722     /**
32723      * @cfg {String} cls
32724      */   
32725     cls : '',
32726     /**
32727      * @cfg {String} href
32728      */   
32729     href : '',
32730     /**
32731      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32732      */   
32733     size : 'xs',
32734     
32735     /**
32736      * @cfg {String} placetitle (center|bottom)
32737      */   
32738     placetitle : '',
32739     
32740     /**
32741      * @cfg {Boolean} isFitContainer defalut true
32742      */   
32743     isFitContainer : true, 
32744     
32745     /**
32746      * @cfg {Boolean} preventDefault defalut false
32747      */   
32748     preventDefault : false, 
32749     
32750     /**
32751      * @cfg {Boolean} inverse defalut false
32752      */   
32753     maskInverse : false, 
32754     
32755     getAutoCreate : function()
32756     {
32757         if(!this.isFitContainer){
32758             return this.getSplitAutoCreate();
32759         }
32760         
32761         var cls = 'masonry-brick masonry-brick-full';
32762         
32763         if(this.href.length){
32764             cls += ' masonry-brick-link';
32765         }
32766         
32767         if(this.bgimage.length){
32768             cls += ' masonry-brick-image';
32769         }
32770         
32771         if(this.maskInverse){
32772             cls += ' mask-inverse';
32773         }
32774         
32775         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32776             cls += ' enable-mask';
32777         }
32778         
32779         if(this.size){
32780             cls += ' masonry-' + this.size + '-brick';
32781         }
32782         
32783         if(this.placetitle.length){
32784             
32785             switch (this.placetitle) {
32786                 case 'center' :
32787                     cls += ' masonry-center-title';
32788                     break;
32789                 case 'bottom' :
32790                     cls += ' masonry-bottom-title';
32791                     break;
32792                 default:
32793                     break;
32794             }
32795             
32796         } else {
32797             if(!this.html.length && !this.bgimage.length){
32798                 cls += ' masonry-center-title';
32799             }
32800
32801             if(!this.html.length && this.bgimage.length){
32802                 cls += ' masonry-bottom-title';
32803             }
32804         }
32805         
32806         if(this.cls){
32807             cls += ' ' + this.cls;
32808         }
32809         
32810         var cfg = {
32811             tag: (this.href.length) ? 'a' : 'div',
32812             cls: cls,
32813             cn: [
32814                 {
32815                     tag: 'div',
32816                     cls: 'masonry-brick-mask'
32817                 },
32818                 {
32819                     tag: 'div',
32820                     cls: 'masonry-brick-paragraph',
32821                     cn: []
32822                 }
32823             ]
32824         };
32825         
32826         if(this.href.length){
32827             cfg.href = this.href;
32828         }
32829         
32830         var cn = cfg.cn[1].cn;
32831         
32832         if(this.title.length){
32833             cn.push({
32834                 tag: 'h4',
32835                 cls: 'masonry-brick-title',
32836                 html: this.title
32837             });
32838         }
32839         
32840         if(this.html.length){
32841             cn.push({
32842                 tag: 'p',
32843                 cls: 'masonry-brick-text',
32844                 html: this.html
32845             });
32846         }
32847         
32848         if (!this.title.length && !this.html.length) {
32849             cfg.cn[1].cls += ' hide';
32850         }
32851         
32852         if(this.bgimage.length){
32853             cfg.cn.push({
32854                 tag: 'img',
32855                 cls: 'masonry-brick-image-view',
32856                 src: this.bgimage
32857             });
32858         }
32859         
32860         if(this.videourl.length){
32861             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32862             // youtube support only?
32863             cfg.cn.push({
32864                 tag: 'iframe',
32865                 cls: 'masonry-brick-image-view',
32866                 src: vurl,
32867                 frameborder : 0,
32868                 allowfullscreen : true
32869             });
32870         }
32871         
32872         return cfg;
32873         
32874     },
32875     
32876     getSplitAutoCreate : function()
32877     {
32878         var cls = 'masonry-brick masonry-brick-split';
32879         
32880         if(this.href.length){
32881             cls += ' masonry-brick-link';
32882         }
32883         
32884         if(this.bgimage.length){
32885             cls += ' masonry-brick-image';
32886         }
32887         
32888         if(this.size){
32889             cls += ' masonry-' + this.size + '-brick';
32890         }
32891         
32892         switch (this.placetitle) {
32893             case 'center' :
32894                 cls += ' masonry-center-title';
32895                 break;
32896             case 'bottom' :
32897                 cls += ' masonry-bottom-title';
32898                 break;
32899             default:
32900                 if(!this.bgimage.length){
32901                     cls += ' masonry-center-title';
32902                 }
32903
32904                 if(this.bgimage.length){
32905                     cls += ' masonry-bottom-title';
32906                 }
32907                 break;
32908         }
32909         
32910         if(this.cls){
32911             cls += ' ' + this.cls;
32912         }
32913         
32914         var cfg = {
32915             tag: (this.href.length) ? 'a' : 'div',
32916             cls: cls,
32917             cn: [
32918                 {
32919                     tag: 'div',
32920                     cls: 'masonry-brick-split-head',
32921                     cn: [
32922                         {
32923                             tag: 'div',
32924                             cls: 'masonry-brick-paragraph',
32925                             cn: []
32926                         }
32927                     ]
32928                 },
32929                 {
32930                     tag: 'div',
32931                     cls: 'masonry-brick-split-body',
32932                     cn: []
32933                 }
32934             ]
32935         };
32936         
32937         if(this.href.length){
32938             cfg.href = this.href;
32939         }
32940         
32941         if(this.title.length){
32942             cfg.cn[0].cn[0].cn.push({
32943                 tag: 'h4',
32944                 cls: 'masonry-brick-title',
32945                 html: this.title
32946             });
32947         }
32948         
32949         if(this.html.length){
32950             cfg.cn[1].cn.push({
32951                 tag: 'p',
32952                 cls: 'masonry-brick-text',
32953                 html: this.html
32954             });
32955         }
32956
32957         if(this.bgimage.length){
32958             cfg.cn[0].cn.push({
32959                 tag: 'img',
32960                 cls: 'masonry-brick-image-view',
32961                 src: this.bgimage
32962             });
32963         }
32964         
32965         if(this.videourl.length){
32966             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32967             // youtube support only?
32968             cfg.cn[0].cn.cn.push({
32969                 tag: 'iframe',
32970                 cls: 'masonry-brick-image-view',
32971                 src: vurl,
32972                 frameborder : 0,
32973                 allowfullscreen : true
32974             });
32975         }
32976         
32977         return cfg;
32978     },
32979     
32980     initEvents: function() 
32981     {
32982         switch (this.size) {
32983             case 'xs' :
32984                 this.x = 1;
32985                 this.y = 1;
32986                 break;
32987             case 'sm' :
32988                 this.x = 2;
32989                 this.y = 2;
32990                 break;
32991             case 'md' :
32992             case 'md-left' :
32993             case 'md-right' :
32994                 this.x = 3;
32995                 this.y = 3;
32996                 break;
32997             case 'tall' :
32998                 this.x = 2;
32999                 this.y = 3;
33000                 break;
33001             case 'wide' :
33002                 this.x = 3;
33003                 this.y = 2;
33004                 break;
33005             case 'wide-thin' :
33006                 this.x = 3;
33007                 this.y = 1;
33008                 break;
33009                         
33010             default :
33011                 break;
33012         }
33013         
33014         if(Roo.isTouch){
33015             this.el.on('touchstart', this.onTouchStart, this);
33016             this.el.on('touchmove', this.onTouchMove, this);
33017             this.el.on('touchend', this.onTouchEnd, this);
33018             this.el.on('contextmenu', this.onContextMenu, this);
33019         } else {
33020             this.el.on('mouseenter'  ,this.enter, this);
33021             this.el.on('mouseleave', this.leave, this);
33022             this.el.on('click', this.onClick, this);
33023         }
33024         
33025         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33026             this.parent().bricks.push(this);   
33027         }
33028         
33029     },
33030     
33031     onClick: function(e, el)
33032     {
33033         var time = this.endTimer - this.startTimer;
33034         // Roo.log(e.preventDefault());
33035         if(Roo.isTouch){
33036             if(time > 1000){
33037                 e.preventDefault();
33038                 return;
33039             }
33040         }
33041         
33042         if(!this.preventDefault){
33043             return;
33044         }
33045         
33046         e.preventDefault();
33047         
33048         if (this.activeClass != '') {
33049             this.selectBrick();
33050         }
33051         
33052         this.fireEvent('click', this, e);
33053     },
33054     
33055     enter: function(e, el)
33056     {
33057         e.preventDefault();
33058         
33059         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33060             return;
33061         }
33062         
33063         if(this.bgimage.length && this.html.length){
33064             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33065         }
33066     },
33067     
33068     leave: function(e, el)
33069     {
33070         e.preventDefault();
33071         
33072         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33073             return;
33074         }
33075         
33076         if(this.bgimage.length && this.html.length){
33077             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33078         }
33079     },
33080     
33081     onTouchStart: function(e, el)
33082     {
33083 //        e.preventDefault();
33084         
33085         this.touchmoved = false;
33086         
33087         if(!this.isFitContainer){
33088             return;
33089         }
33090         
33091         if(!this.bgimage.length || !this.html.length){
33092             return;
33093         }
33094         
33095         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33096         
33097         this.timer = new Date().getTime();
33098         
33099     },
33100     
33101     onTouchMove: function(e, el)
33102     {
33103         this.touchmoved = true;
33104     },
33105     
33106     onContextMenu : function(e,el)
33107     {
33108         e.preventDefault();
33109         e.stopPropagation();
33110         return false;
33111     },
33112     
33113     onTouchEnd: function(e, el)
33114     {
33115 //        e.preventDefault();
33116         
33117         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33118         
33119             this.leave(e,el);
33120             
33121             return;
33122         }
33123         
33124         if(!this.bgimage.length || !this.html.length){
33125             
33126             if(this.href.length){
33127                 window.location.href = this.href;
33128             }
33129             
33130             return;
33131         }
33132         
33133         if(!this.isFitContainer){
33134             return;
33135         }
33136         
33137         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33138         
33139         window.location.href = this.href;
33140     },
33141     
33142     //selection on single brick only
33143     selectBrick : function() {
33144         
33145         if (!this.parentId) {
33146             return;
33147         }
33148         
33149         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33150         var index = m.selectedBrick.indexOf(this.id);
33151         
33152         if ( index > -1) {
33153             m.selectedBrick.splice(index,1);
33154             this.el.removeClass(this.activeClass);
33155             return;
33156         }
33157         
33158         for(var i = 0; i < m.selectedBrick.length; i++) {
33159             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33160             b.el.removeClass(b.activeClass);
33161         }
33162         
33163         m.selectedBrick = [];
33164         
33165         m.selectedBrick.push(this.id);
33166         this.el.addClass(this.activeClass);
33167         return;
33168     },
33169     
33170     isSelected : function(){
33171         return this.el.hasClass(this.activeClass);
33172         
33173     }
33174 });
33175
33176 Roo.apply(Roo.bootstrap.MasonryBrick, {
33177     
33178     //groups: {},
33179     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33180      /**
33181     * register a Masonry Brick
33182     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33183     */
33184     
33185     register : function(brick)
33186     {
33187         //this.groups[brick.id] = brick;
33188         this.groups.add(brick.id, brick);
33189     },
33190     /**
33191     * fetch a  masonry brick based on the masonry brick ID
33192     * @param {string} the masonry brick to add
33193     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33194     */
33195     
33196     get: function(brick_id) 
33197     {
33198         // if (typeof(this.groups[brick_id]) == 'undefined') {
33199         //     return false;
33200         // }
33201         // return this.groups[brick_id] ;
33202         
33203         if(this.groups.key(brick_id)) {
33204             return this.groups.key(brick_id);
33205         }
33206         
33207         return false;
33208     }
33209     
33210     
33211     
33212 });
33213
33214  /*
33215  * - LGPL
33216  *
33217  * element
33218  * 
33219  */
33220
33221 /**
33222  * @class Roo.bootstrap.Brick
33223  * @extends Roo.bootstrap.Component
33224  * Bootstrap Brick class
33225  * 
33226  * @constructor
33227  * Create a new Brick
33228  * @param {Object} config The config object
33229  */
33230
33231 Roo.bootstrap.Brick = function(config){
33232     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33233     
33234     this.addEvents({
33235         // raw events
33236         /**
33237          * @event click
33238          * When a Brick is click
33239          * @param {Roo.bootstrap.Brick} this
33240          * @param {Roo.EventObject} e
33241          */
33242         "click" : true
33243     });
33244 };
33245
33246 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33247     
33248     /**
33249      * @cfg {String} title
33250      */   
33251     title : '',
33252     /**
33253      * @cfg {String} html
33254      */   
33255     html : '',
33256     /**
33257      * @cfg {String} bgimage
33258      */   
33259     bgimage : '',
33260     /**
33261      * @cfg {String} cls
33262      */   
33263     cls : '',
33264     /**
33265      * @cfg {String} href
33266      */   
33267     href : '',
33268     /**
33269      * @cfg {String} video
33270      */   
33271     video : '',
33272     /**
33273      * @cfg {Boolean} square
33274      */   
33275     square : true,
33276     
33277     getAutoCreate : function()
33278     {
33279         var cls = 'roo-brick';
33280         
33281         if(this.href.length){
33282             cls += ' roo-brick-link';
33283         }
33284         
33285         if(this.bgimage.length){
33286             cls += ' roo-brick-image';
33287         }
33288         
33289         if(!this.html.length && !this.bgimage.length){
33290             cls += ' roo-brick-center-title';
33291         }
33292         
33293         if(!this.html.length && this.bgimage.length){
33294             cls += ' roo-brick-bottom-title';
33295         }
33296         
33297         if(this.cls){
33298             cls += ' ' + this.cls;
33299         }
33300         
33301         var cfg = {
33302             tag: (this.href.length) ? 'a' : 'div',
33303             cls: cls,
33304             cn: [
33305                 {
33306                     tag: 'div',
33307                     cls: 'roo-brick-paragraph',
33308                     cn: []
33309                 }
33310             ]
33311         };
33312         
33313         if(this.href.length){
33314             cfg.href = this.href;
33315         }
33316         
33317         var cn = cfg.cn[0].cn;
33318         
33319         if(this.title.length){
33320             cn.push({
33321                 tag: 'h4',
33322                 cls: 'roo-brick-title',
33323                 html: this.title
33324             });
33325         }
33326         
33327         if(this.html.length){
33328             cn.push({
33329                 tag: 'p',
33330                 cls: 'roo-brick-text',
33331                 html: this.html
33332             });
33333         } else {
33334             cn.cls += ' hide';
33335         }
33336         
33337         if(this.bgimage.length){
33338             cfg.cn.push({
33339                 tag: 'img',
33340                 cls: 'roo-brick-image-view',
33341                 src: this.bgimage
33342             });
33343         }
33344         
33345         return cfg;
33346     },
33347     
33348     initEvents: function() 
33349     {
33350         if(this.title.length || this.html.length){
33351             this.el.on('mouseenter'  ,this.enter, this);
33352             this.el.on('mouseleave', this.leave, this);
33353         }
33354         
33355         Roo.EventManager.onWindowResize(this.resize, this); 
33356         
33357         if(this.bgimage.length){
33358             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33359             this.imageEl.on('load', this.onImageLoad, this);
33360             return;
33361         }
33362         
33363         this.resize();
33364     },
33365     
33366     onImageLoad : function()
33367     {
33368         this.resize();
33369     },
33370     
33371     resize : function()
33372     {
33373         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33374         
33375         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33376         
33377         if(this.bgimage.length){
33378             var image = this.el.select('.roo-brick-image-view', true).first();
33379             
33380             image.setWidth(paragraph.getWidth());
33381             
33382             if(this.square){
33383                 image.setHeight(paragraph.getWidth());
33384             }
33385             
33386             this.el.setHeight(image.getHeight());
33387             paragraph.setHeight(image.getHeight());
33388             
33389         }
33390         
33391     },
33392     
33393     enter: function(e, el)
33394     {
33395         e.preventDefault();
33396         
33397         if(this.bgimage.length){
33398             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33399             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33400         }
33401     },
33402     
33403     leave: function(e, el)
33404     {
33405         e.preventDefault();
33406         
33407         if(this.bgimage.length){
33408             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33409             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33410         }
33411     }
33412     
33413 });
33414
33415  
33416
33417  /*
33418  * - LGPL
33419  *
33420  * Number field 
33421  */
33422
33423 /**
33424  * @class Roo.bootstrap.NumberField
33425  * @extends Roo.bootstrap.Input
33426  * Bootstrap NumberField class
33427  * 
33428  * 
33429  * 
33430  * 
33431  * @constructor
33432  * Create a new NumberField
33433  * @param {Object} config The config object
33434  */
33435
33436 Roo.bootstrap.NumberField = function(config){
33437     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33438 };
33439
33440 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33441     
33442     /**
33443      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33444      */
33445     allowDecimals : true,
33446     /**
33447      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33448      */
33449     decimalSeparator : ".",
33450     /**
33451      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33452      */
33453     decimalPrecision : 2,
33454     /**
33455      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33456      */
33457     allowNegative : true,
33458     
33459     /**
33460      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33461      */
33462     allowZero: true,
33463     /**
33464      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33465      */
33466     minValue : Number.NEGATIVE_INFINITY,
33467     /**
33468      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33469      */
33470     maxValue : Number.MAX_VALUE,
33471     /**
33472      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33473      */
33474     minText : "The minimum value for this field is {0}",
33475     /**
33476      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33477      */
33478     maxText : "The maximum value for this field is {0}",
33479     /**
33480      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33481      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33482      */
33483     nanText : "{0} is not a valid number",
33484     /**
33485      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33486      */
33487     thousandsDelimiter : false,
33488     /**
33489      * @cfg {String} valueAlign alignment of value
33490      */
33491     valueAlign : "left",
33492
33493     getAutoCreate : function()
33494     {
33495         var hiddenInput = {
33496             tag: 'input',
33497             type: 'hidden',
33498             id: Roo.id(),
33499             cls: 'hidden-number-input'
33500         };
33501         
33502         if (this.name) {
33503             hiddenInput.name = this.name;
33504         }
33505         
33506         this.name = '';
33507         
33508         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33509         
33510         this.name = hiddenInput.name;
33511         
33512         if(cfg.cn.length > 0) {
33513             cfg.cn.push(hiddenInput);
33514         }
33515         
33516         return cfg;
33517     },
33518
33519     // private
33520     initEvents : function()
33521     {   
33522         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33523         
33524         var allowed = "0123456789";
33525         
33526         if(this.allowDecimals){
33527             allowed += this.decimalSeparator;
33528         }
33529         
33530         if(this.allowNegative){
33531             allowed += "-";
33532         }
33533         
33534         if(this.thousandsDelimiter) {
33535             allowed += ",";
33536         }
33537         
33538         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33539         
33540         var keyPress = function(e){
33541             
33542             var k = e.getKey();
33543             
33544             var c = e.getCharCode();
33545             
33546             if(
33547                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33548                     allowed.indexOf(String.fromCharCode(c)) === -1
33549             ){
33550                 e.stopEvent();
33551                 return;
33552             }
33553             
33554             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33555                 return;
33556             }
33557             
33558             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33559                 e.stopEvent();
33560             }
33561         };
33562         
33563         this.el.on("keypress", keyPress, this);
33564     },
33565     
33566     validateValue : function(value)
33567     {
33568         
33569         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33570             return false;
33571         }
33572         
33573         var num = this.parseValue(value);
33574         
33575         if(isNaN(num)){
33576             this.markInvalid(String.format(this.nanText, value));
33577             return false;
33578         }
33579         
33580         if(num < this.minValue){
33581             this.markInvalid(String.format(this.minText, this.minValue));
33582             return false;
33583         }
33584         
33585         if(num > this.maxValue){
33586             this.markInvalid(String.format(this.maxText, this.maxValue));
33587             return false;
33588         }
33589         
33590         return true;
33591     },
33592
33593     getValue : function()
33594     {
33595         var v = this.hiddenEl().getValue();
33596         
33597         return this.fixPrecision(this.parseValue(v));
33598     },
33599
33600     parseValue : function(value)
33601     {
33602         if(this.thousandsDelimiter) {
33603             value += "";
33604             r = new RegExp(",", "g");
33605             value = value.replace(r, "");
33606         }
33607         
33608         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33609         return isNaN(value) ? '' : value;
33610     },
33611
33612     fixPrecision : function(value)
33613     {
33614         if(this.thousandsDelimiter) {
33615             value += "";
33616             r = new RegExp(",", "g");
33617             value = value.replace(r, "");
33618         }
33619         
33620         var nan = isNaN(value);
33621         
33622         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33623             return nan ? '' : value;
33624         }
33625         return parseFloat(value).toFixed(this.decimalPrecision);
33626     },
33627
33628     setValue : function(v)
33629     {
33630         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33631         
33632         this.value = v;
33633         
33634         if(this.rendered){
33635             
33636             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33637             
33638             this.inputEl().dom.value = (v == '') ? '' :
33639                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33640             
33641             if(!this.allowZero && v === '0') {
33642                 this.hiddenEl().dom.value = '';
33643                 this.inputEl().dom.value = '';
33644             }
33645             
33646             this.validate();
33647         }
33648     },
33649
33650     decimalPrecisionFcn : function(v)
33651     {
33652         return Math.floor(v);
33653     },
33654
33655     beforeBlur : function()
33656     {
33657         var v = this.parseValue(this.getRawValue());
33658         
33659         if(v || v === 0 || v === ''){
33660             this.setValue(v);
33661         }
33662     },
33663     
33664     hiddenEl : function()
33665     {
33666         return this.el.select('input.hidden-number-input',true).first();
33667     }
33668     
33669 });
33670
33671  
33672
33673 /*
33674 * Licence: LGPL
33675 */
33676
33677 /**
33678  * @class Roo.bootstrap.DocumentSlider
33679  * @extends Roo.bootstrap.Component
33680  * Bootstrap DocumentSlider class
33681  * 
33682  * @constructor
33683  * Create a new DocumentViewer
33684  * @param {Object} config The config object
33685  */
33686
33687 Roo.bootstrap.DocumentSlider = function(config){
33688     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33689     
33690     this.files = [];
33691     
33692     this.addEvents({
33693         /**
33694          * @event initial
33695          * Fire after initEvent
33696          * @param {Roo.bootstrap.DocumentSlider} this
33697          */
33698         "initial" : true,
33699         /**
33700          * @event update
33701          * Fire after update
33702          * @param {Roo.bootstrap.DocumentSlider} this
33703          */
33704         "update" : true,
33705         /**
33706          * @event click
33707          * Fire after click
33708          * @param {Roo.bootstrap.DocumentSlider} this
33709          */
33710         "click" : true
33711     });
33712 };
33713
33714 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33715     
33716     files : false,
33717     
33718     indicator : 0,
33719     
33720     getAutoCreate : function()
33721     {
33722         var cfg = {
33723             tag : 'div',
33724             cls : 'roo-document-slider',
33725             cn : [
33726                 {
33727                     tag : 'div',
33728                     cls : 'roo-document-slider-header',
33729                     cn : [
33730                         {
33731                             tag : 'div',
33732                             cls : 'roo-document-slider-header-title'
33733                         }
33734                     ]
33735                 },
33736                 {
33737                     tag : 'div',
33738                     cls : 'roo-document-slider-body',
33739                     cn : [
33740                         {
33741                             tag : 'div',
33742                             cls : 'roo-document-slider-prev',
33743                             cn : [
33744                                 {
33745                                     tag : 'i',
33746                                     cls : 'fa fa-chevron-left'
33747                                 }
33748                             ]
33749                         },
33750                         {
33751                             tag : 'div',
33752                             cls : 'roo-document-slider-thumb',
33753                             cn : [
33754                                 {
33755                                     tag : 'img',
33756                                     cls : 'roo-document-slider-image'
33757                                 }
33758                             ]
33759                         },
33760                         {
33761                             tag : 'div',
33762                             cls : 'roo-document-slider-next',
33763                             cn : [
33764                                 {
33765                                     tag : 'i',
33766                                     cls : 'fa fa-chevron-right'
33767                                 }
33768                             ]
33769                         }
33770                     ]
33771                 }
33772             ]
33773         };
33774         
33775         return cfg;
33776     },
33777     
33778     initEvents : function()
33779     {
33780         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33781         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33782         
33783         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33784         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33785         
33786         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33787         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33788         
33789         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33790         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33791         
33792         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33793         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33794         
33795         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33796         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33797         
33798         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33799         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33800         
33801         this.thumbEl.on('click', this.onClick, this);
33802         
33803         this.prevIndicator.on('click', this.prev, this);
33804         
33805         this.nextIndicator.on('click', this.next, this);
33806         
33807     },
33808     
33809     initial : function()
33810     {
33811         if(this.files.length){
33812             this.indicator = 1;
33813             this.update()
33814         }
33815         
33816         this.fireEvent('initial', this);
33817     },
33818     
33819     update : function()
33820     {
33821         this.imageEl.attr('src', this.files[this.indicator - 1]);
33822         
33823         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33824         
33825         this.prevIndicator.show();
33826         
33827         if(this.indicator == 1){
33828             this.prevIndicator.hide();
33829         }
33830         
33831         this.nextIndicator.show();
33832         
33833         if(this.indicator == this.files.length){
33834             this.nextIndicator.hide();
33835         }
33836         
33837         this.thumbEl.scrollTo('top');
33838         
33839         this.fireEvent('update', this);
33840     },
33841     
33842     onClick : function(e)
33843     {
33844         e.preventDefault();
33845         
33846         this.fireEvent('click', this);
33847     },
33848     
33849     prev : function(e)
33850     {
33851         e.preventDefault();
33852         
33853         this.indicator = Math.max(1, this.indicator - 1);
33854         
33855         this.update();
33856     },
33857     
33858     next : function(e)
33859     {
33860         e.preventDefault();
33861         
33862         this.indicator = Math.min(this.files.length, this.indicator + 1);
33863         
33864         this.update();
33865     }
33866 });
33867 /*
33868  * - LGPL
33869  *
33870  * RadioSet
33871  *
33872  *
33873  */
33874
33875 /**
33876  * @class Roo.bootstrap.RadioSet
33877  * @extends Roo.bootstrap.Input
33878  * Bootstrap RadioSet class
33879  * @cfg {String} indicatorpos (left|right) default left
33880  * @cfg {Boolean} inline (true|false) inline the element (default true)
33881  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33882  * @constructor
33883  * Create a new RadioSet
33884  * @param {Object} config The config object
33885  */
33886
33887 Roo.bootstrap.RadioSet = function(config){
33888     
33889     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33890     
33891     this.radioes = [];
33892     
33893     Roo.bootstrap.RadioSet.register(this);
33894     
33895     this.addEvents({
33896         /**
33897         * @event check
33898         * Fires when the element is checked or unchecked.
33899         * @param {Roo.bootstrap.RadioSet} this This radio
33900         * @param {Roo.bootstrap.Radio} item The checked item
33901         */
33902        check : true,
33903        /**
33904         * @event click
33905         * Fires when the element is click.
33906         * @param {Roo.bootstrap.RadioSet} this This radio set
33907         * @param {Roo.bootstrap.Radio} item The checked item
33908         * @param {Roo.EventObject} e The event object
33909         */
33910        click : true
33911     });
33912     
33913 };
33914
33915 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33916
33917     radioes : false,
33918     
33919     inline : true,
33920     
33921     weight : '',
33922     
33923     indicatorpos : 'left',
33924     
33925     getAutoCreate : function()
33926     {
33927         var label = {
33928             tag : 'label',
33929             cls : 'roo-radio-set-label',
33930             cn : [
33931                 {
33932                     tag : 'span',
33933                     html : this.fieldLabel
33934                 }
33935             ]
33936         };
33937         
33938         if(this.indicatorpos == 'left'){
33939             label.cn.unshift({
33940                 tag : 'i',
33941                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33942                 tooltip : 'This field is required'
33943             });
33944         } else {
33945             label.cn.push({
33946                 tag : 'i',
33947                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33948                 tooltip : 'This field is required'
33949             });
33950         }
33951         
33952         var items = {
33953             tag : 'div',
33954             cls : 'roo-radio-set-items'
33955         };
33956         
33957         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33958         
33959         if (align === 'left' && this.fieldLabel.length) {
33960             
33961             items = {
33962                 cls : "roo-radio-set-right", 
33963                 cn: [
33964                     items
33965                 ]
33966             };
33967             
33968             if(this.labelWidth > 12){
33969                 label.style = "width: " + this.labelWidth + 'px';
33970             }
33971             
33972             if(this.labelWidth < 13 && this.labelmd == 0){
33973                 this.labelmd = this.labelWidth;
33974             }
33975             
33976             if(this.labellg > 0){
33977                 label.cls += ' col-lg-' + this.labellg;
33978                 items.cls += ' col-lg-' + (12 - this.labellg);
33979             }
33980             
33981             if(this.labelmd > 0){
33982                 label.cls += ' col-md-' + this.labelmd;
33983                 items.cls += ' col-md-' + (12 - this.labelmd);
33984             }
33985             
33986             if(this.labelsm > 0){
33987                 label.cls += ' col-sm-' + this.labelsm;
33988                 items.cls += ' col-sm-' + (12 - this.labelsm);
33989             }
33990             
33991             if(this.labelxs > 0){
33992                 label.cls += ' col-xs-' + this.labelxs;
33993                 items.cls += ' col-xs-' + (12 - this.labelxs);
33994             }
33995         }
33996         
33997         var cfg = {
33998             tag : 'div',
33999             cls : 'roo-radio-set',
34000             cn : [
34001                 {
34002                     tag : 'input',
34003                     cls : 'roo-radio-set-input',
34004                     type : 'hidden',
34005                     name : this.name,
34006                     value : this.value ? this.value :  ''
34007                 },
34008                 label,
34009                 items
34010             ]
34011         };
34012         
34013         if(this.weight.length){
34014             cfg.cls += ' roo-radio-' + this.weight;
34015         }
34016         
34017         if(this.inline) {
34018             cfg.cls += ' roo-radio-set-inline';
34019         }
34020         
34021         var settings=this;
34022         ['xs','sm','md','lg'].map(function(size){
34023             if (settings[size]) {
34024                 cfg.cls += ' col-' + size + '-' + settings[size];
34025             }
34026         });
34027         
34028         return cfg;
34029         
34030     },
34031
34032     initEvents : function()
34033     {
34034         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34035         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34036         
34037         if(!this.fieldLabel.length){
34038             this.labelEl.hide();
34039         }
34040         
34041         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34042         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34043         
34044         this.indicator = this.indicatorEl();
34045         
34046         if(this.indicator){
34047             this.indicator.addClass('invisible');
34048         }
34049         
34050         this.originalValue = this.getValue();
34051         
34052     },
34053     
34054     inputEl: function ()
34055     {
34056         return this.el.select('.roo-radio-set-input', true).first();
34057     },
34058     
34059     getChildContainer : function()
34060     {
34061         return this.itemsEl;
34062     },
34063     
34064     register : function(item)
34065     {
34066         this.radioes.push(item);
34067         
34068     },
34069     
34070     validate : function()
34071     {   
34072         if(this.getVisibilityEl().hasClass('hidden')){
34073             return true;
34074         }
34075         
34076         var valid = false;
34077         
34078         Roo.each(this.radioes, function(i){
34079             if(!i.checked){
34080                 return;
34081             }
34082             
34083             valid = true;
34084             return false;
34085         });
34086         
34087         if(this.allowBlank) {
34088             return true;
34089         }
34090         
34091         if(this.disabled || valid){
34092             this.markValid();
34093             return true;
34094         }
34095         
34096         this.markInvalid();
34097         return false;
34098         
34099     },
34100     
34101     markValid : function()
34102     {
34103         if(this.labelEl.isVisible(true)){
34104             this.indicatorEl().removeClass('visible');
34105             this.indicatorEl().addClass('invisible');
34106         }
34107         
34108         this.el.removeClass([this.invalidClass, this.validClass]);
34109         this.el.addClass(this.validClass);
34110         
34111         this.fireEvent('valid', this);
34112     },
34113     
34114     markInvalid : function(msg)
34115     {
34116         if(this.allowBlank || this.disabled){
34117             return;
34118         }
34119         
34120         if(this.labelEl.isVisible(true)){
34121             this.indicatorEl().removeClass('invisible');
34122             this.indicatorEl().addClass('visible');
34123         }
34124         
34125         this.el.removeClass([this.invalidClass, this.validClass]);
34126         this.el.addClass(this.invalidClass);
34127         
34128         this.fireEvent('invalid', this, msg);
34129         
34130     },
34131     
34132     setValue : function(v, suppressEvent)
34133     {   
34134         if(this.value === v){
34135             return;
34136         }
34137         
34138         this.value = v;
34139         
34140         if(this.rendered){
34141             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34142         }
34143         
34144         Roo.each(this.radioes, function(i){
34145             i.checked = false;
34146             i.el.removeClass('checked');
34147         });
34148         
34149         Roo.each(this.radioes, function(i){
34150             
34151             if(i.value === v || i.value.toString() === v.toString()){
34152                 i.checked = true;
34153                 i.el.addClass('checked');
34154                 
34155                 if(suppressEvent !== true){
34156                     this.fireEvent('check', this, i);
34157                 }
34158                 
34159                 return false;
34160             }
34161             
34162         }, this);
34163         
34164         this.validate();
34165     },
34166     
34167     clearInvalid : function(){
34168         
34169         if(!this.el || this.preventMark){
34170             return;
34171         }
34172         
34173         this.el.removeClass([this.invalidClass]);
34174         
34175         this.fireEvent('valid', this);
34176     }
34177     
34178 });
34179
34180 Roo.apply(Roo.bootstrap.RadioSet, {
34181     
34182     groups: {},
34183     
34184     register : function(set)
34185     {
34186         this.groups[set.name] = set;
34187     },
34188     
34189     get: function(name) 
34190     {
34191         if (typeof(this.groups[name]) == 'undefined') {
34192             return false;
34193         }
34194         
34195         return this.groups[name] ;
34196     }
34197     
34198 });
34199 /*
34200  * Based on:
34201  * Ext JS Library 1.1.1
34202  * Copyright(c) 2006-2007, Ext JS, LLC.
34203  *
34204  * Originally Released Under LGPL - original licence link has changed is not relivant.
34205  *
34206  * Fork - LGPL
34207  * <script type="text/javascript">
34208  */
34209
34210
34211 /**
34212  * @class Roo.bootstrap.SplitBar
34213  * @extends Roo.util.Observable
34214  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34215  * <br><br>
34216  * Usage:
34217  * <pre><code>
34218 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34219                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34220 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34221 split.minSize = 100;
34222 split.maxSize = 600;
34223 split.animate = true;
34224 split.on('moved', splitterMoved);
34225 </code></pre>
34226  * @constructor
34227  * Create a new SplitBar
34228  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34229  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34230  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34231  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34232                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34233                         position of the SplitBar).
34234  */
34235 Roo.bootstrap.SplitBar = function(cfg){
34236     
34237     /** @private */
34238     
34239     //{
34240     //  dragElement : elm
34241     //  resizingElement: el,
34242         // optional..
34243     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34244     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34245         // existingProxy ???
34246     //}
34247     
34248     this.el = Roo.get(cfg.dragElement, true);
34249     this.el.dom.unselectable = "on";
34250     /** @private */
34251     this.resizingEl = Roo.get(cfg.resizingElement, true);
34252
34253     /**
34254      * @private
34255      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34256      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34257      * @type Number
34258      */
34259     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34260     
34261     /**
34262      * The minimum size of the resizing element. (Defaults to 0)
34263      * @type Number
34264      */
34265     this.minSize = 0;
34266     
34267     /**
34268      * The maximum size of the resizing element. (Defaults to 2000)
34269      * @type Number
34270      */
34271     this.maxSize = 2000;
34272     
34273     /**
34274      * Whether to animate the transition to the new size
34275      * @type Boolean
34276      */
34277     this.animate = false;
34278     
34279     /**
34280      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34281      * @type Boolean
34282      */
34283     this.useShim = false;
34284     
34285     /** @private */
34286     this.shim = null;
34287     
34288     if(!cfg.existingProxy){
34289         /** @private */
34290         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34291     }else{
34292         this.proxy = Roo.get(cfg.existingProxy).dom;
34293     }
34294     /** @private */
34295     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34296     
34297     /** @private */
34298     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34299     
34300     /** @private */
34301     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34302     
34303     /** @private */
34304     this.dragSpecs = {};
34305     
34306     /**
34307      * @private The adapter to use to positon and resize elements
34308      */
34309     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34310     this.adapter.init(this);
34311     
34312     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34313         /** @private */
34314         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34315         this.el.addClass("roo-splitbar-h");
34316     }else{
34317         /** @private */
34318         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34319         this.el.addClass("roo-splitbar-v");
34320     }
34321     
34322     this.addEvents({
34323         /**
34324          * @event resize
34325          * Fires when the splitter is moved (alias for {@link #event-moved})
34326          * @param {Roo.bootstrap.SplitBar} this
34327          * @param {Number} newSize the new width or height
34328          */
34329         "resize" : true,
34330         /**
34331          * @event moved
34332          * Fires when the splitter is moved
34333          * @param {Roo.bootstrap.SplitBar} this
34334          * @param {Number} newSize the new width or height
34335          */
34336         "moved" : true,
34337         /**
34338          * @event beforeresize
34339          * Fires before the splitter is dragged
34340          * @param {Roo.bootstrap.SplitBar} this
34341          */
34342         "beforeresize" : true,
34343
34344         "beforeapply" : true
34345     });
34346
34347     Roo.util.Observable.call(this);
34348 };
34349
34350 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34351     onStartProxyDrag : function(x, y){
34352         this.fireEvent("beforeresize", this);
34353         if(!this.overlay){
34354             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34355             o.unselectable();
34356             o.enableDisplayMode("block");
34357             // all splitbars share the same overlay
34358             Roo.bootstrap.SplitBar.prototype.overlay = o;
34359         }
34360         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34361         this.overlay.show();
34362         Roo.get(this.proxy).setDisplayed("block");
34363         var size = this.adapter.getElementSize(this);
34364         this.activeMinSize = this.getMinimumSize();;
34365         this.activeMaxSize = this.getMaximumSize();;
34366         var c1 = size - this.activeMinSize;
34367         var c2 = Math.max(this.activeMaxSize - size, 0);
34368         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34369             this.dd.resetConstraints();
34370             this.dd.setXConstraint(
34371                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34372                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34373             );
34374             this.dd.setYConstraint(0, 0);
34375         }else{
34376             this.dd.resetConstraints();
34377             this.dd.setXConstraint(0, 0);
34378             this.dd.setYConstraint(
34379                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34380                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34381             );
34382          }
34383         this.dragSpecs.startSize = size;
34384         this.dragSpecs.startPoint = [x, y];
34385         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34386     },
34387     
34388     /** 
34389      * @private Called after the drag operation by the DDProxy
34390      */
34391     onEndProxyDrag : function(e){
34392         Roo.get(this.proxy).setDisplayed(false);
34393         var endPoint = Roo.lib.Event.getXY(e);
34394         if(this.overlay){
34395             this.overlay.hide();
34396         }
34397         var newSize;
34398         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34399             newSize = this.dragSpecs.startSize + 
34400                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34401                     endPoint[0] - this.dragSpecs.startPoint[0] :
34402                     this.dragSpecs.startPoint[0] - endPoint[0]
34403                 );
34404         }else{
34405             newSize = this.dragSpecs.startSize + 
34406                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34407                     endPoint[1] - this.dragSpecs.startPoint[1] :
34408                     this.dragSpecs.startPoint[1] - endPoint[1]
34409                 );
34410         }
34411         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34412         if(newSize != this.dragSpecs.startSize){
34413             if(this.fireEvent('beforeapply', this, newSize) !== false){
34414                 this.adapter.setElementSize(this, newSize);
34415                 this.fireEvent("moved", this, newSize);
34416                 this.fireEvent("resize", this, newSize);
34417             }
34418         }
34419     },
34420     
34421     /**
34422      * Get the adapter this SplitBar uses
34423      * @return The adapter object
34424      */
34425     getAdapter : function(){
34426         return this.adapter;
34427     },
34428     
34429     /**
34430      * Set the adapter this SplitBar uses
34431      * @param {Object} adapter A SplitBar adapter object
34432      */
34433     setAdapter : function(adapter){
34434         this.adapter = adapter;
34435         this.adapter.init(this);
34436     },
34437     
34438     /**
34439      * Gets the minimum size for the resizing element
34440      * @return {Number} The minimum size
34441      */
34442     getMinimumSize : function(){
34443         return this.minSize;
34444     },
34445     
34446     /**
34447      * Sets the minimum size for the resizing element
34448      * @param {Number} minSize The minimum size
34449      */
34450     setMinimumSize : function(minSize){
34451         this.minSize = minSize;
34452     },
34453     
34454     /**
34455      * Gets the maximum size for the resizing element
34456      * @return {Number} The maximum size
34457      */
34458     getMaximumSize : function(){
34459         return this.maxSize;
34460     },
34461     
34462     /**
34463      * Sets the maximum size for the resizing element
34464      * @param {Number} maxSize The maximum size
34465      */
34466     setMaximumSize : function(maxSize){
34467         this.maxSize = maxSize;
34468     },
34469     
34470     /**
34471      * Sets the initialize size for the resizing element
34472      * @param {Number} size The initial size
34473      */
34474     setCurrentSize : function(size){
34475         var oldAnimate = this.animate;
34476         this.animate = false;
34477         this.adapter.setElementSize(this, size);
34478         this.animate = oldAnimate;
34479     },
34480     
34481     /**
34482      * Destroy this splitbar. 
34483      * @param {Boolean} removeEl True to remove the element
34484      */
34485     destroy : function(removeEl){
34486         if(this.shim){
34487             this.shim.remove();
34488         }
34489         this.dd.unreg();
34490         this.proxy.parentNode.removeChild(this.proxy);
34491         if(removeEl){
34492             this.el.remove();
34493         }
34494     }
34495 });
34496
34497 /**
34498  * @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.
34499  */
34500 Roo.bootstrap.SplitBar.createProxy = function(dir){
34501     var proxy = new Roo.Element(document.createElement("div"));
34502     proxy.unselectable();
34503     var cls = 'roo-splitbar-proxy';
34504     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34505     document.body.appendChild(proxy.dom);
34506     return proxy.dom;
34507 };
34508
34509 /** 
34510  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34511  * Default Adapter. It assumes the splitter and resizing element are not positioned
34512  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34513  */
34514 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34515 };
34516
34517 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34518     // do nothing for now
34519     init : function(s){
34520     
34521     },
34522     /**
34523      * Called before drag operations to get the current size of the resizing element. 
34524      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34525      */
34526      getElementSize : function(s){
34527         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34528             return s.resizingEl.getWidth();
34529         }else{
34530             return s.resizingEl.getHeight();
34531         }
34532     },
34533     
34534     /**
34535      * Called after drag operations to set the size of the resizing element.
34536      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34537      * @param {Number} newSize The new size to set
34538      * @param {Function} onComplete A function to be invoked when resizing is complete
34539      */
34540     setElementSize : function(s, newSize, onComplete){
34541         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34542             if(!s.animate){
34543                 s.resizingEl.setWidth(newSize);
34544                 if(onComplete){
34545                     onComplete(s, newSize);
34546                 }
34547             }else{
34548                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34549             }
34550         }else{
34551             
34552             if(!s.animate){
34553                 s.resizingEl.setHeight(newSize);
34554                 if(onComplete){
34555                     onComplete(s, newSize);
34556                 }
34557             }else{
34558                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34559             }
34560         }
34561     }
34562 };
34563
34564 /** 
34565  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34566  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34567  * Adapter that  moves the splitter element to align with the resized sizing element. 
34568  * Used with an absolute positioned SplitBar.
34569  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34570  * document.body, make sure you assign an id to the body element.
34571  */
34572 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34573     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34574     this.container = Roo.get(container);
34575 };
34576
34577 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34578     init : function(s){
34579         this.basic.init(s);
34580     },
34581     
34582     getElementSize : function(s){
34583         return this.basic.getElementSize(s);
34584     },
34585     
34586     setElementSize : function(s, newSize, onComplete){
34587         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34588     },
34589     
34590     moveSplitter : function(s){
34591         var yes = Roo.bootstrap.SplitBar;
34592         switch(s.placement){
34593             case yes.LEFT:
34594                 s.el.setX(s.resizingEl.getRight());
34595                 break;
34596             case yes.RIGHT:
34597                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34598                 break;
34599             case yes.TOP:
34600                 s.el.setY(s.resizingEl.getBottom());
34601                 break;
34602             case yes.BOTTOM:
34603                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34604                 break;
34605         }
34606     }
34607 };
34608
34609 /**
34610  * Orientation constant - Create a vertical SplitBar
34611  * @static
34612  * @type Number
34613  */
34614 Roo.bootstrap.SplitBar.VERTICAL = 1;
34615
34616 /**
34617  * Orientation constant - Create a horizontal SplitBar
34618  * @static
34619  * @type Number
34620  */
34621 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34622
34623 /**
34624  * Placement constant - The resizing element is to the left of the splitter element
34625  * @static
34626  * @type Number
34627  */
34628 Roo.bootstrap.SplitBar.LEFT = 1;
34629
34630 /**
34631  * Placement constant - The resizing element is to the right of the splitter element
34632  * @static
34633  * @type Number
34634  */
34635 Roo.bootstrap.SplitBar.RIGHT = 2;
34636
34637 /**
34638  * Placement constant - The resizing element is positioned above the splitter element
34639  * @static
34640  * @type Number
34641  */
34642 Roo.bootstrap.SplitBar.TOP = 3;
34643
34644 /**
34645  * Placement constant - The resizing element is positioned under splitter element
34646  * @static
34647  * @type Number
34648  */
34649 Roo.bootstrap.SplitBar.BOTTOM = 4;
34650 Roo.namespace("Roo.bootstrap.layout");/*
34651  * Based on:
34652  * Ext JS Library 1.1.1
34653  * Copyright(c) 2006-2007, Ext JS, LLC.
34654  *
34655  * Originally Released Under LGPL - original licence link has changed is not relivant.
34656  *
34657  * Fork - LGPL
34658  * <script type="text/javascript">
34659  */
34660
34661 /**
34662  * @class Roo.bootstrap.layout.Manager
34663  * @extends Roo.bootstrap.Component
34664  * Base class for layout managers.
34665  */
34666 Roo.bootstrap.layout.Manager = function(config)
34667 {
34668     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34669
34670
34671
34672
34673
34674     /** false to disable window resize monitoring @type Boolean */
34675     this.monitorWindowResize = true;
34676     this.regions = {};
34677     this.addEvents({
34678         /**
34679          * @event layout
34680          * Fires when a layout is performed.
34681          * @param {Roo.LayoutManager} this
34682          */
34683         "layout" : true,
34684         /**
34685          * @event regionresized
34686          * Fires when the user resizes a region.
34687          * @param {Roo.LayoutRegion} region The resized region
34688          * @param {Number} newSize The new size (width for east/west, height for north/south)
34689          */
34690         "regionresized" : true,
34691         /**
34692          * @event regioncollapsed
34693          * Fires when a region is collapsed.
34694          * @param {Roo.LayoutRegion} region The collapsed region
34695          */
34696         "regioncollapsed" : true,
34697         /**
34698          * @event regionexpanded
34699          * Fires when a region is expanded.
34700          * @param {Roo.LayoutRegion} region The expanded region
34701          */
34702         "regionexpanded" : true
34703     });
34704     this.updating = false;
34705
34706     if (config.el) {
34707         this.el = Roo.get(config.el);
34708         this.initEvents();
34709     }
34710
34711 };
34712
34713 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34714
34715
34716     regions : null,
34717
34718     monitorWindowResize : true,
34719
34720
34721     updating : false,
34722
34723
34724     onRender : function(ct, position)
34725     {
34726         if(!this.el){
34727             this.el = Roo.get(ct);
34728             this.initEvents();
34729         }
34730         //this.fireEvent('render',this);
34731     },
34732
34733
34734     initEvents: function()
34735     {
34736
34737
34738         // ie scrollbar fix
34739         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34740             document.body.scroll = "no";
34741         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34742             this.el.position('relative');
34743         }
34744         this.id = this.el.id;
34745         this.el.addClass("roo-layout-container");
34746         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34747         if(this.el.dom != document.body ) {
34748             this.el.on('resize', this.layout,this);
34749             this.el.on('show', this.layout,this);
34750         }
34751
34752     },
34753
34754     /**
34755      * Returns true if this layout is currently being updated
34756      * @return {Boolean}
34757      */
34758     isUpdating : function(){
34759         return this.updating;
34760     },
34761
34762     /**
34763      * Suspend the LayoutManager from doing auto-layouts while
34764      * making multiple add or remove calls
34765      */
34766     beginUpdate : function(){
34767         this.updating = true;
34768     },
34769
34770     /**
34771      * Restore auto-layouts and optionally disable the manager from performing a layout
34772      * @param {Boolean} noLayout true to disable a layout update
34773      */
34774     endUpdate : function(noLayout){
34775         this.updating = false;
34776         if(!noLayout){
34777             this.layout();
34778         }
34779     },
34780
34781     layout: function(){
34782         // abstract...
34783     },
34784
34785     onRegionResized : function(region, newSize){
34786         this.fireEvent("regionresized", region, newSize);
34787         this.layout();
34788     },
34789
34790     onRegionCollapsed : function(region){
34791         this.fireEvent("regioncollapsed", region);
34792     },
34793
34794     onRegionExpanded : function(region){
34795         this.fireEvent("regionexpanded", region);
34796     },
34797
34798     /**
34799      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34800      * performs box-model adjustments.
34801      * @return {Object} The size as an object {width: (the width), height: (the height)}
34802      */
34803     getViewSize : function()
34804     {
34805         var size;
34806         if(this.el.dom != document.body){
34807             size = this.el.getSize();
34808         }else{
34809             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34810         }
34811         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34812         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34813         return size;
34814     },
34815
34816     /**
34817      * Returns the Element this layout is bound to.
34818      * @return {Roo.Element}
34819      */
34820     getEl : function(){
34821         return this.el;
34822     },
34823
34824     /**
34825      * Returns the specified region.
34826      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34827      * @return {Roo.LayoutRegion}
34828      */
34829     getRegion : function(target){
34830         return this.regions[target.toLowerCase()];
34831     },
34832
34833     onWindowResize : function(){
34834         if(this.monitorWindowResize){
34835             this.layout();
34836         }
34837     }
34838 });
34839 /*
34840  * Based on:
34841  * Ext JS Library 1.1.1
34842  * Copyright(c) 2006-2007, Ext JS, LLC.
34843  *
34844  * Originally Released Under LGPL - original licence link has changed is not relivant.
34845  *
34846  * Fork - LGPL
34847  * <script type="text/javascript">
34848  */
34849 /**
34850  * @class Roo.bootstrap.layout.Border
34851  * @extends Roo.bootstrap.layout.Manager
34852  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34853  * please see: examples/bootstrap/nested.html<br><br>
34854  
34855 <b>The container the layout is rendered into can be either the body element or any other element.
34856 If it is not the body element, the container needs to either be an absolute positioned element,
34857 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34858 the container size if it is not the body element.</b>
34859
34860 * @constructor
34861 * Create a new Border
34862 * @param {Object} config Configuration options
34863  */
34864 Roo.bootstrap.layout.Border = function(config){
34865     config = config || {};
34866     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34867     
34868     
34869     
34870     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34871         if(config[region]){
34872             config[region].region = region;
34873             this.addRegion(config[region]);
34874         }
34875     },this);
34876     
34877 };
34878
34879 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34880
34881 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34882     /**
34883      * Creates and adds a new region if it doesn't already exist.
34884      * @param {String} target The target region key (north, south, east, west or center).
34885      * @param {Object} config The regions config object
34886      * @return {BorderLayoutRegion} The new region
34887      */
34888     addRegion : function(config)
34889     {
34890         if(!this.regions[config.region]){
34891             var r = this.factory(config);
34892             this.bindRegion(r);
34893         }
34894         return this.regions[config.region];
34895     },
34896
34897     // private (kinda)
34898     bindRegion : function(r){
34899         this.regions[r.config.region] = r;
34900         
34901         r.on("visibilitychange",    this.layout, this);
34902         r.on("paneladded",          this.layout, this);
34903         r.on("panelremoved",        this.layout, this);
34904         r.on("invalidated",         this.layout, this);
34905         r.on("resized",             this.onRegionResized, this);
34906         r.on("collapsed",           this.onRegionCollapsed, this);
34907         r.on("expanded",            this.onRegionExpanded, this);
34908     },
34909
34910     /**
34911      * Performs a layout update.
34912      */
34913     layout : function()
34914     {
34915         if(this.updating) {
34916             return;
34917         }
34918         
34919         // render all the rebions if they have not been done alreayd?
34920         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34921             if(this.regions[region] && !this.regions[region].bodyEl){
34922                 this.regions[region].onRender(this.el)
34923             }
34924         },this);
34925         
34926         var size = this.getViewSize();
34927         var w = size.width;
34928         var h = size.height;
34929         var centerW = w;
34930         var centerH = h;
34931         var centerY = 0;
34932         var centerX = 0;
34933         //var x = 0, y = 0;
34934
34935         var rs = this.regions;
34936         var north = rs["north"];
34937         var south = rs["south"]; 
34938         var west = rs["west"];
34939         var east = rs["east"];
34940         var center = rs["center"];
34941         //if(this.hideOnLayout){ // not supported anymore
34942             //c.el.setStyle("display", "none");
34943         //}
34944         if(north && north.isVisible()){
34945             var b = north.getBox();
34946             var m = north.getMargins();
34947             b.width = w - (m.left+m.right);
34948             b.x = m.left;
34949             b.y = m.top;
34950             centerY = b.height + b.y + m.bottom;
34951             centerH -= centerY;
34952             north.updateBox(this.safeBox(b));
34953         }
34954         if(south && south.isVisible()){
34955             var b = south.getBox();
34956             var m = south.getMargins();
34957             b.width = w - (m.left+m.right);
34958             b.x = m.left;
34959             var totalHeight = (b.height + m.top + m.bottom);
34960             b.y = h - totalHeight + m.top;
34961             centerH -= totalHeight;
34962             south.updateBox(this.safeBox(b));
34963         }
34964         if(west && west.isVisible()){
34965             var b = west.getBox();
34966             var m = west.getMargins();
34967             b.height = centerH - (m.top+m.bottom);
34968             b.x = m.left;
34969             b.y = centerY + m.top;
34970             var totalWidth = (b.width + m.left + m.right);
34971             centerX += totalWidth;
34972             centerW -= totalWidth;
34973             west.updateBox(this.safeBox(b));
34974         }
34975         if(east && east.isVisible()){
34976             var b = east.getBox();
34977             var m = east.getMargins();
34978             b.height = centerH - (m.top+m.bottom);
34979             var totalWidth = (b.width + m.left + m.right);
34980             b.x = w - totalWidth + m.left;
34981             b.y = centerY + m.top;
34982             centerW -= totalWidth;
34983             east.updateBox(this.safeBox(b));
34984         }
34985         if(center){
34986             var m = center.getMargins();
34987             var centerBox = {
34988                 x: centerX + m.left,
34989                 y: centerY + m.top,
34990                 width: centerW - (m.left+m.right),
34991                 height: centerH - (m.top+m.bottom)
34992             };
34993             //if(this.hideOnLayout){
34994                 //center.el.setStyle("display", "block");
34995             //}
34996             center.updateBox(this.safeBox(centerBox));
34997         }
34998         this.el.repaint();
34999         this.fireEvent("layout", this);
35000     },
35001
35002     // private
35003     safeBox : function(box){
35004         box.width = Math.max(0, box.width);
35005         box.height = Math.max(0, box.height);
35006         return box;
35007     },
35008
35009     /**
35010      * Adds a ContentPanel (or subclass) to this layout.
35011      * @param {String} target The target region key (north, south, east, west or center).
35012      * @param {Roo.ContentPanel} panel The panel to add
35013      * @return {Roo.ContentPanel} The added panel
35014      */
35015     add : function(target, panel){
35016          
35017         target = target.toLowerCase();
35018         return this.regions[target].add(panel);
35019     },
35020
35021     /**
35022      * Remove a ContentPanel (or subclass) to this layout.
35023      * @param {String} target The target region key (north, south, east, west or center).
35024      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35025      * @return {Roo.ContentPanel} The removed panel
35026      */
35027     remove : function(target, panel){
35028         target = target.toLowerCase();
35029         return this.regions[target].remove(panel);
35030     },
35031
35032     /**
35033      * Searches all regions for a panel with the specified id
35034      * @param {String} panelId
35035      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35036      */
35037     findPanel : function(panelId){
35038         var rs = this.regions;
35039         for(var target in rs){
35040             if(typeof rs[target] != "function"){
35041                 var p = rs[target].getPanel(panelId);
35042                 if(p){
35043                     return p;
35044                 }
35045             }
35046         }
35047         return null;
35048     },
35049
35050     /**
35051      * Searches all regions for a panel with the specified id and activates (shows) it.
35052      * @param {String/ContentPanel} panelId The panels id or the panel itself
35053      * @return {Roo.ContentPanel} The shown panel or null
35054      */
35055     showPanel : function(panelId) {
35056       var rs = this.regions;
35057       for(var target in rs){
35058          var r = rs[target];
35059          if(typeof r != "function"){
35060             if(r.hasPanel(panelId)){
35061                return r.showPanel(panelId);
35062             }
35063          }
35064       }
35065       return null;
35066    },
35067
35068    /**
35069      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35070      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35071      */
35072    /*
35073     restoreState : function(provider){
35074         if(!provider){
35075             provider = Roo.state.Manager;
35076         }
35077         var sm = new Roo.LayoutStateManager();
35078         sm.init(this, provider);
35079     },
35080 */
35081  
35082  
35083     /**
35084      * Adds a xtype elements to the layout.
35085      * <pre><code>
35086
35087 layout.addxtype({
35088        xtype : 'ContentPanel',
35089        region: 'west',
35090        items: [ .... ]
35091    }
35092 );
35093
35094 layout.addxtype({
35095         xtype : 'NestedLayoutPanel',
35096         region: 'west',
35097         layout: {
35098            center: { },
35099            west: { }   
35100         },
35101         items : [ ... list of content panels or nested layout panels.. ]
35102    }
35103 );
35104 </code></pre>
35105      * @param {Object} cfg Xtype definition of item to add.
35106      */
35107     addxtype : function(cfg)
35108     {
35109         // basically accepts a pannel...
35110         // can accept a layout region..!?!?
35111         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35112         
35113         
35114         // theory?  children can only be panels??
35115         
35116         //if (!cfg.xtype.match(/Panel$/)) {
35117         //    return false;
35118         //}
35119         var ret = false;
35120         
35121         if (typeof(cfg.region) == 'undefined') {
35122             Roo.log("Failed to add Panel, region was not set");
35123             Roo.log(cfg);
35124             return false;
35125         }
35126         var region = cfg.region;
35127         delete cfg.region;
35128         
35129           
35130         var xitems = [];
35131         if (cfg.items) {
35132             xitems = cfg.items;
35133             delete cfg.items;
35134         }
35135         var nb = false;
35136         
35137         switch(cfg.xtype) 
35138         {
35139             case 'Content':  // ContentPanel (el, cfg)
35140             case 'Scroll':  // ContentPanel (el, cfg)
35141             case 'View': 
35142                 cfg.autoCreate = true;
35143                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35144                 //} else {
35145                 //    var el = this.el.createChild();
35146                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35147                 //}
35148                 
35149                 this.add(region, ret);
35150                 break;
35151             
35152             /*
35153             case 'TreePanel': // our new panel!
35154                 cfg.el = this.el.createChild();
35155                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35156                 this.add(region, ret);
35157                 break;
35158             */
35159             
35160             case 'Nest': 
35161                 // create a new Layout (which is  a Border Layout...
35162                 
35163                 var clayout = cfg.layout;
35164                 clayout.el  = this.el.createChild();
35165                 clayout.items   = clayout.items  || [];
35166                 
35167                 delete cfg.layout;
35168                 
35169                 // replace this exitems with the clayout ones..
35170                 xitems = clayout.items;
35171                  
35172                 // force background off if it's in center...
35173                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35174                     cfg.background = false;
35175                 }
35176                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35177                 
35178                 
35179                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35180                 //console.log('adding nested layout panel '  + cfg.toSource());
35181                 this.add(region, ret);
35182                 nb = {}; /// find first...
35183                 break;
35184             
35185             case 'Grid':
35186                 
35187                 // needs grid and region
35188                 
35189                 //var el = this.getRegion(region).el.createChild();
35190                 /*
35191                  *var el = this.el.createChild();
35192                 // create the grid first...
35193                 cfg.grid.container = el;
35194                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35195                 */
35196                 
35197                 if (region == 'center' && this.active ) {
35198                     cfg.background = false;
35199                 }
35200                 
35201                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35202                 
35203                 this.add(region, ret);
35204                 /*
35205                 if (cfg.background) {
35206                     // render grid on panel activation (if panel background)
35207                     ret.on('activate', function(gp) {
35208                         if (!gp.grid.rendered) {
35209                     //        gp.grid.render(el);
35210                         }
35211                     });
35212                 } else {
35213                   //  cfg.grid.render(el);
35214                 }
35215                 */
35216                 break;
35217            
35218            
35219             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35220                 // it was the old xcomponent building that caused this before.
35221                 // espeically if border is the top element in the tree.
35222                 ret = this;
35223                 break; 
35224                 
35225                     
35226                 
35227                 
35228                 
35229             default:
35230                 /*
35231                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35232                     
35233                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35234                     this.add(region, ret);
35235                 } else {
35236                 */
35237                     Roo.log(cfg);
35238                     throw "Can not add '" + cfg.xtype + "' to Border";
35239                     return null;
35240              
35241                                 
35242              
35243         }
35244         this.beginUpdate();
35245         // add children..
35246         var region = '';
35247         var abn = {};
35248         Roo.each(xitems, function(i)  {
35249             region = nb && i.region ? i.region : false;
35250             
35251             var add = ret.addxtype(i);
35252            
35253             if (region) {
35254                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35255                 if (!i.background) {
35256                     abn[region] = nb[region] ;
35257                 }
35258             }
35259             
35260         });
35261         this.endUpdate();
35262
35263         // make the last non-background panel active..
35264         //if (nb) { Roo.log(abn); }
35265         if (nb) {
35266             
35267             for(var r in abn) {
35268                 region = this.getRegion(r);
35269                 if (region) {
35270                     // tried using nb[r], but it does not work..
35271                      
35272                     region.showPanel(abn[r]);
35273                    
35274                 }
35275             }
35276         }
35277         return ret;
35278         
35279     },
35280     
35281     
35282 // private
35283     factory : function(cfg)
35284     {
35285         
35286         var validRegions = Roo.bootstrap.layout.Border.regions;
35287
35288         var target = cfg.region;
35289         cfg.mgr = this;
35290         
35291         var r = Roo.bootstrap.layout;
35292         Roo.log(target);
35293         switch(target){
35294             case "north":
35295                 return new r.North(cfg);
35296             case "south":
35297                 return new r.South(cfg);
35298             case "east":
35299                 return new r.East(cfg);
35300             case "west":
35301                 return new r.West(cfg);
35302             case "center":
35303                 return new r.Center(cfg);
35304         }
35305         throw 'Layout region "'+target+'" not supported.';
35306     }
35307     
35308     
35309 });
35310  /*
35311  * Based on:
35312  * Ext JS Library 1.1.1
35313  * Copyright(c) 2006-2007, Ext JS, LLC.
35314  *
35315  * Originally Released Under LGPL - original licence link has changed is not relivant.
35316  *
35317  * Fork - LGPL
35318  * <script type="text/javascript">
35319  */
35320  
35321 /**
35322  * @class Roo.bootstrap.layout.Basic
35323  * @extends Roo.util.Observable
35324  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35325  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35326  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35327  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35328  * @cfg {string}   region  the region that it inhabits..
35329  * @cfg {bool}   skipConfig skip config?
35330  * 
35331
35332  */
35333 Roo.bootstrap.layout.Basic = function(config){
35334     
35335     this.mgr = config.mgr;
35336     
35337     this.position = config.region;
35338     
35339     var skipConfig = config.skipConfig;
35340     
35341     this.events = {
35342         /**
35343          * @scope Roo.BasicLayoutRegion
35344          */
35345         
35346         /**
35347          * @event beforeremove
35348          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35349          * @param {Roo.LayoutRegion} this
35350          * @param {Roo.ContentPanel} panel The panel
35351          * @param {Object} e The cancel event object
35352          */
35353         "beforeremove" : true,
35354         /**
35355          * @event invalidated
35356          * Fires when the layout for this region is changed.
35357          * @param {Roo.LayoutRegion} this
35358          */
35359         "invalidated" : true,
35360         /**
35361          * @event visibilitychange
35362          * Fires when this region is shown or hidden 
35363          * @param {Roo.LayoutRegion} this
35364          * @param {Boolean} visibility true or false
35365          */
35366         "visibilitychange" : true,
35367         /**
35368          * @event paneladded
35369          * Fires when a panel is added. 
35370          * @param {Roo.LayoutRegion} this
35371          * @param {Roo.ContentPanel} panel The panel
35372          */
35373         "paneladded" : true,
35374         /**
35375          * @event panelremoved
35376          * Fires when a panel is removed. 
35377          * @param {Roo.LayoutRegion} this
35378          * @param {Roo.ContentPanel} panel The panel
35379          */
35380         "panelremoved" : true,
35381         /**
35382          * @event beforecollapse
35383          * Fires when this region before collapse.
35384          * @param {Roo.LayoutRegion} this
35385          */
35386         "beforecollapse" : true,
35387         /**
35388          * @event collapsed
35389          * Fires when this region is collapsed.
35390          * @param {Roo.LayoutRegion} this
35391          */
35392         "collapsed" : true,
35393         /**
35394          * @event expanded
35395          * Fires when this region is expanded.
35396          * @param {Roo.LayoutRegion} this
35397          */
35398         "expanded" : true,
35399         /**
35400          * @event slideshow
35401          * Fires when this region is slid into view.
35402          * @param {Roo.LayoutRegion} this
35403          */
35404         "slideshow" : true,
35405         /**
35406          * @event slidehide
35407          * Fires when this region slides out of view. 
35408          * @param {Roo.LayoutRegion} this
35409          */
35410         "slidehide" : true,
35411         /**
35412          * @event panelactivated
35413          * Fires when a panel is activated. 
35414          * @param {Roo.LayoutRegion} this
35415          * @param {Roo.ContentPanel} panel The activated panel
35416          */
35417         "panelactivated" : true,
35418         /**
35419          * @event resized
35420          * Fires when the user resizes this region. 
35421          * @param {Roo.LayoutRegion} this
35422          * @param {Number} newSize The new size (width for east/west, height for north/south)
35423          */
35424         "resized" : true
35425     };
35426     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35427     this.panels = new Roo.util.MixedCollection();
35428     this.panels.getKey = this.getPanelId.createDelegate(this);
35429     this.box = null;
35430     this.activePanel = null;
35431     // ensure listeners are added...
35432     
35433     if (config.listeners || config.events) {
35434         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35435             listeners : config.listeners || {},
35436             events : config.events || {}
35437         });
35438     }
35439     
35440     if(skipConfig !== true){
35441         this.applyConfig(config);
35442     }
35443 };
35444
35445 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35446 {
35447     getPanelId : function(p){
35448         return p.getId();
35449     },
35450     
35451     applyConfig : function(config){
35452         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35453         this.config = config;
35454         
35455     },
35456     
35457     /**
35458      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35459      * the width, for horizontal (north, south) the height.
35460      * @param {Number} newSize The new width or height
35461      */
35462     resizeTo : function(newSize){
35463         var el = this.el ? this.el :
35464                  (this.activePanel ? this.activePanel.getEl() : null);
35465         if(el){
35466             switch(this.position){
35467                 case "east":
35468                 case "west":
35469                     el.setWidth(newSize);
35470                     this.fireEvent("resized", this, newSize);
35471                 break;
35472                 case "north":
35473                 case "south":
35474                     el.setHeight(newSize);
35475                     this.fireEvent("resized", this, newSize);
35476                 break;                
35477             }
35478         }
35479     },
35480     
35481     getBox : function(){
35482         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35483     },
35484     
35485     getMargins : function(){
35486         return this.margins;
35487     },
35488     
35489     updateBox : function(box){
35490         this.box = box;
35491         var el = this.activePanel.getEl();
35492         el.dom.style.left = box.x + "px";
35493         el.dom.style.top = box.y + "px";
35494         this.activePanel.setSize(box.width, box.height);
35495     },
35496     
35497     /**
35498      * Returns the container element for this region.
35499      * @return {Roo.Element}
35500      */
35501     getEl : function(){
35502         return this.activePanel;
35503     },
35504     
35505     /**
35506      * Returns true if this region is currently visible.
35507      * @return {Boolean}
35508      */
35509     isVisible : function(){
35510         return this.activePanel ? true : false;
35511     },
35512     
35513     setActivePanel : function(panel){
35514         panel = this.getPanel(panel);
35515         if(this.activePanel && this.activePanel != panel){
35516             this.activePanel.setActiveState(false);
35517             this.activePanel.getEl().setLeftTop(-10000,-10000);
35518         }
35519         this.activePanel = panel;
35520         panel.setActiveState(true);
35521         if(this.box){
35522             panel.setSize(this.box.width, this.box.height);
35523         }
35524         this.fireEvent("panelactivated", this, panel);
35525         this.fireEvent("invalidated");
35526     },
35527     
35528     /**
35529      * Show the specified panel.
35530      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35531      * @return {Roo.ContentPanel} The shown panel or null
35532      */
35533     showPanel : function(panel){
35534         panel = this.getPanel(panel);
35535         if(panel){
35536             this.setActivePanel(panel);
35537         }
35538         return panel;
35539     },
35540     
35541     /**
35542      * Get the active panel for this region.
35543      * @return {Roo.ContentPanel} The active panel or null
35544      */
35545     getActivePanel : function(){
35546         return this.activePanel;
35547     },
35548     
35549     /**
35550      * Add the passed ContentPanel(s)
35551      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35552      * @return {Roo.ContentPanel} The panel added (if only one was added)
35553      */
35554     add : function(panel){
35555         if(arguments.length > 1){
35556             for(var i = 0, len = arguments.length; i < len; i++) {
35557                 this.add(arguments[i]);
35558             }
35559             return null;
35560         }
35561         if(this.hasPanel(panel)){
35562             this.showPanel(panel);
35563             return panel;
35564         }
35565         var el = panel.getEl();
35566         if(el.dom.parentNode != this.mgr.el.dom){
35567             this.mgr.el.dom.appendChild(el.dom);
35568         }
35569         if(panel.setRegion){
35570             panel.setRegion(this);
35571         }
35572         this.panels.add(panel);
35573         el.setStyle("position", "absolute");
35574         if(!panel.background){
35575             this.setActivePanel(panel);
35576             if(this.config.initialSize && this.panels.getCount()==1){
35577                 this.resizeTo(this.config.initialSize);
35578             }
35579         }
35580         this.fireEvent("paneladded", this, panel);
35581         return panel;
35582     },
35583     
35584     /**
35585      * Returns true if the panel is in this region.
35586      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35587      * @return {Boolean}
35588      */
35589     hasPanel : function(panel){
35590         if(typeof panel == "object"){ // must be panel obj
35591             panel = panel.getId();
35592         }
35593         return this.getPanel(panel) ? true : false;
35594     },
35595     
35596     /**
35597      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35598      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35599      * @param {Boolean} preservePanel Overrides the config preservePanel option
35600      * @return {Roo.ContentPanel} The panel that was removed
35601      */
35602     remove : function(panel, preservePanel){
35603         panel = this.getPanel(panel);
35604         if(!panel){
35605             return null;
35606         }
35607         var e = {};
35608         this.fireEvent("beforeremove", this, panel, e);
35609         if(e.cancel === true){
35610             return null;
35611         }
35612         var panelId = panel.getId();
35613         this.panels.removeKey(panelId);
35614         return panel;
35615     },
35616     
35617     /**
35618      * Returns the panel specified or null if it's not in this region.
35619      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35620      * @return {Roo.ContentPanel}
35621      */
35622     getPanel : function(id){
35623         if(typeof id == "object"){ // must be panel obj
35624             return id;
35625         }
35626         return this.panels.get(id);
35627     },
35628     
35629     /**
35630      * Returns this regions position (north/south/east/west/center).
35631      * @return {String} 
35632      */
35633     getPosition: function(){
35634         return this.position;    
35635     }
35636 });/*
35637  * Based on:
35638  * Ext JS Library 1.1.1
35639  * Copyright(c) 2006-2007, Ext JS, LLC.
35640  *
35641  * Originally Released Under LGPL - original licence link has changed is not relivant.
35642  *
35643  * Fork - LGPL
35644  * <script type="text/javascript">
35645  */
35646  
35647 /**
35648  * @class Roo.bootstrap.layout.Region
35649  * @extends Roo.bootstrap.layout.Basic
35650  * This class represents a region in a layout manager.
35651  
35652  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35653  * @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})
35654  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35655  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35656  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35657  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35658  * @cfg {String}    title           The title for the region (overrides panel titles)
35659  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35660  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35661  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35662  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35663  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35664  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35665  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35666  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35667  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35668  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35669
35670  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35671  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35672  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35673  * @cfg {Number}    width           For East/West panels
35674  * @cfg {Number}    height          For North/South panels
35675  * @cfg {Boolean}   split           To show the splitter
35676  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35677  * 
35678  * @cfg {string}   cls             Extra CSS classes to add to region
35679  * 
35680  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35681  * @cfg {string}   region  the region that it inhabits..
35682  *
35683
35684  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35685  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35686
35687  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35688  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35689  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35690  */
35691 Roo.bootstrap.layout.Region = function(config)
35692 {
35693     this.applyConfig(config);
35694
35695     var mgr = config.mgr;
35696     var pos = config.region;
35697     config.skipConfig = true;
35698     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35699     
35700     if (mgr.el) {
35701         this.onRender(mgr.el);   
35702     }
35703      
35704     this.visible = true;
35705     this.collapsed = false;
35706     this.unrendered_panels = [];
35707 };
35708
35709 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35710
35711     position: '', // set by wrapper (eg. north/south etc..)
35712     unrendered_panels : null,  // unrendered panels.
35713     createBody : function(){
35714         /** This region's body element 
35715         * @type Roo.Element */
35716         this.bodyEl = this.el.createChild({
35717                 tag: "div",
35718                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35719         });
35720     },
35721
35722     onRender: function(ctr, pos)
35723     {
35724         var dh = Roo.DomHelper;
35725         /** This region's container element 
35726         * @type Roo.Element */
35727         this.el = dh.append(ctr.dom, {
35728                 tag: "div",
35729                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35730             }, true);
35731         /** This region's title element 
35732         * @type Roo.Element */
35733     
35734         this.titleEl = dh.append(this.el.dom,
35735             {
35736                     tag: "div",
35737                     unselectable: "on",
35738                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35739                     children:[
35740                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35741                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35742                     ]}, true);
35743         
35744         this.titleEl.enableDisplayMode();
35745         /** This region's title text element 
35746         * @type HTMLElement */
35747         this.titleTextEl = this.titleEl.dom.firstChild;
35748         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35749         /*
35750         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35751         this.closeBtn.enableDisplayMode();
35752         this.closeBtn.on("click", this.closeClicked, this);
35753         this.closeBtn.hide();
35754     */
35755         this.createBody(this.config);
35756         if(this.config.hideWhenEmpty){
35757             this.hide();
35758             this.on("paneladded", this.validateVisibility, this);
35759             this.on("panelremoved", this.validateVisibility, this);
35760         }
35761         if(this.autoScroll){
35762             this.bodyEl.setStyle("overflow", "auto");
35763         }else{
35764             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35765         }
35766         //if(c.titlebar !== false){
35767             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35768                 this.titleEl.hide();
35769             }else{
35770                 this.titleEl.show();
35771                 if(this.config.title){
35772                     this.titleTextEl.innerHTML = this.config.title;
35773                 }
35774             }
35775         //}
35776         if(this.config.collapsed){
35777             this.collapse(true);
35778         }
35779         if(this.config.hidden){
35780             this.hide();
35781         }
35782         
35783         if (this.unrendered_panels && this.unrendered_panels.length) {
35784             for (var i =0;i< this.unrendered_panels.length; i++) {
35785                 this.add(this.unrendered_panels[i]);
35786             }
35787             this.unrendered_panels = null;
35788             
35789         }
35790         
35791     },
35792     
35793     applyConfig : function(c)
35794     {
35795         /*
35796          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35797             var dh = Roo.DomHelper;
35798             if(c.titlebar !== false){
35799                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35800                 this.collapseBtn.on("click", this.collapse, this);
35801                 this.collapseBtn.enableDisplayMode();
35802                 /*
35803                 if(c.showPin === true || this.showPin){
35804                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35805                     this.stickBtn.enableDisplayMode();
35806                     this.stickBtn.on("click", this.expand, this);
35807                     this.stickBtn.hide();
35808                 }
35809                 
35810             }
35811             */
35812             /** This region's collapsed element
35813             * @type Roo.Element */
35814             /*
35815              *
35816             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35817                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35818             ]}, true);
35819             
35820             if(c.floatable !== false){
35821                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35822                this.collapsedEl.on("click", this.collapseClick, this);
35823             }
35824
35825             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35826                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35827                    id: "message", unselectable: "on", style:{"float":"left"}});
35828                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35829              }
35830             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35831             this.expandBtn.on("click", this.expand, this);
35832             
35833         }
35834         
35835         if(this.collapseBtn){
35836             this.collapseBtn.setVisible(c.collapsible == true);
35837         }
35838         
35839         this.cmargins = c.cmargins || this.cmargins ||
35840                          (this.position == "west" || this.position == "east" ?
35841                              {top: 0, left: 2, right:2, bottom: 0} :
35842                              {top: 2, left: 0, right:0, bottom: 2});
35843         */
35844         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35845         
35846         
35847         this.bottomTabs = c.tabPosition != "top";
35848         
35849         this.autoScroll = c.autoScroll || false;
35850         
35851         
35852        
35853         
35854         this.duration = c.duration || .30;
35855         this.slideDuration = c.slideDuration || .45;
35856         this.config = c;
35857        
35858     },
35859     /**
35860      * Returns true if this region is currently visible.
35861      * @return {Boolean}
35862      */
35863     isVisible : function(){
35864         return this.visible;
35865     },
35866
35867     /**
35868      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35869      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35870      */
35871     //setCollapsedTitle : function(title){
35872     //    title = title || "&#160;";
35873      //   if(this.collapsedTitleTextEl){
35874       //      this.collapsedTitleTextEl.innerHTML = title;
35875        // }
35876     //},
35877
35878     getBox : function(){
35879         var b;
35880       //  if(!this.collapsed){
35881             b = this.el.getBox(false, true);
35882        // }else{
35883           //  b = this.collapsedEl.getBox(false, true);
35884         //}
35885         return b;
35886     },
35887
35888     getMargins : function(){
35889         return this.margins;
35890         //return this.collapsed ? this.cmargins : this.margins;
35891     },
35892 /*
35893     highlight : function(){
35894         this.el.addClass("x-layout-panel-dragover");
35895     },
35896
35897     unhighlight : function(){
35898         this.el.removeClass("x-layout-panel-dragover");
35899     },
35900 */
35901     updateBox : function(box)
35902     {
35903         if (!this.bodyEl) {
35904             return; // not rendered yet..
35905         }
35906         
35907         this.box = box;
35908         if(!this.collapsed){
35909             this.el.dom.style.left = box.x + "px";
35910             this.el.dom.style.top = box.y + "px";
35911             this.updateBody(box.width, box.height);
35912         }else{
35913             this.collapsedEl.dom.style.left = box.x + "px";
35914             this.collapsedEl.dom.style.top = box.y + "px";
35915             this.collapsedEl.setSize(box.width, box.height);
35916         }
35917         if(this.tabs){
35918             this.tabs.autoSizeTabs();
35919         }
35920     },
35921
35922     updateBody : function(w, h)
35923     {
35924         if(w !== null){
35925             this.el.setWidth(w);
35926             w -= this.el.getBorderWidth("rl");
35927             if(this.config.adjustments){
35928                 w += this.config.adjustments[0];
35929             }
35930         }
35931         if(h !== null && h > 0){
35932             this.el.setHeight(h);
35933             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35934             h -= this.el.getBorderWidth("tb");
35935             if(this.config.adjustments){
35936                 h += this.config.adjustments[1];
35937             }
35938             this.bodyEl.setHeight(h);
35939             if(this.tabs){
35940                 h = this.tabs.syncHeight(h);
35941             }
35942         }
35943         if(this.panelSize){
35944             w = w !== null ? w : this.panelSize.width;
35945             h = h !== null ? h : this.panelSize.height;
35946         }
35947         if(this.activePanel){
35948             var el = this.activePanel.getEl();
35949             w = w !== null ? w : el.getWidth();
35950             h = h !== null ? h : el.getHeight();
35951             this.panelSize = {width: w, height: h};
35952             this.activePanel.setSize(w, h);
35953         }
35954         if(Roo.isIE && this.tabs){
35955             this.tabs.el.repaint();
35956         }
35957     },
35958
35959     /**
35960      * Returns the container element for this region.
35961      * @return {Roo.Element}
35962      */
35963     getEl : function(){
35964         return this.el;
35965     },
35966
35967     /**
35968      * Hides this region.
35969      */
35970     hide : function(){
35971         //if(!this.collapsed){
35972             this.el.dom.style.left = "-2000px";
35973             this.el.hide();
35974         //}else{
35975          //   this.collapsedEl.dom.style.left = "-2000px";
35976          //   this.collapsedEl.hide();
35977        // }
35978         this.visible = false;
35979         this.fireEvent("visibilitychange", this, false);
35980     },
35981
35982     /**
35983      * Shows this region if it was previously hidden.
35984      */
35985     show : function(){
35986         //if(!this.collapsed){
35987             this.el.show();
35988         //}else{
35989         //    this.collapsedEl.show();
35990        // }
35991         this.visible = true;
35992         this.fireEvent("visibilitychange", this, true);
35993     },
35994 /*
35995     closeClicked : function(){
35996         if(this.activePanel){
35997             this.remove(this.activePanel);
35998         }
35999     },
36000
36001     collapseClick : function(e){
36002         if(this.isSlid){
36003            e.stopPropagation();
36004            this.slideIn();
36005         }else{
36006            e.stopPropagation();
36007            this.slideOut();
36008         }
36009     },
36010 */
36011     /**
36012      * Collapses this region.
36013      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36014      */
36015     /*
36016     collapse : function(skipAnim, skipCheck = false){
36017         if(this.collapsed) {
36018             return;
36019         }
36020         
36021         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36022             
36023             this.collapsed = true;
36024             if(this.split){
36025                 this.split.el.hide();
36026             }
36027             if(this.config.animate && skipAnim !== true){
36028                 this.fireEvent("invalidated", this);
36029                 this.animateCollapse();
36030             }else{
36031                 this.el.setLocation(-20000,-20000);
36032                 this.el.hide();
36033                 this.collapsedEl.show();
36034                 this.fireEvent("collapsed", this);
36035                 this.fireEvent("invalidated", this);
36036             }
36037         }
36038         
36039     },
36040 */
36041     animateCollapse : function(){
36042         // overridden
36043     },
36044
36045     /**
36046      * Expands this region if it was previously collapsed.
36047      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36048      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36049      */
36050     /*
36051     expand : function(e, skipAnim){
36052         if(e) {
36053             e.stopPropagation();
36054         }
36055         if(!this.collapsed || this.el.hasActiveFx()) {
36056             return;
36057         }
36058         if(this.isSlid){
36059             this.afterSlideIn();
36060             skipAnim = true;
36061         }
36062         this.collapsed = false;
36063         if(this.config.animate && skipAnim !== true){
36064             this.animateExpand();
36065         }else{
36066             this.el.show();
36067             if(this.split){
36068                 this.split.el.show();
36069             }
36070             this.collapsedEl.setLocation(-2000,-2000);
36071             this.collapsedEl.hide();
36072             this.fireEvent("invalidated", this);
36073             this.fireEvent("expanded", this);
36074         }
36075     },
36076 */
36077     animateExpand : function(){
36078         // overridden
36079     },
36080
36081     initTabs : function()
36082     {
36083         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36084         
36085         var ts = new Roo.bootstrap.panel.Tabs({
36086                 el: this.bodyEl.dom,
36087                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36088                 disableTooltips: this.config.disableTabTips,
36089                 toolbar : this.config.toolbar
36090             });
36091         
36092         if(this.config.hideTabs){
36093             ts.stripWrap.setDisplayed(false);
36094         }
36095         this.tabs = ts;
36096         ts.resizeTabs = this.config.resizeTabs === true;
36097         ts.minTabWidth = this.config.minTabWidth || 40;
36098         ts.maxTabWidth = this.config.maxTabWidth || 250;
36099         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36100         ts.monitorResize = false;
36101         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36102         ts.bodyEl.addClass('roo-layout-tabs-body');
36103         this.panels.each(this.initPanelAsTab, this);
36104     },
36105
36106     initPanelAsTab : function(panel){
36107         var ti = this.tabs.addTab(
36108             panel.getEl().id,
36109             panel.getTitle(),
36110             null,
36111             this.config.closeOnTab && panel.isClosable(),
36112             panel.tpl
36113         );
36114         if(panel.tabTip !== undefined){
36115             ti.setTooltip(panel.tabTip);
36116         }
36117         ti.on("activate", function(){
36118               this.setActivePanel(panel);
36119         }, this);
36120         
36121         if(this.config.closeOnTab){
36122             ti.on("beforeclose", function(t, e){
36123                 e.cancel = true;
36124                 this.remove(panel);
36125             }, this);
36126         }
36127         
36128         panel.tabItem = ti;
36129         
36130         return ti;
36131     },
36132
36133     updatePanelTitle : function(panel, title)
36134     {
36135         if(this.activePanel == panel){
36136             this.updateTitle(title);
36137         }
36138         if(this.tabs){
36139             var ti = this.tabs.getTab(panel.getEl().id);
36140             ti.setText(title);
36141             if(panel.tabTip !== undefined){
36142                 ti.setTooltip(panel.tabTip);
36143             }
36144         }
36145     },
36146
36147     updateTitle : function(title){
36148         if(this.titleTextEl && !this.config.title){
36149             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36150         }
36151     },
36152
36153     setActivePanel : function(panel)
36154     {
36155         panel = this.getPanel(panel);
36156         if(this.activePanel && this.activePanel != panel){
36157             if(this.activePanel.setActiveState(false) === false){
36158                 return;
36159             }
36160         }
36161         this.activePanel = panel;
36162         panel.setActiveState(true);
36163         if(this.panelSize){
36164             panel.setSize(this.panelSize.width, this.panelSize.height);
36165         }
36166         if(this.closeBtn){
36167             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36168         }
36169         this.updateTitle(panel.getTitle());
36170         if(this.tabs){
36171             this.fireEvent("invalidated", this);
36172         }
36173         this.fireEvent("panelactivated", this, panel);
36174     },
36175
36176     /**
36177      * Shows the specified panel.
36178      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36179      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36180      */
36181     showPanel : function(panel)
36182     {
36183         panel = this.getPanel(panel);
36184         if(panel){
36185             if(this.tabs){
36186                 var tab = this.tabs.getTab(panel.getEl().id);
36187                 if(tab.isHidden()){
36188                     this.tabs.unhideTab(tab.id);
36189                 }
36190                 tab.activate();
36191             }else{
36192                 this.setActivePanel(panel);
36193             }
36194         }
36195         return panel;
36196     },
36197
36198     /**
36199      * Get the active panel for this region.
36200      * @return {Roo.ContentPanel} The active panel or null
36201      */
36202     getActivePanel : function(){
36203         return this.activePanel;
36204     },
36205
36206     validateVisibility : function(){
36207         if(this.panels.getCount() < 1){
36208             this.updateTitle("&#160;");
36209             this.closeBtn.hide();
36210             this.hide();
36211         }else{
36212             if(!this.isVisible()){
36213                 this.show();
36214             }
36215         }
36216     },
36217
36218     /**
36219      * Adds the passed ContentPanel(s) to this region.
36220      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36221      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36222      */
36223     add : function(panel)
36224     {
36225         if(arguments.length > 1){
36226             for(var i = 0, len = arguments.length; i < len; i++) {
36227                 this.add(arguments[i]);
36228             }
36229             return null;
36230         }
36231         
36232         // if we have not been rendered yet, then we can not really do much of this..
36233         if (!this.bodyEl) {
36234             this.unrendered_panels.push(panel);
36235             return panel;
36236         }
36237         
36238         
36239         
36240         
36241         if(this.hasPanel(panel)){
36242             this.showPanel(panel);
36243             return panel;
36244         }
36245         panel.setRegion(this);
36246         this.panels.add(panel);
36247        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36248             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36249             // and hide them... ???
36250             this.bodyEl.dom.appendChild(panel.getEl().dom);
36251             if(panel.background !== true){
36252                 this.setActivePanel(panel);
36253             }
36254             this.fireEvent("paneladded", this, panel);
36255             return panel;
36256         }
36257         */
36258         if(!this.tabs){
36259             this.initTabs();
36260         }else{
36261             this.initPanelAsTab(panel);
36262         }
36263         
36264         
36265         if(panel.background !== true){
36266             this.tabs.activate(panel.getEl().id);
36267         }
36268         this.fireEvent("paneladded", this, panel);
36269         return panel;
36270     },
36271
36272     /**
36273      * Hides the tab for the specified panel.
36274      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36275      */
36276     hidePanel : function(panel){
36277         if(this.tabs && (panel = this.getPanel(panel))){
36278             this.tabs.hideTab(panel.getEl().id);
36279         }
36280     },
36281
36282     /**
36283      * Unhides the tab for a previously hidden panel.
36284      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36285      */
36286     unhidePanel : function(panel){
36287         if(this.tabs && (panel = this.getPanel(panel))){
36288             this.tabs.unhideTab(panel.getEl().id);
36289         }
36290     },
36291
36292     clearPanels : function(){
36293         while(this.panels.getCount() > 0){
36294              this.remove(this.panels.first());
36295         }
36296     },
36297
36298     /**
36299      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36300      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36301      * @param {Boolean} preservePanel Overrides the config preservePanel option
36302      * @return {Roo.ContentPanel} The panel that was removed
36303      */
36304     remove : function(panel, preservePanel)
36305     {
36306         panel = this.getPanel(panel);
36307         if(!panel){
36308             return null;
36309         }
36310         var e = {};
36311         this.fireEvent("beforeremove", this, panel, e);
36312         if(e.cancel === true){
36313             return null;
36314         }
36315         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36316         var panelId = panel.getId();
36317         this.panels.removeKey(panelId);
36318         if(preservePanel){
36319             document.body.appendChild(panel.getEl().dom);
36320         }
36321         if(this.tabs){
36322             this.tabs.removeTab(panel.getEl().id);
36323         }else if (!preservePanel){
36324             this.bodyEl.dom.removeChild(panel.getEl().dom);
36325         }
36326         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36327             var p = this.panels.first();
36328             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36329             tempEl.appendChild(p.getEl().dom);
36330             this.bodyEl.update("");
36331             this.bodyEl.dom.appendChild(p.getEl().dom);
36332             tempEl = null;
36333             this.updateTitle(p.getTitle());
36334             this.tabs = null;
36335             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36336             this.setActivePanel(p);
36337         }
36338         panel.setRegion(null);
36339         if(this.activePanel == panel){
36340             this.activePanel = null;
36341         }
36342         if(this.config.autoDestroy !== false && preservePanel !== true){
36343             try{panel.destroy();}catch(e){}
36344         }
36345         this.fireEvent("panelremoved", this, panel);
36346         return panel;
36347     },
36348
36349     /**
36350      * Returns the TabPanel component used by this region
36351      * @return {Roo.TabPanel}
36352      */
36353     getTabs : function(){
36354         return this.tabs;
36355     },
36356
36357     createTool : function(parentEl, className){
36358         var btn = Roo.DomHelper.append(parentEl, {
36359             tag: "div",
36360             cls: "x-layout-tools-button",
36361             children: [ {
36362                 tag: "div",
36363                 cls: "roo-layout-tools-button-inner " + className,
36364                 html: "&#160;"
36365             }]
36366         }, true);
36367         btn.addClassOnOver("roo-layout-tools-button-over");
36368         return btn;
36369     }
36370 });/*
36371  * Based on:
36372  * Ext JS Library 1.1.1
36373  * Copyright(c) 2006-2007, Ext JS, LLC.
36374  *
36375  * Originally Released Under LGPL - original licence link has changed is not relivant.
36376  *
36377  * Fork - LGPL
36378  * <script type="text/javascript">
36379  */
36380  
36381
36382
36383 /**
36384  * @class Roo.SplitLayoutRegion
36385  * @extends Roo.LayoutRegion
36386  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36387  */
36388 Roo.bootstrap.layout.Split = function(config){
36389     this.cursor = config.cursor;
36390     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36391 };
36392
36393 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36394 {
36395     splitTip : "Drag to resize.",
36396     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36397     useSplitTips : false,
36398
36399     applyConfig : function(config){
36400         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36401     },
36402     
36403     onRender : function(ctr,pos) {
36404         
36405         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36406         if(!this.config.split){
36407             return;
36408         }
36409         if(!this.split){
36410             
36411             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36412                             tag: "div",
36413                             id: this.el.id + "-split",
36414                             cls: "roo-layout-split roo-layout-split-"+this.position,
36415                             html: "&#160;"
36416             });
36417             /** The SplitBar for this region 
36418             * @type Roo.SplitBar */
36419             // does not exist yet...
36420             Roo.log([this.position, this.orientation]);
36421             
36422             this.split = new Roo.bootstrap.SplitBar({
36423                 dragElement : splitEl,
36424                 resizingElement: this.el,
36425                 orientation : this.orientation
36426             });
36427             
36428             this.split.on("moved", this.onSplitMove, this);
36429             this.split.useShim = this.config.useShim === true;
36430             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36431             if(this.useSplitTips){
36432                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36433             }
36434             //if(config.collapsible){
36435             //    this.split.el.on("dblclick", this.collapse,  this);
36436             //}
36437         }
36438         if(typeof this.config.minSize != "undefined"){
36439             this.split.minSize = this.config.minSize;
36440         }
36441         if(typeof this.config.maxSize != "undefined"){
36442             this.split.maxSize = this.config.maxSize;
36443         }
36444         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36445             this.hideSplitter();
36446         }
36447         
36448     },
36449
36450     getHMaxSize : function(){
36451          var cmax = this.config.maxSize || 10000;
36452          var center = this.mgr.getRegion("center");
36453          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36454     },
36455
36456     getVMaxSize : function(){
36457          var cmax = this.config.maxSize || 10000;
36458          var center = this.mgr.getRegion("center");
36459          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36460     },
36461
36462     onSplitMove : function(split, newSize){
36463         this.fireEvent("resized", this, newSize);
36464     },
36465     
36466     /** 
36467      * Returns the {@link Roo.SplitBar} for this region.
36468      * @return {Roo.SplitBar}
36469      */
36470     getSplitBar : function(){
36471         return this.split;
36472     },
36473     
36474     hide : function(){
36475         this.hideSplitter();
36476         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36477     },
36478
36479     hideSplitter : function(){
36480         if(this.split){
36481             this.split.el.setLocation(-2000,-2000);
36482             this.split.el.hide();
36483         }
36484     },
36485
36486     show : function(){
36487         if(this.split){
36488             this.split.el.show();
36489         }
36490         Roo.bootstrap.layout.Split.superclass.show.call(this);
36491     },
36492     
36493     beforeSlide: function(){
36494         if(Roo.isGecko){// firefox overflow auto bug workaround
36495             this.bodyEl.clip();
36496             if(this.tabs) {
36497                 this.tabs.bodyEl.clip();
36498             }
36499             if(this.activePanel){
36500                 this.activePanel.getEl().clip();
36501                 
36502                 if(this.activePanel.beforeSlide){
36503                     this.activePanel.beforeSlide();
36504                 }
36505             }
36506         }
36507     },
36508     
36509     afterSlide : function(){
36510         if(Roo.isGecko){// firefox overflow auto bug workaround
36511             this.bodyEl.unclip();
36512             if(this.tabs) {
36513                 this.tabs.bodyEl.unclip();
36514             }
36515             if(this.activePanel){
36516                 this.activePanel.getEl().unclip();
36517                 if(this.activePanel.afterSlide){
36518                     this.activePanel.afterSlide();
36519                 }
36520             }
36521         }
36522     },
36523
36524     initAutoHide : function(){
36525         if(this.autoHide !== false){
36526             if(!this.autoHideHd){
36527                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36528                 this.autoHideHd = {
36529                     "mouseout": function(e){
36530                         if(!e.within(this.el, true)){
36531                             st.delay(500);
36532                         }
36533                     },
36534                     "mouseover" : function(e){
36535                         st.cancel();
36536                     },
36537                     scope : this
36538                 };
36539             }
36540             this.el.on(this.autoHideHd);
36541         }
36542     },
36543
36544     clearAutoHide : function(){
36545         if(this.autoHide !== false){
36546             this.el.un("mouseout", this.autoHideHd.mouseout);
36547             this.el.un("mouseover", this.autoHideHd.mouseover);
36548         }
36549     },
36550
36551     clearMonitor : function(){
36552         Roo.get(document).un("click", this.slideInIf, this);
36553     },
36554
36555     // these names are backwards but not changed for compat
36556     slideOut : function(){
36557         if(this.isSlid || this.el.hasActiveFx()){
36558             return;
36559         }
36560         this.isSlid = true;
36561         if(this.collapseBtn){
36562             this.collapseBtn.hide();
36563         }
36564         this.closeBtnState = this.closeBtn.getStyle('display');
36565         this.closeBtn.hide();
36566         if(this.stickBtn){
36567             this.stickBtn.show();
36568         }
36569         this.el.show();
36570         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36571         this.beforeSlide();
36572         this.el.setStyle("z-index", 10001);
36573         this.el.slideIn(this.getSlideAnchor(), {
36574             callback: function(){
36575                 this.afterSlide();
36576                 this.initAutoHide();
36577                 Roo.get(document).on("click", this.slideInIf, this);
36578                 this.fireEvent("slideshow", this);
36579             },
36580             scope: this,
36581             block: true
36582         });
36583     },
36584
36585     afterSlideIn : function(){
36586         this.clearAutoHide();
36587         this.isSlid = false;
36588         this.clearMonitor();
36589         this.el.setStyle("z-index", "");
36590         if(this.collapseBtn){
36591             this.collapseBtn.show();
36592         }
36593         this.closeBtn.setStyle('display', this.closeBtnState);
36594         if(this.stickBtn){
36595             this.stickBtn.hide();
36596         }
36597         this.fireEvent("slidehide", this);
36598     },
36599
36600     slideIn : function(cb){
36601         if(!this.isSlid || this.el.hasActiveFx()){
36602             Roo.callback(cb);
36603             return;
36604         }
36605         this.isSlid = false;
36606         this.beforeSlide();
36607         this.el.slideOut(this.getSlideAnchor(), {
36608             callback: function(){
36609                 this.el.setLeftTop(-10000, -10000);
36610                 this.afterSlide();
36611                 this.afterSlideIn();
36612                 Roo.callback(cb);
36613             },
36614             scope: this,
36615             block: true
36616         });
36617     },
36618     
36619     slideInIf : function(e){
36620         if(!e.within(this.el)){
36621             this.slideIn();
36622         }
36623     },
36624
36625     animateCollapse : function(){
36626         this.beforeSlide();
36627         this.el.setStyle("z-index", 20000);
36628         var anchor = this.getSlideAnchor();
36629         this.el.slideOut(anchor, {
36630             callback : function(){
36631                 this.el.setStyle("z-index", "");
36632                 this.collapsedEl.slideIn(anchor, {duration:.3});
36633                 this.afterSlide();
36634                 this.el.setLocation(-10000,-10000);
36635                 this.el.hide();
36636                 this.fireEvent("collapsed", this);
36637             },
36638             scope: this,
36639             block: true
36640         });
36641     },
36642
36643     animateExpand : function(){
36644         this.beforeSlide();
36645         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36646         this.el.setStyle("z-index", 20000);
36647         this.collapsedEl.hide({
36648             duration:.1
36649         });
36650         this.el.slideIn(this.getSlideAnchor(), {
36651             callback : function(){
36652                 this.el.setStyle("z-index", "");
36653                 this.afterSlide();
36654                 if(this.split){
36655                     this.split.el.show();
36656                 }
36657                 this.fireEvent("invalidated", this);
36658                 this.fireEvent("expanded", this);
36659             },
36660             scope: this,
36661             block: true
36662         });
36663     },
36664
36665     anchors : {
36666         "west" : "left",
36667         "east" : "right",
36668         "north" : "top",
36669         "south" : "bottom"
36670     },
36671
36672     sanchors : {
36673         "west" : "l",
36674         "east" : "r",
36675         "north" : "t",
36676         "south" : "b"
36677     },
36678
36679     canchors : {
36680         "west" : "tl-tr",
36681         "east" : "tr-tl",
36682         "north" : "tl-bl",
36683         "south" : "bl-tl"
36684     },
36685
36686     getAnchor : function(){
36687         return this.anchors[this.position];
36688     },
36689
36690     getCollapseAnchor : function(){
36691         return this.canchors[this.position];
36692     },
36693
36694     getSlideAnchor : function(){
36695         return this.sanchors[this.position];
36696     },
36697
36698     getAlignAdj : function(){
36699         var cm = this.cmargins;
36700         switch(this.position){
36701             case "west":
36702                 return [0, 0];
36703             break;
36704             case "east":
36705                 return [0, 0];
36706             break;
36707             case "north":
36708                 return [0, 0];
36709             break;
36710             case "south":
36711                 return [0, 0];
36712             break;
36713         }
36714     },
36715
36716     getExpandAdj : function(){
36717         var c = this.collapsedEl, cm = this.cmargins;
36718         switch(this.position){
36719             case "west":
36720                 return [-(cm.right+c.getWidth()+cm.left), 0];
36721             break;
36722             case "east":
36723                 return [cm.right+c.getWidth()+cm.left, 0];
36724             break;
36725             case "north":
36726                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36727             break;
36728             case "south":
36729                 return [0, cm.top+cm.bottom+c.getHeight()];
36730             break;
36731         }
36732     }
36733 });/*
36734  * Based on:
36735  * Ext JS Library 1.1.1
36736  * Copyright(c) 2006-2007, Ext JS, LLC.
36737  *
36738  * Originally Released Under LGPL - original licence link has changed is not relivant.
36739  *
36740  * Fork - LGPL
36741  * <script type="text/javascript">
36742  */
36743 /*
36744  * These classes are private internal classes
36745  */
36746 Roo.bootstrap.layout.Center = function(config){
36747     config.region = "center";
36748     Roo.bootstrap.layout.Region.call(this, config);
36749     this.visible = true;
36750     this.minWidth = config.minWidth || 20;
36751     this.minHeight = config.minHeight || 20;
36752 };
36753
36754 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36755     hide : function(){
36756         // center panel can't be hidden
36757     },
36758     
36759     show : function(){
36760         // center panel can't be hidden
36761     },
36762     
36763     getMinWidth: function(){
36764         return this.minWidth;
36765     },
36766     
36767     getMinHeight: function(){
36768         return this.minHeight;
36769     }
36770 });
36771
36772
36773
36774
36775  
36776
36777
36778
36779
36780
36781 Roo.bootstrap.layout.North = function(config)
36782 {
36783     config.region = 'north';
36784     config.cursor = 'n-resize';
36785     
36786     Roo.bootstrap.layout.Split.call(this, config);
36787     
36788     
36789     if(this.split){
36790         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36791         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36792         this.split.el.addClass("roo-layout-split-v");
36793     }
36794     var size = config.initialSize || config.height;
36795     if(typeof size != "undefined"){
36796         this.el.setHeight(size);
36797     }
36798 };
36799 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36800 {
36801     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36802     
36803     
36804     
36805     getBox : function(){
36806         if(this.collapsed){
36807             return this.collapsedEl.getBox();
36808         }
36809         var box = this.el.getBox();
36810         if(this.split){
36811             box.height += this.split.el.getHeight();
36812         }
36813         return box;
36814     },
36815     
36816     updateBox : function(box){
36817         if(this.split && !this.collapsed){
36818             box.height -= this.split.el.getHeight();
36819             this.split.el.setLeft(box.x);
36820             this.split.el.setTop(box.y+box.height);
36821             this.split.el.setWidth(box.width);
36822         }
36823         if(this.collapsed){
36824             this.updateBody(box.width, null);
36825         }
36826         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36827     }
36828 });
36829
36830
36831
36832
36833
36834 Roo.bootstrap.layout.South = function(config){
36835     config.region = 'south';
36836     config.cursor = 's-resize';
36837     Roo.bootstrap.layout.Split.call(this, config);
36838     if(this.split){
36839         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36840         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36841         this.split.el.addClass("roo-layout-split-v");
36842     }
36843     var size = config.initialSize || config.height;
36844     if(typeof size != "undefined"){
36845         this.el.setHeight(size);
36846     }
36847 };
36848
36849 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36850     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36851     getBox : function(){
36852         if(this.collapsed){
36853             return this.collapsedEl.getBox();
36854         }
36855         var box = this.el.getBox();
36856         if(this.split){
36857             var sh = this.split.el.getHeight();
36858             box.height += sh;
36859             box.y -= sh;
36860         }
36861         return box;
36862     },
36863     
36864     updateBox : function(box){
36865         if(this.split && !this.collapsed){
36866             var sh = this.split.el.getHeight();
36867             box.height -= sh;
36868             box.y += sh;
36869             this.split.el.setLeft(box.x);
36870             this.split.el.setTop(box.y-sh);
36871             this.split.el.setWidth(box.width);
36872         }
36873         if(this.collapsed){
36874             this.updateBody(box.width, null);
36875         }
36876         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36877     }
36878 });
36879
36880 Roo.bootstrap.layout.East = function(config){
36881     config.region = "east";
36882     config.cursor = "e-resize";
36883     Roo.bootstrap.layout.Split.call(this, config);
36884     if(this.split){
36885         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36886         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36887         this.split.el.addClass("roo-layout-split-h");
36888     }
36889     var size = config.initialSize || config.width;
36890     if(typeof size != "undefined"){
36891         this.el.setWidth(size);
36892     }
36893 };
36894 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36895     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36896     getBox : function(){
36897         if(this.collapsed){
36898             return this.collapsedEl.getBox();
36899         }
36900         var box = this.el.getBox();
36901         if(this.split){
36902             var sw = this.split.el.getWidth();
36903             box.width += sw;
36904             box.x -= sw;
36905         }
36906         return box;
36907     },
36908
36909     updateBox : function(box){
36910         if(this.split && !this.collapsed){
36911             var sw = this.split.el.getWidth();
36912             box.width -= sw;
36913             this.split.el.setLeft(box.x);
36914             this.split.el.setTop(box.y);
36915             this.split.el.setHeight(box.height);
36916             box.x += sw;
36917         }
36918         if(this.collapsed){
36919             this.updateBody(null, box.height);
36920         }
36921         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36922     }
36923 });
36924
36925 Roo.bootstrap.layout.West = function(config){
36926     config.region = "west";
36927     config.cursor = "w-resize";
36928     
36929     Roo.bootstrap.layout.Split.call(this, config);
36930     if(this.split){
36931         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36932         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36933         this.split.el.addClass("roo-layout-split-h");
36934     }
36935     
36936 };
36937 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36938     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36939     
36940     onRender: function(ctr, pos)
36941     {
36942         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36943         var size = this.config.initialSize || this.config.width;
36944         if(typeof size != "undefined"){
36945             this.el.setWidth(size);
36946         }
36947     },
36948     
36949     getBox : function(){
36950         if(this.collapsed){
36951             return this.collapsedEl.getBox();
36952         }
36953         var box = this.el.getBox();
36954         if(this.split){
36955             box.width += this.split.el.getWidth();
36956         }
36957         return box;
36958     },
36959     
36960     updateBox : function(box){
36961         if(this.split && !this.collapsed){
36962             var sw = this.split.el.getWidth();
36963             box.width -= sw;
36964             this.split.el.setLeft(box.x+box.width);
36965             this.split.el.setTop(box.y);
36966             this.split.el.setHeight(box.height);
36967         }
36968         if(this.collapsed){
36969             this.updateBody(null, box.height);
36970         }
36971         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36972     }
36973 });
36974 Roo.namespace("Roo.bootstrap.panel");/*
36975  * Based on:
36976  * Ext JS Library 1.1.1
36977  * Copyright(c) 2006-2007, Ext JS, LLC.
36978  *
36979  * Originally Released Under LGPL - original licence link has changed is not relivant.
36980  *
36981  * Fork - LGPL
36982  * <script type="text/javascript">
36983  */
36984 /**
36985  * @class Roo.ContentPanel
36986  * @extends Roo.util.Observable
36987  * A basic ContentPanel element.
36988  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36989  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36990  * @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
36991  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36992  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36993  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36994  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36995  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36996  * @cfg {String} title          The title for this panel
36997  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36998  * @cfg {String} url            Calls {@link #setUrl} with this value
36999  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37000  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37001  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37002  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37003  * @cfg {Boolean} badges render the badges
37004
37005  * @constructor
37006  * Create a new ContentPanel.
37007  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37008  * @param {String/Object} config A string to set only the title or a config object
37009  * @param {String} content (optional) Set the HTML content for this panel
37010  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37011  */
37012 Roo.bootstrap.panel.Content = function( config){
37013     
37014     this.tpl = config.tpl || false;
37015     
37016     var el = config.el;
37017     var content = config.content;
37018
37019     if(config.autoCreate){ // xtype is available if this is called from factory
37020         el = Roo.id();
37021     }
37022     this.el = Roo.get(el);
37023     if(!this.el && config && config.autoCreate){
37024         if(typeof config.autoCreate == "object"){
37025             if(!config.autoCreate.id){
37026                 config.autoCreate.id = config.id||el;
37027             }
37028             this.el = Roo.DomHelper.append(document.body,
37029                         config.autoCreate, true);
37030         }else{
37031             var elcfg =  {   tag: "div",
37032                             cls: "roo-layout-inactive-content",
37033                             id: config.id||el
37034                             };
37035             if (config.html) {
37036                 elcfg.html = config.html;
37037                 
37038             }
37039                         
37040             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37041         }
37042     } 
37043     this.closable = false;
37044     this.loaded = false;
37045     this.active = false;
37046    
37047       
37048     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37049         
37050         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37051         
37052         this.wrapEl = this.el; //this.el.wrap();
37053         var ti = [];
37054         if (config.toolbar.items) {
37055             ti = config.toolbar.items ;
37056             delete config.toolbar.items ;
37057         }
37058         
37059         var nitems = [];
37060         this.toolbar.render(this.wrapEl, 'before');
37061         for(var i =0;i < ti.length;i++) {
37062           //  Roo.log(['add child', items[i]]);
37063             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37064         }
37065         this.toolbar.items = nitems;
37066         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37067         delete config.toolbar;
37068         
37069     }
37070     /*
37071     // xtype created footer. - not sure if will work as we normally have to render first..
37072     if (this.footer && !this.footer.el && this.footer.xtype) {
37073         if (!this.wrapEl) {
37074             this.wrapEl = this.el.wrap();
37075         }
37076     
37077         this.footer.container = this.wrapEl.createChild();
37078          
37079         this.footer = Roo.factory(this.footer, Roo);
37080         
37081     }
37082     */
37083     
37084      if(typeof config == "string"){
37085         this.title = config;
37086     }else{
37087         Roo.apply(this, config);
37088     }
37089     
37090     if(this.resizeEl){
37091         this.resizeEl = Roo.get(this.resizeEl, true);
37092     }else{
37093         this.resizeEl = this.el;
37094     }
37095     // handle view.xtype
37096     
37097  
37098     
37099     
37100     this.addEvents({
37101         /**
37102          * @event activate
37103          * Fires when this panel is activated. 
37104          * @param {Roo.ContentPanel} this
37105          */
37106         "activate" : true,
37107         /**
37108          * @event deactivate
37109          * Fires when this panel is activated. 
37110          * @param {Roo.ContentPanel} this
37111          */
37112         "deactivate" : true,
37113
37114         /**
37115          * @event resize
37116          * Fires when this panel is resized if fitToFrame is true.
37117          * @param {Roo.ContentPanel} this
37118          * @param {Number} width The width after any component adjustments
37119          * @param {Number} height The height after any component adjustments
37120          */
37121         "resize" : true,
37122         
37123          /**
37124          * @event render
37125          * Fires when this tab is created
37126          * @param {Roo.ContentPanel} this
37127          */
37128         "render" : true
37129         
37130         
37131         
37132     });
37133     
37134
37135     
37136     
37137     if(this.autoScroll){
37138         this.resizeEl.setStyle("overflow", "auto");
37139     } else {
37140         // fix randome scrolling
37141         //this.el.on('scroll', function() {
37142         //    Roo.log('fix random scolling');
37143         //    this.scrollTo('top',0); 
37144         //});
37145     }
37146     content = content || this.content;
37147     if(content){
37148         this.setContent(content);
37149     }
37150     if(config && config.url){
37151         this.setUrl(this.url, this.params, this.loadOnce);
37152     }
37153     
37154     
37155     
37156     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37157     
37158     if (this.view && typeof(this.view.xtype) != 'undefined') {
37159         this.view.el = this.el.appendChild(document.createElement("div"));
37160         this.view = Roo.factory(this.view); 
37161         this.view.render  &&  this.view.render(false, '');  
37162     }
37163     
37164     
37165     this.fireEvent('render', this);
37166 };
37167
37168 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37169     
37170     tabTip : '',
37171     
37172     setRegion : function(region){
37173         this.region = region;
37174         this.setActiveClass(region && !this.background);
37175     },
37176     
37177     
37178     setActiveClass: function(state)
37179     {
37180         if(state){
37181            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37182            this.el.setStyle('position','relative');
37183         }else{
37184            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37185            this.el.setStyle('position', 'absolute');
37186         } 
37187     },
37188     
37189     /**
37190      * Returns the toolbar for this Panel if one was configured. 
37191      * @return {Roo.Toolbar} 
37192      */
37193     getToolbar : function(){
37194         return this.toolbar;
37195     },
37196     
37197     setActiveState : function(active)
37198     {
37199         this.active = active;
37200         this.setActiveClass(active);
37201         if(!active){
37202             if(this.fireEvent("deactivate", this) === false){
37203                 return false;
37204             }
37205             return true;
37206         }
37207         this.fireEvent("activate", this);
37208         return true;
37209     },
37210     /**
37211      * Updates this panel's element
37212      * @param {String} content The new content
37213      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37214     */
37215     setContent : function(content, loadScripts){
37216         this.el.update(content, loadScripts);
37217     },
37218
37219     ignoreResize : function(w, h){
37220         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37221             return true;
37222         }else{
37223             this.lastSize = {width: w, height: h};
37224             return false;
37225         }
37226     },
37227     /**
37228      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37229      * @return {Roo.UpdateManager} The UpdateManager
37230      */
37231     getUpdateManager : function(){
37232         return this.el.getUpdateManager();
37233     },
37234      /**
37235      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37236      * @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:
37237 <pre><code>
37238 panel.load({
37239     url: "your-url.php",
37240     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37241     callback: yourFunction,
37242     scope: yourObject, //(optional scope)
37243     discardUrl: false,
37244     nocache: false,
37245     text: "Loading...",
37246     timeout: 30,
37247     scripts: false
37248 });
37249 </code></pre>
37250      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37251      * 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.
37252      * @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}
37253      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37254      * @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.
37255      * @return {Roo.ContentPanel} this
37256      */
37257     load : function(){
37258         var um = this.el.getUpdateManager();
37259         um.update.apply(um, arguments);
37260         return this;
37261     },
37262
37263
37264     /**
37265      * 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.
37266      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37267      * @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)
37268      * @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)
37269      * @return {Roo.UpdateManager} The UpdateManager
37270      */
37271     setUrl : function(url, params, loadOnce){
37272         if(this.refreshDelegate){
37273             this.removeListener("activate", this.refreshDelegate);
37274         }
37275         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37276         this.on("activate", this.refreshDelegate);
37277         return this.el.getUpdateManager();
37278     },
37279     
37280     _handleRefresh : function(url, params, loadOnce){
37281         if(!loadOnce || !this.loaded){
37282             var updater = this.el.getUpdateManager();
37283             updater.update(url, params, this._setLoaded.createDelegate(this));
37284         }
37285     },
37286     
37287     _setLoaded : function(){
37288         this.loaded = true;
37289     }, 
37290     
37291     /**
37292      * Returns this panel's id
37293      * @return {String} 
37294      */
37295     getId : function(){
37296         return this.el.id;
37297     },
37298     
37299     /** 
37300      * Returns this panel's element - used by regiosn to add.
37301      * @return {Roo.Element} 
37302      */
37303     getEl : function(){
37304         return this.wrapEl || this.el;
37305     },
37306     
37307    
37308     
37309     adjustForComponents : function(width, height)
37310     {
37311         //Roo.log('adjustForComponents ');
37312         if(this.resizeEl != this.el){
37313             width -= this.el.getFrameWidth('lr');
37314             height -= this.el.getFrameWidth('tb');
37315         }
37316         if(this.toolbar){
37317             var te = this.toolbar.getEl();
37318             te.setWidth(width);
37319             height -= te.getHeight();
37320         }
37321         if(this.footer){
37322             var te = this.footer.getEl();
37323             te.setWidth(width);
37324             height -= te.getHeight();
37325         }
37326         
37327         
37328         if(this.adjustments){
37329             width += this.adjustments[0];
37330             height += this.adjustments[1];
37331         }
37332         return {"width": width, "height": height};
37333     },
37334     
37335     setSize : function(width, height){
37336         if(this.fitToFrame && !this.ignoreResize(width, height)){
37337             if(this.fitContainer && this.resizeEl != this.el){
37338                 this.el.setSize(width, height);
37339             }
37340             var size = this.adjustForComponents(width, height);
37341             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37342             this.fireEvent('resize', this, size.width, size.height);
37343         }
37344     },
37345     
37346     /**
37347      * Returns this panel's title
37348      * @return {String} 
37349      */
37350     getTitle : function(){
37351         
37352         if (typeof(this.title) != 'object') {
37353             return this.title;
37354         }
37355         
37356         var t = '';
37357         for (var k in this.title) {
37358             if (!this.title.hasOwnProperty(k)) {
37359                 continue;
37360             }
37361             
37362             if (k.indexOf('-') >= 0) {
37363                 var s = k.split('-');
37364                 for (var i = 0; i<s.length; i++) {
37365                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37366                 }
37367             } else {
37368                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37369             }
37370         }
37371         return t;
37372     },
37373     
37374     /**
37375      * Set this panel's title
37376      * @param {String} title
37377      */
37378     setTitle : function(title){
37379         this.title = title;
37380         if(this.region){
37381             this.region.updatePanelTitle(this, title);
37382         }
37383     },
37384     
37385     /**
37386      * Returns true is this panel was configured to be closable
37387      * @return {Boolean} 
37388      */
37389     isClosable : function(){
37390         return this.closable;
37391     },
37392     
37393     beforeSlide : function(){
37394         this.el.clip();
37395         this.resizeEl.clip();
37396     },
37397     
37398     afterSlide : function(){
37399         this.el.unclip();
37400         this.resizeEl.unclip();
37401     },
37402     
37403     /**
37404      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37405      *   Will fail silently if the {@link #setUrl} method has not been called.
37406      *   This does not activate the panel, just updates its content.
37407      */
37408     refresh : function(){
37409         if(this.refreshDelegate){
37410            this.loaded = false;
37411            this.refreshDelegate();
37412         }
37413     },
37414     
37415     /**
37416      * Destroys this panel
37417      */
37418     destroy : function(){
37419         this.el.removeAllListeners();
37420         var tempEl = document.createElement("span");
37421         tempEl.appendChild(this.el.dom);
37422         tempEl.innerHTML = "";
37423         this.el.remove();
37424         this.el = null;
37425     },
37426     
37427     /**
37428      * form - if the content panel contains a form - this is a reference to it.
37429      * @type {Roo.form.Form}
37430      */
37431     form : false,
37432     /**
37433      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37434      *    This contains a reference to it.
37435      * @type {Roo.View}
37436      */
37437     view : false,
37438     
37439       /**
37440      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37441      * <pre><code>
37442
37443 layout.addxtype({
37444        xtype : 'Form',
37445        items: [ .... ]
37446    }
37447 );
37448
37449 </code></pre>
37450      * @param {Object} cfg Xtype definition of item to add.
37451      */
37452     
37453     
37454     getChildContainer: function () {
37455         return this.getEl();
37456     }
37457     
37458     
37459     /*
37460         var  ret = new Roo.factory(cfg);
37461         return ret;
37462         
37463         
37464         // add form..
37465         if (cfg.xtype.match(/^Form$/)) {
37466             
37467             var el;
37468             //if (this.footer) {
37469             //    el = this.footer.container.insertSibling(false, 'before');
37470             //} else {
37471                 el = this.el.createChild();
37472             //}
37473
37474             this.form = new  Roo.form.Form(cfg);
37475             
37476             
37477             if ( this.form.allItems.length) {
37478                 this.form.render(el.dom);
37479             }
37480             return this.form;
37481         }
37482         // should only have one of theses..
37483         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37484             // views.. should not be just added - used named prop 'view''
37485             
37486             cfg.el = this.el.appendChild(document.createElement("div"));
37487             // factory?
37488             
37489             var ret = new Roo.factory(cfg);
37490              
37491              ret.render && ret.render(false, ''); // render blank..
37492             this.view = ret;
37493             return ret;
37494         }
37495         return false;
37496     }
37497     \*/
37498 });
37499  
37500 /**
37501  * @class Roo.bootstrap.panel.Grid
37502  * @extends Roo.bootstrap.panel.Content
37503  * @constructor
37504  * Create a new GridPanel.
37505  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37506  * @param {Object} config A the config object
37507   
37508  */
37509
37510
37511
37512 Roo.bootstrap.panel.Grid = function(config)
37513 {
37514     
37515       
37516     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37517         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37518
37519     config.el = this.wrapper;
37520     //this.el = this.wrapper;
37521     
37522       if (config.container) {
37523         // ctor'ed from a Border/panel.grid
37524         
37525         
37526         this.wrapper.setStyle("overflow", "hidden");
37527         this.wrapper.addClass('roo-grid-container');
37528
37529     }
37530     
37531     
37532     if(config.toolbar){
37533         var tool_el = this.wrapper.createChild();    
37534         this.toolbar = Roo.factory(config.toolbar);
37535         var ti = [];
37536         if (config.toolbar.items) {
37537             ti = config.toolbar.items ;
37538             delete config.toolbar.items ;
37539         }
37540         
37541         var nitems = [];
37542         this.toolbar.render(tool_el);
37543         for(var i =0;i < ti.length;i++) {
37544           //  Roo.log(['add child', items[i]]);
37545             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37546         }
37547         this.toolbar.items = nitems;
37548         
37549         delete config.toolbar;
37550     }
37551     
37552     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37553     config.grid.scrollBody = true;;
37554     config.grid.monitorWindowResize = false; // turn off autosizing
37555     config.grid.autoHeight = false;
37556     config.grid.autoWidth = false;
37557     
37558     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37559     
37560     if (config.background) {
37561         // render grid on panel activation (if panel background)
37562         this.on('activate', function(gp) {
37563             if (!gp.grid.rendered) {
37564                 gp.grid.render(this.wrapper);
37565                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37566             }
37567         });
37568             
37569     } else {
37570         this.grid.render(this.wrapper);
37571         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37572
37573     }
37574     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37575     // ??? needed ??? config.el = this.wrapper;
37576     
37577     
37578     
37579   
37580     // xtype created footer. - not sure if will work as we normally have to render first..
37581     if (this.footer && !this.footer.el && this.footer.xtype) {
37582         
37583         var ctr = this.grid.getView().getFooterPanel(true);
37584         this.footer.dataSource = this.grid.dataSource;
37585         this.footer = Roo.factory(this.footer, Roo);
37586         this.footer.render(ctr);
37587         
37588     }
37589     
37590     
37591     
37592     
37593      
37594 };
37595
37596 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37597     getId : function(){
37598         return this.grid.id;
37599     },
37600     
37601     /**
37602      * Returns the grid for this panel
37603      * @return {Roo.bootstrap.Table} 
37604      */
37605     getGrid : function(){
37606         return this.grid;    
37607     },
37608     
37609     setSize : function(width, height){
37610         if(!this.ignoreResize(width, height)){
37611             var grid = this.grid;
37612             var size = this.adjustForComponents(width, height);
37613             var gridel = grid.getGridEl();
37614             gridel.setSize(size.width, size.height);
37615             /*
37616             var thd = grid.getGridEl().select('thead',true).first();
37617             var tbd = grid.getGridEl().select('tbody', true).first();
37618             if (tbd) {
37619                 tbd.setSize(width, height - thd.getHeight());
37620             }
37621             */
37622             grid.autoSize();
37623         }
37624     },
37625      
37626     
37627     
37628     beforeSlide : function(){
37629         this.grid.getView().scroller.clip();
37630     },
37631     
37632     afterSlide : function(){
37633         this.grid.getView().scroller.unclip();
37634     },
37635     
37636     destroy : function(){
37637         this.grid.destroy();
37638         delete this.grid;
37639         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37640     }
37641 });
37642
37643 /**
37644  * @class Roo.bootstrap.panel.Nest
37645  * @extends Roo.bootstrap.panel.Content
37646  * @constructor
37647  * Create a new Panel, that can contain a layout.Border.
37648  * 
37649  * 
37650  * @param {Roo.BorderLayout} layout The layout for this panel
37651  * @param {String/Object} config A string to set only the title or a config object
37652  */
37653 Roo.bootstrap.panel.Nest = function(config)
37654 {
37655     // construct with only one argument..
37656     /* FIXME - implement nicer consturctors
37657     if (layout.layout) {
37658         config = layout;
37659         layout = config.layout;
37660         delete config.layout;
37661     }
37662     if (layout.xtype && !layout.getEl) {
37663         // then layout needs constructing..
37664         layout = Roo.factory(layout, Roo);
37665     }
37666     */
37667     
37668     config.el =  config.layout.getEl();
37669     
37670     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37671     
37672     config.layout.monitorWindowResize = false; // turn off autosizing
37673     this.layout = config.layout;
37674     this.layout.getEl().addClass("roo-layout-nested-layout");
37675     
37676     
37677     
37678     
37679 };
37680
37681 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37682
37683     setSize : function(width, height){
37684         if(!this.ignoreResize(width, height)){
37685             var size = this.adjustForComponents(width, height);
37686             var el = this.layout.getEl();
37687             if (size.height < 1) {
37688                 el.setWidth(size.width);   
37689             } else {
37690                 el.setSize(size.width, size.height);
37691             }
37692             var touch = el.dom.offsetWidth;
37693             this.layout.layout();
37694             // ie requires a double layout on the first pass
37695             if(Roo.isIE && !this.initialized){
37696                 this.initialized = true;
37697                 this.layout.layout();
37698             }
37699         }
37700     },
37701     
37702     // activate all subpanels if not currently active..
37703     
37704     setActiveState : function(active){
37705         this.active = active;
37706         this.setActiveClass(active);
37707         
37708         if(!active){
37709             this.fireEvent("deactivate", this);
37710             return;
37711         }
37712         
37713         this.fireEvent("activate", this);
37714         // not sure if this should happen before or after..
37715         if (!this.layout) {
37716             return; // should not happen..
37717         }
37718         var reg = false;
37719         for (var r in this.layout.regions) {
37720             reg = this.layout.getRegion(r);
37721             if (reg.getActivePanel()) {
37722                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37723                 reg.setActivePanel(reg.getActivePanel());
37724                 continue;
37725             }
37726             if (!reg.panels.length) {
37727                 continue;
37728             }
37729             reg.showPanel(reg.getPanel(0));
37730         }
37731         
37732         
37733         
37734         
37735     },
37736     
37737     /**
37738      * Returns the nested BorderLayout for this panel
37739      * @return {Roo.BorderLayout} 
37740      */
37741     getLayout : function(){
37742         return this.layout;
37743     },
37744     
37745      /**
37746      * Adds a xtype elements to the layout of the nested panel
37747      * <pre><code>
37748
37749 panel.addxtype({
37750        xtype : 'ContentPanel',
37751        region: 'west',
37752        items: [ .... ]
37753    }
37754 );
37755
37756 panel.addxtype({
37757         xtype : 'NestedLayoutPanel',
37758         region: 'west',
37759         layout: {
37760            center: { },
37761            west: { }   
37762         },
37763         items : [ ... list of content panels or nested layout panels.. ]
37764    }
37765 );
37766 </code></pre>
37767      * @param {Object} cfg Xtype definition of item to add.
37768      */
37769     addxtype : function(cfg) {
37770         return this.layout.addxtype(cfg);
37771     
37772     }
37773 });        /*
37774  * Based on:
37775  * Ext JS Library 1.1.1
37776  * Copyright(c) 2006-2007, Ext JS, LLC.
37777  *
37778  * Originally Released Under LGPL - original licence link has changed is not relivant.
37779  *
37780  * Fork - LGPL
37781  * <script type="text/javascript">
37782  */
37783 /**
37784  * @class Roo.TabPanel
37785  * @extends Roo.util.Observable
37786  * A lightweight tab container.
37787  * <br><br>
37788  * Usage:
37789  * <pre><code>
37790 // basic tabs 1, built from existing content
37791 var tabs = new Roo.TabPanel("tabs1");
37792 tabs.addTab("script", "View Script");
37793 tabs.addTab("markup", "View Markup");
37794 tabs.activate("script");
37795
37796 // more advanced tabs, built from javascript
37797 var jtabs = new Roo.TabPanel("jtabs");
37798 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37799
37800 // set up the UpdateManager
37801 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37802 var updater = tab2.getUpdateManager();
37803 updater.setDefaultUrl("ajax1.htm");
37804 tab2.on('activate', updater.refresh, updater, true);
37805
37806 // Use setUrl for Ajax loading
37807 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37808 tab3.setUrl("ajax2.htm", null, true);
37809
37810 // Disabled tab
37811 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37812 tab4.disable();
37813
37814 jtabs.activate("jtabs-1");
37815  * </code></pre>
37816  * @constructor
37817  * Create a new TabPanel.
37818  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37819  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37820  */
37821 Roo.bootstrap.panel.Tabs = function(config){
37822     /**
37823     * The container element for this TabPanel.
37824     * @type Roo.Element
37825     */
37826     this.el = Roo.get(config.el);
37827     delete config.el;
37828     if(config){
37829         if(typeof config == "boolean"){
37830             this.tabPosition = config ? "bottom" : "top";
37831         }else{
37832             Roo.apply(this, config);
37833         }
37834     }
37835     
37836     if(this.tabPosition == "bottom"){
37837         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37838         this.el.addClass("roo-tabs-bottom");
37839     }
37840     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37841     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37842     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37843     if(Roo.isIE){
37844         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37845     }
37846     if(this.tabPosition != "bottom"){
37847         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37848          * @type Roo.Element
37849          */
37850         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37851         this.el.addClass("roo-tabs-top");
37852     }
37853     this.items = [];
37854
37855     this.bodyEl.setStyle("position", "relative");
37856
37857     this.active = null;
37858     this.activateDelegate = this.activate.createDelegate(this);
37859
37860     this.addEvents({
37861         /**
37862          * @event tabchange
37863          * Fires when the active tab changes
37864          * @param {Roo.TabPanel} this
37865          * @param {Roo.TabPanelItem} activePanel The new active tab
37866          */
37867         "tabchange": true,
37868         /**
37869          * @event beforetabchange
37870          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37871          * @param {Roo.TabPanel} this
37872          * @param {Object} e Set cancel to true on this object to cancel the tab change
37873          * @param {Roo.TabPanelItem} tab The tab being changed to
37874          */
37875         "beforetabchange" : true
37876     });
37877
37878     Roo.EventManager.onWindowResize(this.onResize, this);
37879     this.cpad = this.el.getPadding("lr");
37880     this.hiddenCount = 0;
37881
37882
37883     // toolbar on the tabbar support...
37884     if (this.toolbar) {
37885         alert("no toolbar support yet");
37886         this.toolbar  = false;
37887         /*
37888         var tcfg = this.toolbar;
37889         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37890         this.toolbar = new Roo.Toolbar(tcfg);
37891         if (Roo.isSafari) {
37892             var tbl = tcfg.container.child('table', true);
37893             tbl.setAttribute('width', '100%');
37894         }
37895         */
37896         
37897     }
37898    
37899
37900
37901     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37902 };
37903
37904 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37905     /*
37906      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37907      */
37908     tabPosition : "top",
37909     /*
37910      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37911      */
37912     currentTabWidth : 0,
37913     /*
37914      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37915      */
37916     minTabWidth : 40,
37917     /*
37918      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37919      */
37920     maxTabWidth : 250,
37921     /*
37922      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37923      */
37924     preferredTabWidth : 175,
37925     /*
37926      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37927      */
37928     resizeTabs : false,
37929     /*
37930      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37931      */
37932     monitorResize : true,
37933     /*
37934      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37935      */
37936     toolbar : false,
37937
37938     /**
37939      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37940      * @param {String} id The id of the div to use <b>or create</b>
37941      * @param {String} text The text for the tab
37942      * @param {String} content (optional) Content to put in the TabPanelItem body
37943      * @param {Boolean} closable (optional) True to create a close icon on the tab
37944      * @return {Roo.TabPanelItem} The created TabPanelItem
37945      */
37946     addTab : function(id, text, content, closable, tpl)
37947     {
37948         var item = new Roo.bootstrap.panel.TabItem({
37949             panel: this,
37950             id : id,
37951             text : text,
37952             closable : closable,
37953             tpl : tpl
37954         });
37955         this.addTabItem(item);
37956         if(content){
37957             item.setContent(content);
37958         }
37959         return item;
37960     },
37961
37962     /**
37963      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37964      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37965      * @return {Roo.TabPanelItem}
37966      */
37967     getTab : function(id){
37968         return this.items[id];
37969     },
37970
37971     /**
37972      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37973      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37974      */
37975     hideTab : function(id){
37976         var t = this.items[id];
37977         if(!t.isHidden()){
37978            t.setHidden(true);
37979            this.hiddenCount++;
37980            this.autoSizeTabs();
37981         }
37982     },
37983
37984     /**
37985      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37986      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37987      */
37988     unhideTab : function(id){
37989         var t = this.items[id];
37990         if(t.isHidden()){
37991            t.setHidden(false);
37992            this.hiddenCount--;
37993            this.autoSizeTabs();
37994         }
37995     },
37996
37997     /**
37998      * Adds an existing {@link Roo.TabPanelItem}.
37999      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38000      */
38001     addTabItem : function(item){
38002         this.items[item.id] = item;
38003         this.items.push(item);
38004       //  if(this.resizeTabs){
38005     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38006   //         this.autoSizeTabs();
38007 //        }else{
38008 //            item.autoSize();
38009        // }
38010     },
38011
38012     /**
38013      * Removes a {@link Roo.TabPanelItem}.
38014      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38015      */
38016     removeTab : function(id){
38017         var items = this.items;
38018         var tab = items[id];
38019         if(!tab) { return; }
38020         var index = items.indexOf(tab);
38021         if(this.active == tab && items.length > 1){
38022             var newTab = this.getNextAvailable(index);
38023             if(newTab) {
38024                 newTab.activate();
38025             }
38026         }
38027         this.stripEl.dom.removeChild(tab.pnode.dom);
38028         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38029             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38030         }
38031         items.splice(index, 1);
38032         delete this.items[tab.id];
38033         tab.fireEvent("close", tab);
38034         tab.purgeListeners();
38035         this.autoSizeTabs();
38036     },
38037
38038     getNextAvailable : function(start){
38039         var items = this.items;
38040         var index = start;
38041         // look for a next tab that will slide over to
38042         // replace the one being removed
38043         while(index < items.length){
38044             var item = items[++index];
38045             if(item && !item.isHidden()){
38046                 return item;
38047             }
38048         }
38049         // if one isn't found select the previous tab (on the left)
38050         index = start;
38051         while(index >= 0){
38052             var item = items[--index];
38053             if(item && !item.isHidden()){
38054                 return item;
38055             }
38056         }
38057         return null;
38058     },
38059
38060     /**
38061      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38062      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38063      */
38064     disableTab : function(id){
38065         var tab = this.items[id];
38066         if(tab && this.active != tab){
38067             tab.disable();
38068         }
38069     },
38070
38071     /**
38072      * Enables a {@link Roo.TabPanelItem} that is disabled.
38073      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38074      */
38075     enableTab : function(id){
38076         var tab = this.items[id];
38077         tab.enable();
38078     },
38079
38080     /**
38081      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38082      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38083      * @return {Roo.TabPanelItem} The TabPanelItem.
38084      */
38085     activate : function(id){
38086         var tab = this.items[id];
38087         if(!tab){
38088             return null;
38089         }
38090         if(tab == this.active || tab.disabled){
38091             return tab;
38092         }
38093         var e = {};
38094         this.fireEvent("beforetabchange", this, e, tab);
38095         if(e.cancel !== true && !tab.disabled){
38096             if(this.active){
38097                 this.active.hide();
38098             }
38099             this.active = this.items[id];
38100             this.active.show();
38101             this.fireEvent("tabchange", this, this.active);
38102         }
38103         return tab;
38104     },
38105
38106     /**
38107      * Gets the active {@link Roo.TabPanelItem}.
38108      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38109      */
38110     getActiveTab : function(){
38111         return this.active;
38112     },
38113
38114     /**
38115      * Updates the tab body element to fit the height of the container element
38116      * for overflow scrolling
38117      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38118      */
38119     syncHeight : function(targetHeight){
38120         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38121         var bm = this.bodyEl.getMargins();
38122         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38123         this.bodyEl.setHeight(newHeight);
38124         return newHeight;
38125     },
38126
38127     onResize : function(){
38128         if(this.monitorResize){
38129             this.autoSizeTabs();
38130         }
38131     },
38132
38133     /**
38134      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38135      */
38136     beginUpdate : function(){
38137         this.updating = true;
38138     },
38139
38140     /**
38141      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38142      */
38143     endUpdate : function(){
38144         this.updating = false;
38145         this.autoSizeTabs();
38146     },
38147
38148     /**
38149      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38150      */
38151     autoSizeTabs : function(){
38152         var count = this.items.length;
38153         var vcount = count - this.hiddenCount;
38154         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38155             return;
38156         }
38157         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38158         var availWidth = Math.floor(w / vcount);
38159         var b = this.stripBody;
38160         if(b.getWidth() > w){
38161             var tabs = this.items;
38162             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38163             if(availWidth < this.minTabWidth){
38164                 /*if(!this.sleft){    // incomplete scrolling code
38165                     this.createScrollButtons();
38166                 }
38167                 this.showScroll();
38168                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38169             }
38170         }else{
38171             if(this.currentTabWidth < this.preferredTabWidth){
38172                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38173             }
38174         }
38175     },
38176
38177     /**
38178      * Returns the number of tabs in this TabPanel.
38179      * @return {Number}
38180      */
38181      getCount : function(){
38182          return this.items.length;
38183      },
38184
38185     /**
38186      * Resizes all the tabs to the passed width
38187      * @param {Number} The new width
38188      */
38189     setTabWidth : function(width){
38190         this.currentTabWidth = width;
38191         for(var i = 0, len = this.items.length; i < len; i++) {
38192                 if(!this.items[i].isHidden()) {
38193                 this.items[i].setWidth(width);
38194             }
38195         }
38196     },
38197
38198     /**
38199      * Destroys this TabPanel
38200      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38201      */
38202     destroy : function(removeEl){
38203         Roo.EventManager.removeResizeListener(this.onResize, this);
38204         for(var i = 0, len = this.items.length; i < len; i++){
38205             this.items[i].purgeListeners();
38206         }
38207         if(removeEl === true){
38208             this.el.update("");
38209             this.el.remove();
38210         }
38211     },
38212     
38213     createStrip : function(container)
38214     {
38215         var strip = document.createElement("nav");
38216         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38217         container.appendChild(strip);
38218         return strip;
38219     },
38220     
38221     createStripList : function(strip)
38222     {
38223         // div wrapper for retard IE
38224         // returns the "tr" element.
38225         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38226         //'<div class="x-tabs-strip-wrap">'+
38227           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38228           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38229         return strip.firstChild; //.firstChild.firstChild.firstChild;
38230     },
38231     createBody : function(container)
38232     {
38233         var body = document.createElement("div");
38234         Roo.id(body, "tab-body");
38235         //Roo.fly(body).addClass("x-tabs-body");
38236         Roo.fly(body).addClass("tab-content");
38237         container.appendChild(body);
38238         return body;
38239     },
38240     createItemBody :function(bodyEl, id){
38241         var body = Roo.getDom(id);
38242         if(!body){
38243             body = document.createElement("div");
38244             body.id = id;
38245         }
38246         //Roo.fly(body).addClass("x-tabs-item-body");
38247         Roo.fly(body).addClass("tab-pane");
38248          bodyEl.insertBefore(body, bodyEl.firstChild);
38249         return body;
38250     },
38251     /** @private */
38252     createStripElements :  function(stripEl, text, closable, tpl)
38253     {
38254         var td = document.createElement("li"); // was td..
38255         
38256         
38257         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38258         
38259         
38260         stripEl.appendChild(td);
38261         /*if(closable){
38262             td.className = "x-tabs-closable";
38263             if(!this.closeTpl){
38264                 this.closeTpl = new Roo.Template(
38265                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38266                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38267                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38268                 );
38269             }
38270             var el = this.closeTpl.overwrite(td, {"text": text});
38271             var close = el.getElementsByTagName("div")[0];
38272             var inner = el.getElementsByTagName("em")[0];
38273             return {"el": el, "close": close, "inner": inner};
38274         } else {
38275         */
38276         // not sure what this is..
38277 //            if(!this.tabTpl){
38278                 //this.tabTpl = new Roo.Template(
38279                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38280                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38281                 //);
38282 //                this.tabTpl = new Roo.Template(
38283 //                   '<a href="#">' +
38284 //                   '<span unselectable="on"' +
38285 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38286 //                            ' >{text}</span></a>'
38287 //                );
38288 //                
38289 //            }
38290
38291
38292             var template = tpl || this.tabTpl || false;
38293             
38294             if(!template){
38295                 
38296                 template = new Roo.Template(
38297                    '<a href="#">' +
38298                    '<span unselectable="on"' +
38299                             (this.disableTooltips ? '' : ' title="{text}"') +
38300                             ' >{text}</span></a>'
38301                 );
38302             }
38303             
38304             switch (typeof(template)) {
38305                 case 'object' :
38306                     break;
38307                 case 'string' :
38308                     template = new Roo.Template(template);
38309                     break;
38310                 default :
38311                     break;
38312             }
38313             
38314             var el = template.overwrite(td, {"text": text});
38315             
38316             var inner = el.getElementsByTagName("span")[0];
38317             
38318             return {"el": el, "inner": inner};
38319             
38320     }
38321         
38322     
38323 });
38324
38325 /**
38326  * @class Roo.TabPanelItem
38327  * @extends Roo.util.Observable
38328  * Represents an individual item (tab plus body) in a TabPanel.
38329  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38330  * @param {String} id The id of this TabPanelItem
38331  * @param {String} text The text for the tab of this TabPanelItem
38332  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38333  */
38334 Roo.bootstrap.panel.TabItem = function(config){
38335     /**
38336      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38337      * @type Roo.TabPanel
38338      */
38339     this.tabPanel = config.panel;
38340     /**
38341      * The id for this TabPanelItem
38342      * @type String
38343      */
38344     this.id = config.id;
38345     /** @private */
38346     this.disabled = false;
38347     /** @private */
38348     this.text = config.text;
38349     /** @private */
38350     this.loaded = false;
38351     this.closable = config.closable;
38352
38353     /**
38354      * The body element for this TabPanelItem.
38355      * @type Roo.Element
38356      */
38357     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38358     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38359     this.bodyEl.setStyle("display", "block");
38360     this.bodyEl.setStyle("zoom", "1");
38361     //this.hideAction();
38362
38363     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38364     /** @private */
38365     this.el = Roo.get(els.el);
38366     this.inner = Roo.get(els.inner, true);
38367     this.textEl = Roo.get(this.el.dom.firstChild, true);
38368     this.pnode = Roo.get(els.el.parentNode, true);
38369 //    this.el.on("mousedown", this.onTabMouseDown, this);
38370     this.el.on("click", this.onTabClick, this);
38371     /** @private */
38372     if(config.closable){
38373         var c = Roo.get(els.close, true);
38374         c.dom.title = this.closeText;
38375         c.addClassOnOver("close-over");
38376         c.on("click", this.closeClick, this);
38377      }
38378
38379     this.addEvents({
38380          /**
38381          * @event activate
38382          * Fires when this tab becomes the active tab.
38383          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38384          * @param {Roo.TabPanelItem} this
38385          */
38386         "activate": true,
38387         /**
38388          * @event beforeclose
38389          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38390          * @param {Roo.TabPanelItem} this
38391          * @param {Object} e Set cancel to true on this object to cancel the close.
38392          */
38393         "beforeclose": true,
38394         /**
38395          * @event close
38396          * Fires when this tab is closed.
38397          * @param {Roo.TabPanelItem} this
38398          */
38399          "close": true,
38400         /**
38401          * @event deactivate
38402          * Fires when this tab is no longer the active tab.
38403          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38404          * @param {Roo.TabPanelItem} this
38405          */
38406          "deactivate" : true
38407     });
38408     this.hidden = false;
38409
38410     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38411 };
38412
38413 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38414            {
38415     purgeListeners : function(){
38416        Roo.util.Observable.prototype.purgeListeners.call(this);
38417        this.el.removeAllListeners();
38418     },
38419     /**
38420      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38421      */
38422     show : function(){
38423         this.pnode.addClass("active");
38424         this.showAction();
38425         if(Roo.isOpera){
38426             this.tabPanel.stripWrap.repaint();
38427         }
38428         this.fireEvent("activate", this.tabPanel, this);
38429     },
38430
38431     /**
38432      * Returns true if this tab is the active tab.
38433      * @return {Boolean}
38434      */
38435     isActive : function(){
38436         return this.tabPanel.getActiveTab() == this;
38437     },
38438
38439     /**
38440      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38441      */
38442     hide : function(){
38443         this.pnode.removeClass("active");
38444         this.hideAction();
38445         this.fireEvent("deactivate", this.tabPanel, this);
38446     },
38447
38448     hideAction : function(){
38449         this.bodyEl.hide();
38450         this.bodyEl.setStyle("position", "absolute");
38451         this.bodyEl.setLeft("-20000px");
38452         this.bodyEl.setTop("-20000px");
38453     },
38454
38455     showAction : function(){
38456         this.bodyEl.setStyle("position", "relative");
38457         this.bodyEl.setTop("");
38458         this.bodyEl.setLeft("");
38459         this.bodyEl.show();
38460     },
38461
38462     /**
38463      * Set the tooltip for the tab.
38464      * @param {String} tooltip The tab's tooltip
38465      */
38466     setTooltip : function(text){
38467         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38468             this.textEl.dom.qtip = text;
38469             this.textEl.dom.removeAttribute('title');
38470         }else{
38471             this.textEl.dom.title = text;
38472         }
38473     },
38474
38475     onTabClick : function(e){
38476         e.preventDefault();
38477         this.tabPanel.activate(this.id);
38478     },
38479
38480     onTabMouseDown : function(e){
38481         e.preventDefault();
38482         this.tabPanel.activate(this.id);
38483     },
38484 /*
38485     getWidth : function(){
38486         return this.inner.getWidth();
38487     },
38488
38489     setWidth : function(width){
38490         var iwidth = width - this.pnode.getPadding("lr");
38491         this.inner.setWidth(iwidth);
38492         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38493         this.pnode.setWidth(width);
38494     },
38495 */
38496     /**
38497      * Show or hide the tab
38498      * @param {Boolean} hidden True to hide or false to show.
38499      */
38500     setHidden : function(hidden){
38501         this.hidden = hidden;
38502         this.pnode.setStyle("display", hidden ? "none" : "");
38503     },
38504
38505     /**
38506      * Returns true if this tab is "hidden"
38507      * @return {Boolean}
38508      */
38509     isHidden : function(){
38510         return this.hidden;
38511     },
38512
38513     /**
38514      * Returns the text for this tab
38515      * @return {String}
38516      */
38517     getText : function(){
38518         return this.text;
38519     },
38520     /*
38521     autoSize : function(){
38522         //this.el.beginMeasure();
38523         this.textEl.setWidth(1);
38524         /*
38525          *  #2804 [new] Tabs in Roojs
38526          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38527          */
38528         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38529         //this.el.endMeasure();
38530     //},
38531
38532     /**
38533      * Sets the text for the tab (Note: this also sets the tooltip text)
38534      * @param {String} text The tab's text and tooltip
38535      */
38536     setText : function(text){
38537         this.text = text;
38538         this.textEl.update(text);
38539         this.setTooltip(text);
38540         //if(!this.tabPanel.resizeTabs){
38541         //    this.autoSize();
38542         //}
38543     },
38544     /**
38545      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38546      */
38547     activate : function(){
38548         this.tabPanel.activate(this.id);
38549     },
38550
38551     /**
38552      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38553      */
38554     disable : function(){
38555         if(this.tabPanel.active != this){
38556             this.disabled = true;
38557             this.pnode.addClass("disabled");
38558         }
38559     },
38560
38561     /**
38562      * Enables this TabPanelItem if it was previously disabled.
38563      */
38564     enable : function(){
38565         this.disabled = false;
38566         this.pnode.removeClass("disabled");
38567     },
38568
38569     /**
38570      * Sets the content for this TabPanelItem.
38571      * @param {String} content The content
38572      * @param {Boolean} loadScripts true to look for and load scripts
38573      */
38574     setContent : function(content, loadScripts){
38575         this.bodyEl.update(content, loadScripts);
38576     },
38577
38578     /**
38579      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38580      * @return {Roo.UpdateManager} The UpdateManager
38581      */
38582     getUpdateManager : function(){
38583         return this.bodyEl.getUpdateManager();
38584     },
38585
38586     /**
38587      * Set a URL to be used to load the content for this TabPanelItem.
38588      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38589      * @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)
38590      * @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)
38591      * @return {Roo.UpdateManager} The UpdateManager
38592      */
38593     setUrl : function(url, params, loadOnce){
38594         if(this.refreshDelegate){
38595             this.un('activate', this.refreshDelegate);
38596         }
38597         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38598         this.on("activate", this.refreshDelegate);
38599         return this.bodyEl.getUpdateManager();
38600     },
38601
38602     /** @private */
38603     _handleRefresh : function(url, params, loadOnce){
38604         if(!loadOnce || !this.loaded){
38605             var updater = this.bodyEl.getUpdateManager();
38606             updater.update(url, params, this._setLoaded.createDelegate(this));
38607         }
38608     },
38609
38610     /**
38611      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38612      *   Will fail silently if the setUrl method has not been called.
38613      *   This does not activate the panel, just updates its content.
38614      */
38615     refresh : function(){
38616         if(this.refreshDelegate){
38617            this.loaded = false;
38618            this.refreshDelegate();
38619         }
38620     },
38621
38622     /** @private */
38623     _setLoaded : function(){
38624         this.loaded = true;
38625     },
38626
38627     /** @private */
38628     closeClick : function(e){
38629         var o = {};
38630         e.stopEvent();
38631         this.fireEvent("beforeclose", this, o);
38632         if(o.cancel !== true){
38633             this.tabPanel.removeTab(this.id);
38634         }
38635     },
38636     /**
38637      * The text displayed in the tooltip for the close icon.
38638      * @type String
38639      */
38640     closeText : "Close this tab"
38641 });
38642 /**
38643 *    This script refer to:
38644 *    Title: International Telephone Input
38645 *    Author: Jack O'Connor
38646 *    Code version:  v12.1.12
38647 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38648 **/
38649
38650 Roo.bootstrap.PhoneInputData = function() {
38651     var d = [
38652       [
38653         "Afghanistan (‫افغانستان‬‎)",
38654         "af",
38655         "93"
38656       ],
38657       [
38658         "Albania (Shqipëri)",
38659         "al",
38660         "355"
38661       ],
38662       [
38663         "Algeria (‫الجزائر‬‎)",
38664         "dz",
38665         "213"
38666       ],
38667       [
38668         "American Samoa",
38669         "as",
38670         "1684"
38671       ],
38672       [
38673         "Andorra",
38674         "ad",
38675         "376"
38676       ],
38677       [
38678         "Angola",
38679         "ao",
38680         "244"
38681       ],
38682       [
38683         "Anguilla",
38684         "ai",
38685         "1264"
38686       ],
38687       [
38688         "Antigua and Barbuda",
38689         "ag",
38690         "1268"
38691       ],
38692       [
38693         "Argentina",
38694         "ar",
38695         "54"
38696       ],
38697       [
38698         "Armenia (Հայաստան)",
38699         "am",
38700         "374"
38701       ],
38702       [
38703         "Aruba",
38704         "aw",
38705         "297"
38706       ],
38707       [
38708         "Australia",
38709         "au",
38710         "61",
38711         0
38712       ],
38713       [
38714         "Austria (Österreich)",
38715         "at",
38716         "43"
38717       ],
38718       [
38719         "Azerbaijan (Azərbaycan)",
38720         "az",
38721         "994"
38722       ],
38723       [
38724         "Bahamas",
38725         "bs",
38726         "1242"
38727       ],
38728       [
38729         "Bahrain (‫البحرين‬‎)",
38730         "bh",
38731         "973"
38732       ],
38733       [
38734         "Bangladesh (বাংলাদেশ)",
38735         "bd",
38736         "880"
38737       ],
38738       [
38739         "Barbados",
38740         "bb",
38741         "1246"
38742       ],
38743       [
38744         "Belarus (Беларусь)",
38745         "by",
38746         "375"
38747       ],
38748       [
38749         "Belgium (België)",
38750         "be",
38751         "32"
38752       ],
38753       [
38754         "Belize",
38755         "bz",
38756         "501"
38757       ],
38758       [
38759         "Benin (Bénin)",
38760         "bj",
38761         "229"
38762       ],
38763       [
38764         "Bermuda",
38765         "bm",
38766         "1441"
38767       ],
38768       [
38769         "Bhutan (འབྲུག)",
38770         "bt",
38771         "975"
38772       ],
38773       [
38774         "Bolivia",
38775         "bo",
38776         "591"
38777       ],
38778       [
38779         "Bosnia and Herzegovina (Босна и Херцеговина)",
38780         "ba",
38781         "387"
38782       ],
38783       [
38784         "Botswana",
38785         "bw",
38786         "267"
38787       ],
38788       [
38789         "Brazil (Brasil)",
38790         "br",
38791         "55"
38792       ],
38793       [
38794         "British Indian Ocean Territory",
38795         "io",
38796         "246"
38797       ],
38798       [
38799         "British Virgin Islands",
38800         "vg",
38801         "1284"
38802       ],
38803       [
38804         "Brunei",
38805         "bn",
38806         "673"
38807       ],
38808       [
38809         "Bulgaria (България)",
38810         "bg",
38811         "359"
38812       ],
38813       [
38814         "Burkina Faso",
38815         "bf",
38816         "226"
38817       ],
38818       [
38819         "Burundi (Uburundi)",
38820         "bi",
38821         "257"
38822       ],
38823       [
38824         "Cambodia (កម្ពុជា)",
38825         "kh",
38826         "855"
38827       ],
38828       [
38829         "Cameroon (Cameroun)",
38830         "cm",
38831         "237"
38832       ],
38833       [
38834         "Canada",
38835         "ca",
38836         "1",
38837         1,
38838         ["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"]
38839       ],
38840       [
38841         "Cape Verde (Kabu Verdi)",
38842         "cv",
38843         "238"
38844       ],
38845       [
38846         "Caribbean Netherlands",
38847         "bq",
38848         "599",
38849         1
38850       ],
38851       [
38852         "Cayman Islands",
38853         "ky",
38854         "1345"
38855       ],
38856       [
38857         "Central African Republic (République centrafricaine)",
38858         "cf",
38859         "236"
38860       ],
38861       [
38862         "Chad (Tchad)",
38863         "td",
38864         "235"
38865       ],
38866       [
38867         "Chile",
38868         "cl",
38869         "56"
38870       ],
38871       [
38872         "China (中国)",
38873         "cn",
38874         "86"
38875       ],
38876       [
38877         "Christmas Island",
38878         "cx",
38879         "61",
38880         2
38881       ],
38882       [
38883         "Cocos (Keeling) Islands",
38884         "cc",
38885         "61",
38886         1
38887       ],
38888       [
38889         "Colombia",
38890         "co",
38891         "57"
38892       ],
38893       [
38894         "Comoros (‫جزر القمر‬‎)",
38895         "km",
38896         "269"
38897       ],
38898       [
38899         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38900         "cd",
38901         "243"
38902       ],
38903       [
38904         "Congo (Republic) (Congo-Brazzaville)",
38905         "cg",
38906         "242"
38907       ],
38908       [
38909         "Cook Islands",
38910         "ck",
38911         "682"
38912       ],
38913       [
38914         "Costa Rica",
38915         "cr",
38916         "506"
38917       ],
38918       [
38919         "Côte d’Ivoire",
38920         "ci",
38921         "225"
38922       ],
38923       [
38924         "Croatia (Hrvatska)",
38925         "hr",
38926         "385"
38927       ],
38928       [
38929         "Cuba",
38930         "cu",
38931         "53"
38932       ],
38933       [
38934         "Curaçao",
38935         "cw",
38936         "599",
38937         0
38938       ],
38939       [
38940         "Cyprus (Κύπρος)",
38941         "cy",
38942         "357"
38943       ],
38944       [
38945         "Czech Republic (Česká republika)",
38946         "cz",
38947         "420"
38948       ],
38949       [
38950         "Denmark (Danmark)",
38951         "dk",
38952         "45"
38953       ],
38954       [
38955         "Djibouti",
38956         "dj",
38957         "253"
38958       ],
38959       [
38960         "Dominica",
38961         "dm",
38962         "1767"
38963       ],
38964       [
38965         "Dominican Republic (República Dominicana)",
38966         "do",
38967         "1",
38968         2,
38969         ["809", "829", "849"]
38970       ],
38971       [
38972         "Ecuador",
38973         "ec",
38974         "593"
38975       ],
38976       [
38977         "Egypt (‫مصر‬‎)",
38978         "eg",
38979         "20"
38980       ],
38981       [
38982         "El Salvador",
38983         "sv",
38984         "503"
38985       ],
38986       [
38987         "Equatorial Guinea (Guinea Ecuatorial)",
38988         "gq",
38989         "240"
38990       ],
38991       [
38992         "Eritrea",
38993         "er",
38994         "291"
38995       ],
38996       [
38997         "Estonia (Eesti)",
38998         "ee",
38999         "372"
39000       ],
39001       [
39002         "Ethiopia",
39003         "et",
39004         "251"
39005       ],
39006       [
39007         "Falkland Islands (Islas Malvinas)",
39008         "fk",
39009         "500"
39010       ],
39011       [
39012         "Faroe Islands (Føroyar)",
39013         "fo",
39014         "298"
39015       ],
39016       [
39017         "Fiji",
39018         "fj",
39019         "679"
39020       ],
39021       [
39022         "Finland (Suomi)",
39023         "fi",
39024         "358",
39025         0
39026       ],
39027       [
39028         "France",
39029         "fr",
39030         "33"
39031       ],
39032       [
39033         "French Guiana (Guyane française)",
39034         "gf",
39035         "594"
39036       ],
39037       [
39038         "French Polynesia (Polynésie française)",
39039         "pf",
39040         "689"
39041       ],
39042       [
39043         "Gabon",
39044         "ga",
39045         "241"
39046       ],
39047       [
39048         "Gambia",
39049         "gm",
39050         "220"
39051       ],
39052       [
39053         "Georgia (საქართველო)",
39054         "ge",
39055         "995"
39056       ],
39057       [
39058         "Germany (Deutschland)",
39059         "de",
39060         "49"
39061       ],
39062       [
39063         "Ghana (Gaana)",
39064         "gh",
39065         "233"
39066       ],
39067       [
39068         "Gibraltar",
39069         "gi",
39070         "350"
39071       ],
39072       [
39073         "Greece (Ελλάδα)",
39074         "gr",
39075         "30"
39076       ],
39077       [
39078         "Greenland (Kalaallit Nunaat)",
39079         "gl",
39080         "299"
39081       ],
39082       [
39083         "Grenada",
39084         "gd",
39085         "1473"
39086       ],
39087       [
39088         "Guadeloupe",
39089         "gp",
39090         "590",
39091         0
39092       ],
39093       [
39094         "Guam",
39095         "gu",
39096         "1671"
39097       ],
39098       [
39099         "Guatemala",
39100         "gt",
39101         "502"
39102       ],
39103       [
39104         "Guernsey",
39105         "gg",
39106         "44",
39107         1
39108       ],
39109       [
39110         "Guinea (Guinée)",
39111         "gn",
39112         "224"
39113       ],
39114       [
39115         "Guinea-Bissau (Guiné Bissau)",
39116         "gw",
39117         "245"
39118       ],
39119       [
39120         "Guyana",
39121         "gy",
39122         "592"
39123       ],
39124       [
39125         "Haiti",
39126         "ht",
39127         "509"
39128       ],
39129       [
39130         "Honduras",
39131         "hn",
39132         "504"
39133       ],
39134       [
39135         "Hong Kong (香港)",
39136         "hk",
39137         "852"
39138       ],
39139       [
39140         "Hungary (Magyarország)",
39141         "hu",
39142         "36"
39143       ],
39144       [
39145         "Iceland (Ísland)",
39146         "is",
39147         "354"
39148       ],
39149       [
39150         "India (भारत)",
39151         "in",
39152         "91"
39153       ],
39154       [
39155         "Indonesia",
39156         "id",
39157         "62"
39158       ],
39159       [
39160         "Iran (‫ایران‬‎)",
39161         "ir",
39162         "98"
39163       ],
39164       [
39165         "Iraq (‫العراق‬‎)",
39166         "iq",
39167         "964"
39168       ],
39169       [
39170         "Ireland",
39171         "ie",
39172         "353"
39173       ],
39174       [
39175         "Isle of Man",
39176         "im",
39177         "44",
39178         2
39179       ],
39180       [
39181         "Israel (‫ישראל‬‎)",
39182         "il",
39183         "972"
39184       ],
39185       [
39186         "Italy (Italia)",
39187         "it",
39188         "39",
39189         0
39190       ],
39191       [
39192         "Jamaica",
39193         "jm",
39194         "1876"
39195       ],
39196       [
39197         "Japan (日本)",
39198         "jp",
39199         "81"
39200       ],
39201       [
39202         "Jersey",
39203         "je",
39204         "44",
39205         3
39206       ],
39207       [
39208         "Jordan (‫الأردن‬‎)",
39209         "jo",
39210         "962"
39211       ],
39212       [
39213         "Kazakhstan (Казахстан)",
39214         "kz",
39215         "7",
39216         1
39217       ],
39218       [
39219         "Kenya",
39220         "ke",
39221         "254"
39222       ],
39223       [
39224         "Kiribati",
39225         "ki",
39226         "686"
39227       ],
39228       [
39229         "Kosovo",
39230         "xk",
39231         "383"
39232       ],
39233       [
39234         "Kuwait (‫الكويت‬‎)",
39235         "kw",
39236         "965"
39237       ],
39238       [
39239         "Kyrgyzstan (Кыргызстан)",
39240         "kg",
39241         "996"
39242       ],
39243       [
39244         "Laos (ລາວ)",
39245         "la",
39246         "856"
39247       ],
39248       [
39249         "Latvia (Latvija)",
39250         "lv",
39251         "371"
39252       ],
39253       [
39254         "Lebanon (‫لبنان‬‎)",
39255         "lb",
39256         "961"
39257       ],
39258       [
39259         "Lesotho",
39260         "ls",
39261         "266"
39262       ],
39263       [
39264         "Liberia",
39265         "lr",
39266         "231"
39267       ],
39268       [
39269         "Libya (‫ليبيا‬‎)",
39270         "ly",
39271         "218"
39272       ],
39273       [
39274         "Liechtenstein",
39275         "li",
39276         "423"
39277       ],
39278       [
39279         "Lithuania (Lietuva)",
39280         "lt",
39281         "370"
39282       ],
39283       [
39284         "Luxembourg",
39285         "lu",
39286         "352"
39287       ],
39288       [
39289         "Macau (澳門)",
39290         "mo",
39291         "853"
39292       ],
39293       [
39294         "Macedonia (FYROM) (Македонија)",
39295         "mk",
39296         "389"
39297       ],
39298       [
39299         "Madagascar (Madagasikara)",
39300         "mg",
39301         "261"
39302       ],
39303       [
39304         "Malawi",
39305         "mw",
39306         "265"
39307       ],
39308       [
39309         "Malaysia",
39310         "my",
39311         "60"
39312       ],
39313       [
39314         "Maldives",
39315         "mv",
39316         "960"
39317       ],
39318       [
39319         "Mali",
39320         "ml",
39321         "223"
39322       ],
39323       [
39324         "Malta",
39325         "mt",
39326         "356"
39327       ],
39328       [
39329         "Marshall Islands",
39330         "mh",
39331         "692"
39332       ],
39333       [
39334         "Martinique",
39335         "mq",
39336         "596"
39337       ],
39338       [
39339         "Mauritania (‫موريتانيا‬‎)",
39340         "mr",
39341         "222"
39342       ],
39343       [
39344         "Mauritius (Moris)",
39345         "mu",
39346         "230"
39347       ],
39348       [
39349         "Mayotte",
39350         "yt",
39351         "262",
39352         1
39353       ],
39354       [
39355         "Mexico (México)",
39356         "mx",
39357         "52"
39358       ],
39359       [
39360         "Micronesia",
39361         "fm",
39362         "691"
39363       ],
39364       [
39365         "Moldova (Republica Moldova)",
39366         "md",
39367         "373"
39368       ],
39369       [
39370         "Monaco",
39371         "mc",
39372         "377"
39373       ],
39374       [
39375         "Mongolia (Монгол)",
39376         "mn",
39377         "976"
39378       ],
39379       [
39380         "Montenegro (Crna Gora)",
39381         "me",
39382         "382"
39383       ],
39384       [
39385         "Montserrat",
39386         "ms",
39387         "1664"
39388       ],
39389       [
39390         "Morocco (‫المغرب‬‎)",
39391         "ma",
39392         "212",
39393         0
39394       ],
39395       [
39396         "Mozambique (Moçambique)",
39397         "mz",
39398         "258"
39399       ],
39400       [
39401         "Myanmar (Burma) (မြန်မာ)",
39402         "mm",
39403         "95"
39404       ],
39405       [
39406         "Namibia (Namibië)",
39407         "na",
39408         "264"
39409       ],
39410       [
39411         "Nauru",
39412         "nr",
39413         "674"
39414       ],
39415       [
39416         "Nepal (नेपाल)",
39417         "np",
39418         "977"
39419       ],
39420       [
39421         "Netherlands (Nederland)",
39422         "nl",
39423         "31"
39424       ],
39425       [
39426         "New Caledonia (Nouvelle-Calédonie)",
39427         "nc",
39428         "687"
39429       ],
39430       [
39431         "New Zealand",
39432         "nz",
39433         "64"
39434       ],
39435       [
39436         "Nicaragua",
39437         "ni",
39438         "505"
39439       ],
39440       [
39441         "Niger (Nijar)",
39442         "ne",
39443         "227"
39444       ],
39445       [
39446         "Nigeria",
39447         "ng",
39448         "234"
39449       ],
39450       [
39451         "Niue",
39452         "nu",
39453         "683"
39454       ],
39455       [
39456         "Norfolk Island",
39457         "nf",
39458         "672"
39459       ],
39460       [
39461         "North Korea (조선 민주주의 인민 공화국)",
39462         "kp",
39463         "850"
39464       ],
39465       [
39466         "Northern Mariana Islands",
39467         "mp",
39468         "1670"
39469       ],
39470       [
39471         "Norway (Norge)",
39472         "no",
39473         "47",
39474         0
39475       ],
39476       [
39477         "Oman (‫عُمان‬‎)",
39478         "om",
39479         "968"
39480       ],
39481       [
39482         "Pakistan (‫پاکستان‬‎)",
39483         "pk",
39484         "92"
39485       ],
39486       [
39487         "Palau",
39488         "pw",
39489         "680"
39490       ],
39491       [
39492         "Palestine (‫فلسطين‬‎)",
39493         "ps",
39494         "970"
39495       ],
39496       [
39497         "Panama (Panamá)",
39498         "pa",
39499         "507"
39500       ],
39501       [
39502         "Papua New Guinea",
39503         "pg",
39504         "675"
39505       ],
39506       [
39507         "Paraguay",
39508         "py",
39509         "595"
39510       ],
39511       [
39512         "Peru (Perú)",
39513         "pe",
39514         "51"
39515       ],
39516       [
39517         "Philippines",
39518         "ph",
39519         "63"
39520       ],
39521       [
39522         "Poland (Polska)",
39523         "pl",
39524         "48"
39525       ],
39526       [
39527         "Portugal",
39528         "pt",
39529         "351"
39530       ],
39531       [
39532         "Puerto Rico",
39533         "pr",
39534         "1",
39535         3,
39536         ["787", "939"]
39537       ],
39538       [
39539         "Qatar (‫قطر‬‎)",
39540         "qa",
39541         "974"
39542       ],
39543       [
39544         "Réunion (La Réunion)",
39545         "re",
39546         "262",
39547         0
39548       ],
39549       [
39550         "Romania (România)",
39551         "ro",
39552         "40"
39553       ],
39554       [
39555         "Russia (Россия)",
39556         "ru",
39557         "7",
39558         0
39559       ],
39560       [
39561         "Rwanda",
39562         "rw",
39563         "250"
39564       ],
39565       [
39566         "Saint Barthélemy",
39567         "bl",
39568         "590",
39569         1
39570       ],
39571       [
39572         "Saint Helena",
39573         "sh",
39574         "290"
39575       ],
39576       [
39577         "Saint Kitts and Nevis",
39578         "kn",
39579         "1869"
39580       ],
39581       [
39582         "Saint Lucia",
39583         "lc",
39584         "1758"
39585       ],
39586       [
39587         "Saint Martin (Saint-Martin (partie française))",
39588         "mf",
39589         "590",
39590         2
39591       ],
39592       [
39593         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39594         "pm",
39595         "508"
39596       ],
39597       [
39598         "Saint Vincent and the Grenadines",
39599         "vc",
39600         "1784"
39601       ],
39602       [
39603         "Samoa",
39604         "ws",
39605         "685"
39606       ],
39607       [
39608         "San Marino",
39609         "sm",
39610         "378"
39611       ],
39612       [
39613         "São Tomé and Príncipe (São Tomé e Príncipe)",
39614         "st",
39615         "239"
39616       ],
39617       [
39618         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39619         "sa",
39620         "966"
39621       ],
39622       [
39623         "Senegal (Sénégal)",
39624         "sn",
39625         "221"
39626       ],
39627       [
39628         "Serbia (Србија)",
39629         "rs",
39630         "381"
39631       ],
39632       [
39633         "Seychelles",
39634         "sc",
39635         "248"
39636       ],
39637       [
39638         "Sierra Leone",
39639         "sl",
39640         "232"
39641       ],
39642       [
39643         "Singapore",
39644         "sg",
39645         "65"
39646       ],
39647       [
39648         "Sint Maarten",
39649         "sx",
39650         "1721"
39651       ],
39652       [
39653         "Slovakia (Slovensko)",
39654         "sk",
39655         "421"
39656       ],
39657       [
39658         "Slovenia (Slovenija)",
39659         "si",
39660         "386"
39661       ],
39662       [
39663         "Solomon Islands",
39664         "sb",
39665         "677"
39666       ],
39667       [
39668         "Somalia (Soomaaliya)",
39669         "so",
39670         "252"
39671       ],
39672       [
39673         "South Africa",
39674         "za",
39675         "27"
39676       ],
39677       [
39678         "South Korea (대한민국)",
39679         "kr",
39680         "82"
39681       ],
39682       [
39683         "South Sudan (‫جنوب السودان‬‎)",
39684         "ss",
39685         "211"
39686       ],
39687       [
39688         "Spain (España)",
39689         "es",
39690         "34"
39691       ],
39692       [
39693         "Sri Lanka (ශ්‍රී ලංකාව)",
39694         "lk",
39695         "94"
39696       ],
39697       [
39698         "Sudan (‫السودان‬‎)",
39699         "sd",
39700         "249"
39701       ],
39702       [
39703         "Suriname",
39704         "sr",
39705         "597"
39706       ],
39707       [
39708         "Svalbard and Jan Mayen",
39709         "sj",
39710         "47",
39711         1
39712       ],
39713       [
39714         "Swaziland",
39715         "sz",
39716         "268"
39717       ],
39718       [
39719         "Sweden (Sverige)",
39720         "se",
39721         "46"
39722       ],
39723       [
39724         "Switzerland (Schweiz)",
39725         "ch",
39726         "41"
39727       ],
39728       [
39729         "Syria (‫سوريا‬‎)",
39730         "sy",
39731         "963"
39732       ],
39733       [
39734         "Taiwan (台灣)",
39735         "tw",
39736         "886"
39737       ],
39738       [
39739         "Tajikistan",
39740         "tj",
39741         "992"
39742       ],
39743       [
39744         "Tanzania",
39745         "tz",
39746         "255"
39747       ],
39748       [
39749         "Thailand (ไทย)",
39750         "th",
39751         "66"
39752       ],
39753       [
39754         "Timor-Leste",
39755         "tl",
39756         "670"
39757       ],
39758       [
39759         "Togo",
39760         "tg",
39761         "228"
39762       ],
39763       [
39764         "Tokelau",
39765         "tk",
39766         "690"
39767       ],
39768       [
39769         "Tonga",
39770         "to",
39771         "676"
39772       ],
39773       [
39774         "Trinidad and Tobago",
39775         "tt",
39776         "1868"
39777       ],
39778       [
39779         "Tunisia (‫تونس‬‎)",
39780         "tn",
39781         "216"
39782       ],
39783       [
39784         "Turkey (Türkiye)",
39785         "tr",
39786         "90"
39787       ],
39788       [
39789         "Turkmenistan",
39790         "tm",
39791         "993"
39792       ],
39793       [
39794         "Turks and Caicos Islands",
39795         "tc",
39796         "1649"
39797       ],
39798       [
39799         "Tuvalu",
39800         "tv",
39801         "688"
39802       ],
39803       [
39804         "U.S. Virgin Islands",
39805         "vi",
39806         "1340"
39807       ],
39808       [
39809         "Uganda",
39810         "ug",
39811         "256"
39812       ],
39813       [
39814         "Ukraine (Україна)",
39815         "ua",
39816         "380"
39817       ],
39818       [
39819         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39820         "ae",
39821         "971"
39822       ],
39823       [
39824         "United Kingdom",
39825         "gb",
39826         "44",
39827         0
39828       ],
39829       [
39830         "United States",
39831         "us",
39832         "1",
39833         0
39834       ],
39835       [
39836         "Uruguay",
39837         "uy",
39838         "598"
39839       ],
39840       [
39841         "Uzbekistan (Oʻzbekiston)",
39842         "uz",
39843         "998"
39844       ],
39845       [
39846         "Vanuatu",
39847         "vu",
39848         "678"
39849       ],
39850       [
39851         "Vatican City (Città del Vaticano)",
39852         "va",
39853         "39",
39854         1
39855       ],
39856       [
39857         "Venezuela",
39858         "ve",
39859         "58"
39860       ],
39861       [
39862         "Vietnam (Việt Nam)",
39863         "vn",
39864         "84"
39865       ],
39866       [
39867         "Wallis and Futuna (Wallis-et-Futuna)",
39868         "wf",
39869         "681"
39870       ],
39871       [
39872         "Western Sahara (‫الصحراء الغربية‬‎)",
39873         "eh",
39874         "212",
39875         1
39876       ],
39877       [
39878         "Yemen (‫اليمن‬‎)",
39879         "ye",
39880         "967"
39881       ],
39882       [
39883         "Zambia",
39884         "zm",
39885         "260"
39886       ],
39887       [
39888         "Zimbabwe",
39889         "zw",
39890         "263"
39891       ],
39892       [
39893         "Åland Islands",
39894         "ax",
39895         "358",
39896         1
39897       ]
39898   ];
39899   
39900   return d;
39901 }/**
39902 *    This script refer to:
39903 *    Title: International Telephone Input
39904 *    Author: Jack O'Connor
39905 *    Code version:  v12.1.12
39906 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39907 **/
39908
39909 /**
39910  * @class Roo.bootstrap.PhoneInput
39911  * @extends Roo.bootstrap.TriggerField
39912  * An input with International dial-code selection
39913  
39914  * @cfg {String} defaultDialCode default '+852'
39915  * @cfg {Array} preferedCountries default []
39916   
39917  * @constructor
39918  * Create a new PhoneInput.
39919  * @param {Object} config Configuration options
39920  */
39921
39922 Roo.bootstrap.PhoneInput = function(config) {
39923     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39924 };
39925
39926 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39927         
39928         listWidth: undefined,
39929         
39930         selectedClass: 'active',
39931         
39932         invalidClass : "has-warning",
39933         
39934         validClass: 'has-success',
39935         
39936         allowed: '0123456789',
39937         
39938         max_length: 15,
39939         
39940         /**
39941          * @cfg {String} defaultDialCode The default dial code when initializing the input
39942          */
39943         defaultDialCode: '+852',
39944         
39945         /**
39946          * @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
39947          */
39948         preferedCountries: false,
39949         
39950         getAutoCreate : function()
39951         {
39952             var data = Roo.bootstrap.PhoneInputData();
39953             var align = this.labelAlign || this.parentLabelAlign();
39954             var id = Roo.id();
39955             
39956             this.allCountries = [];
39957             this.dialCodeMapping = [];
39958             
39959             for (var i = 0; i < data.length; i++) {
39960               var c = data[i];
39961               this.allCountries[i] = {
39962                 name: c[0],
39963                 iso2: c[1],
39964                 dialCode: c[2],
39965                 priority: c[3] || 0,
39966                 areaCodes: c[4] || null
39967               };
39968               this.dialCodeMapping[c[2]] = {
39969                   name: c[0],
39970                   iso2: c[1],
39971                   priority: c[3] || 0,
39972                   areaCodes: c[4] || null
39973               };
39974             }
39975             
39976             var cfg = {
39977                 cls: 'form-group',
39978                 cn: []
39979             };
39980             
39981             var input =  {
39982                 tag: 'input',
39983                 id : id,
39984                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39985                 maxlength: this.max_length,
39986                 cls : 'form-control tel-input',
39987                 autocomplete: 'new-password'
39988             };
39989             
39990             var hiddenInput = {
39991                 tag: 'input',
39992                 type: 'hidden',
39993                 cls: 'hidden-tel-input'
39994             };
39995             
39996             if (this.name) {
39997                 hiddenInput.name = this.name;
39998             }
39999             
40000             if (this.disabled) {
40001                 input.disabled = true;
40002             }
40003             
40004             var flag_container = {
40005                 tag: 'div',
40006                 cls: 'flag-box',
40007                 cn: [
40008                     {
40009                         tag: 'div',
40010                         cls: 'flag'
40011                     },
40012                     {
40013                         tag: 'div',
40014                         cls: 'caret'
40015                     }
40016                 ]
40017             };
40018             
40019             var box = {
40020                 tag: 'div',
40021                 cls: this.hasFeedback ? 'has-feedback' : '',
40022                 cn: [
40023                     hiddenInput,
40024                     input,
40025                     {
40026                         tag: 'input',
40027                         cls: 'dial-code-holder',
40028                         disabled: true
40029                     }
40030                 ]
40031             };
40032             
40033             var container = {
40034                 cls: 'roo-select2-container input-group',
40035                 cn: [
40036                     flag_container,
40037                     box
40038                 ]
40039             };
40040             
40041             if (this.fieldLabel.length) {
40042                 var indicator = {
40043                     tag: 'i',
40044                     tooltip: 'This field is required'
40045                 };
40046                 
40047                 var label = {
40048                     tag: 'label',
40049                     'for':  id,
40050                     cls: 'control-label',
40051                     cn: []
40052                 };
40053                 
40054                 var label_text = {
40055                     tag: 'span',
40056                     html: this.fieldLabel
40057                 };
40058                 
40059                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40060                 label.cn = [
40061                     indicator,
40062                     label_text
40063                 ];
40064                 
40065                 if(this.indicatorpos == 'right') {
40066                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40067                     label.cn = [
40068                         label_text,
40069                         indicator
40070                     ];
40071                 }
40072                 
40073                 if(align == 'left') {
40074                     container = {
40075                         tag: 'div',
40076                         cn: [
40077                             container
40078                         ]
40079                     };
40080                     
40081                     if(this.labelWidth > 12){
40082                         label.style = "width: " + this.labelWidth + 'px';
40083                     }
40084                     if(this.labelWidth < 13 && this.labelmd == 0){
40085                         this.labelmd = this.labelWidth;
40086                     }
40087                     if(this.labellg > 0){
40088                         label.cls += ' col-lg-' + this.labellg;
40089                         input.cls += ' col-lg-' + (12 - this.labellg);
40090                     }
40091                     if(this.labelmd > 0){
40092                         label.cls += ' col-md-' + this.labelmd;
40093                         container.cls += ' col-md-' + (12 - this.labelmd);
40094                     }
40095                     if(this.labelsm > 0){
40096                         label.cls += ' col-sm-' + this.labelsm;
40097                         container.cls += ' col-sm-' + (12 - this.labelsm);
40098                     }
40099                     if(this.labelxs > 0){
40100                         label.cls += ' col-xs-' + this.labelxs;
40101                         container.cls += ' col-xs-' + (12 - this.labelxs);
40102                     }
40103                 }
40104             }
40105             
40106             cfg.cn = [
40107                 label,
40108                 container
40109             ];
40110             
40111             var settings = this;
40112             
40113             ['xs','sm','md','lg'].map(function(size){
40114                 if (settings[size]) {
40115                     cfg.cls += ' col-' + size + '-' + settings[size];
40116                 }
40117             });
40118             
40119             this.store = new Roo.data.Store({
40120                 proxy : new Roo.data.MemoryProxy({}),
40121                 reader : new Roo.data.JsonReader({
40122                     fields : [
40123                         {
40124                             'name' : 'name',
40125                             'type' : 'string'
40126                         },
40127                         {
40128                             'name' : 'iso2',
40129                             'type' : 'string'
40130                         },
40131                         {
40132                             'name' : 'dialCode',
40133                             'type' : 'string'
40134                         },
40135                         {
40136                             'name' : 'priority',
40137                             'type' : 'string'
40138                         },
40139                         {
40140                             'name' : 'areaCodes',
40141                             'type' : 'string'
40142                         }
40143                     ]
40144                 })
40145             });
40146             
40147             if(!this.preferedCountries) {
40148                 this.preferedCountries = [
40149                     'hk',
40150                     'gb',
40151                     'us'
40152                 ];
40153             }
40154             
40155             var p = this.preferedCountries.reverse();
40156             
40157             if(p) {
40158                 for (var i = 0; i < p.length; i++) {
40159                     for (var j = 0; j < this.allCountries.length; j++) {
40160                         if(this.allCountries[j].iso2 == p[i]) {
40161                             var t = this.allCountries[j];
40162                             this.allCountries.splice(j,1);
40163                             this.allCountries.unshift(t);
40164                         }
40165                     } 
40166                 }
40167             }
40168             
40169             this.store.proxy.data = {
40170                 success: true,
40171                 data: this.allCountries
40172             };
40173             
40174             return cfg;
40175         },
40176         
40177         initEvents : function()
40178         {
40179             this.createList();
40180             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40181             
40182             this.indicator = this.indicatorEl();
40183             this.flag = this.flagEl();
40184             this.dialCodeHolder = this.dialCodeHolderEl();
40185             
40186             this.trigger = this.el.select('div.flag-box',true).first();
40187             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40188             
40189             var _this = this;
40190             
40191             (function(){
40192                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40193                 _this.list.setWidth(lw);
40194             }).defer(100);
40195             
40196             this.list.on('mouseover', this.onViewOver, this);
40197             this.list.on('mousemove', this.onViewMove, this);
40198             this.inputEl().on("keyup", this.onKeyUp, this);
40199             this.inputEl().on("keypress", this.onKeyPress, this);
40200             
40201             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40202
40203             this.view = new Roo.View(this.list, this.tpl, {
40204                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40205             });
40206             
40207             this.view.on('click', this.onViewClick, this);
40208             this.setValue(this.defaultDialCode);
40209         },
40210         
40211         onTriggerClick : function(e)
40212         {
40213             Roo.log('trigger click');
40214             if(this.disabled){
40215                 return;
40216             }
40217             
40218             if(this.isExpanded()){
40219                 this.collapse();
40220                 this.hasFocus = false;
40221             }else {
40222                 this.store.load({});
40223                 this.hasFocus = true;
40224                 this.expand();
40225             }
40226         },
40227         
40228         isExpanded : function()
40229         {
40230             return this.list.isVisible();
40231         },
40232         
40233         collapse : function()
40234         {
40235             if(!this.isExpanded()){
40236                 return;
40237             }
40238             this.list.hide();
40239             Roo.get(document).un('mousedown', this.collapseIf, this);
40240             Roo.get(document).un('mousewheel', this.collapseIf, this);
40241             this.fireEvent('collapse', this);
40242             this.validate();
40243         },
40244         
40245         expand : function()
40246         {
40247             Roo.log('expand');
40248
40249             if(this.isExpanded() || !this.hasFocus){
40250                 return;
40251             }
40252             
40253             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40254             this.list.setWidth(lw);
40255             
40256             this.list.show();
40257             this.restrictHeight();
40258             
40259             Roo.get(document).on('mousedown', this.collapseIf, this);
40260             Roo.get(document).on('mousewheel', this.collapseIf, this);
40261             
40262             this.fireEvent('expand', this);
40263         },
40264         
40265         restrictHeight : function()
40266         {
40267             this.list.alignTo(this.inputEl(), this.listAlign);
40268             this.list.alignTo(this.inputEl(), this.listAlign);
40269         },
40270         
40271         onViewOver : function(e, t)
40272         {
40273             if(this.inKeyMode){
40274                 return;
40275             }
40276             var item = this.view.findItemFromChild(t);
40277             
40278             if(item){
40279                 var index = this.view.indexOf(item);
40280                 this.select(index, false);
40281             }
40282         },
40283
40284         // private
40285         onViewClick : function(view, doFocus, el, e)
40286         {
40287             var index = this.view.getSelectedIndexes()[0];
40288             
40289             var r = this.store.getAt(index);
40290             
40291             if(r){
40292                 this.onSelect(r, index);
40293             }
40294             if(doFocus !== false && !this.blockFocus){
40295                 this.inputEl().focus();
40296             }
40297         },
40298         
40299         onViewMove : function(e, t)
40300         {
40301             this.inKeyMode = false;
40302         },
40303         
40304         select : function(index, scrollIntoView)
40305         {
40306             this.selectedIndex = index;
40307             this.view.select(index);
40308             if(scrollIntoView !== false){
40309                 var el = this.view.getNode(index);
40310                 if(el){
40311                     this.list.scrollChildIntoView(el, false);
40312                 }
40313             }
40314         },
40315         
40316         createList : function()
40317         {
40318             this.list = Roo.get(document.body).createChild({
40319                 tag: 'ul',
40320                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40321                 style: 'display:none'
40322             });
40323             
40324             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40325         },
40326         
40327         collapseIf : function(e)
40328         {
40329             var in_combo  = e.within(this.el);
40330             var in_list =  e.within(this.list);
40331             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40332             
40333             if (in_combo || in_list || is_list) {
40334                 return;
40335             }
40336             this.collapse();
40337         },
40338         
40339         onSelect : function(record, index)
40340         {
40341             if(this.fireEvent('beforeselect', this, record, index) !== false){
40342                 
40343                 this.setFlagClass(record.data.iso2);
40344                 this.setDialCode(record.data.dialCode);
40345                 this.hasFocus = false;
40346                 this.collapse();
40347                 this.fireEvent('select', this, record, index);
40348             }
40349         },
40350         
40351         flagEl : function()
40352         {
40353             var flag = this.el.select('div.flag',true).first();
40354             if(!flag){
40355                 return false;
40356             }
40357             return flag;
40358         },
40359         
40360         dialCodeHolderEl : function()
40361         {
40362             var d = this.el.select('input.dial-code-holder',true).first();
40363             if(!d){
40364                 return false;
40365             }
40366             return d;
40367         },
40368         
40369         setDialCode : function(v)
40370         {
40371             this.dialCodeHolder.dom.value = '+'+v;
40372         },
40373         
40374         setFlagClass : function(n)
40375         {
40376             this.flag.dom.className = 'flag '+n;
40377         },
40378         
40379         getValue : function()
40380         {
40381             var v = this.inputEl().getValue();
40382             if(this.dialCodeHolder) {
40383                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40384             }
40385             return v;
40386         },
40387         
40388         setValue : function(v)
40389         {
40390             var d = this.getDialCode(v);
40391             
40392             //invalid dial code
40393             if(v.length == 0 || !d || d.length == 0) {
40394                 if(this.rendered){
40395                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40396                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40397                 }
40398                 return;
40399             }
40400             
40401             //valid dial code
40402             this.setFlagClass(this.dialCodeMapping[d].iso2);
40403             this.setDialCode(d);
40404             this.inputEl().dom.value = v.replace('+'+d,'');
40405             this.hiddenEl().dom.value = this.getValue();
40406             
40407             this.validate();
40408         },
40409         
40410         getDialCode : function(v)
40411         {
40412             v = v ||  '';
40413             
40414             if (v.length == 0) {
40415                 return this.dialCodeHolder.dom.value;
40416             }
40417             
40418             var dialCode = "";
40419             if (v.charAt(0) != "+") {
40420                 return false;
40421             }
40422             var numericChars = "";
40423             for (var i = 1; i < v.length; i++) {
40424               var c = v.charAt(i);
40425               if (!isNaN(c)) {
40426                 numericChars += c;
40427                 if (this.dialCodeMapping[numericChars]) {
40428                   dialCode = v.substr(1, i);
40429                 }
40430                 if (numericChars.length == 4) {
40431                   break;
40432                 }
40433               }
40434             }
40435             return dialCode;
40436         },
40437         
40438         reset : function()
40439         {
40440             this.setValue(this.defaultDialCode);
40441             this.validate();
40442         },
40443         
40444         hiddenEl : function()
40445         {
40446             return this.el.select('input.hidden-tel-input',true).first();
40447         },
40448         
40449         // after setting val
40450         onKeyUp : function(e){
40451             this.setValue(this.getValue());
40452         },
40453         
40454         onKeyPress : function(e){
40455             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40456                 e.stopEvent();
40457             }
40458         }
40459         
40460 });
40461 /**
40462  * @class Roo.bootstrap.MoneyField
40463  * @extends Roo.bootstrap.ComboBox
40464  * Bootstrap MoneyField class
40465  * 
40466  * @constructor
40467  * Create a new MoneyField.
40468  * @param {Object} config Configuration options
40469  */
40470
40471 Roo.bootstrap.MoneyField = function(config) {
40472     
40473     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40474     
40475 };
40476
40477 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40478     
40479     /**
40480      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40481      */
40482     allowDecimals : true,
40483     /**
40484      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40485      */
40486     decimalSeparator : ".",
40487     /**
40488      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40489      */
40490     decimalPrecision : 0,
40491     /**
40492      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40493      */
40494     allowNegative : true,
40495     /**
40496      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40497      */
40498     allowZero: true,
40499     /**
40500      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40501      */
40502     minValue : Number.NEGATIVE_INFINITY,
40503     /**
40504      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40505      */
40506     maxValue : Number.MAX_VALUE,
40507     /**
40508      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40509      */
40510     minText : "The minimum value for this field is {0}",
40511     /**
40512      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40513      */
40514     maxText : "The maximum value for this field is {0}",
40515     /**
40516      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40517      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40518      */
40519     nanText : "{0} is not a valid number",
40520     /**
40521      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40522      */
40523     castInt : true,
40524     /**
40525      * @cfg {String} defaults currency of the MoneyField
40526      * value should be in lkey
40527      */
40528     defaultCurrency : false,
40529     /**
40530      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40531      */
40532     thousandsDelimiter : false,
40533     /**
40534      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40535      */
40536     max_length: false,
40537     
40538     inputlg : 9,
40539     inputmd : 9,
40540     inputsm : 9,
40541     inputxs : 6,
40542     
40543     store : false,
40544     
40545     getAutoCreate : function()
40546     {
40547         var align = this.labelAlign || this.parentLabelAlign();
40548         
40549         var id = Roo.id();
40550
40551         var cfg = {
40552             cls: 'form-group',
40553             cn: []
40554         };
40555
40556         var input =  {
40557             tag: 'input',
40558             id : id,
40559             cls : 'form-control roo-money-amount-input',
40560             autocomplete: 'new-password'
40561         };
40562         
40563         var hiddenInput = {
40564             tag: 'input',
40565             type: 'hidden',
40566             id: Roo.id(),
40567             cls: 'hidden-number-input'
40568         };
40569         
40570         if(this.max_length) {
40571             input.maxlength = this.max_length; 
40572         }
40573         
40574         if (this.name) {
40575             hiddenInput.name = this.name;
40576         }
40577
40578         if (this.disabled) {
40579             input.disabled = true;
40580         }
40581
40582         var clg = 12 - this.inputlg;
40583         var cmd = 12 - this.inputmd;
40584         var csm = 12 - this.inputsm;
40585         var cxs = 12 - this.inputxs;
40586         
40587         var container = {
40588             tag : 'div',
40589             cls : 'row roo-money-field',
40590             cn : [
40591                 {
40592                     tag : 'div',
40593                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40594                     cn : [
40595                         {
40596                             tag : 'div',
40597                             cls: 'roo-select2-container input-group',
40598                             cn: [
40599                                 {
40600                                     tag : 'input',
40601                                     cls : 'form-control roo-money-currency-input',
40602                                     autocomplete: 'new-password',
40603                                     readOnly : 1,
40604                                     name : this.currencyName
40605                                 },
40606                                 {
40607                                     tag :'span',
40608                                     cls : 'input-group-addon',
40609                                     cn : [
40610                                         {
40611                                             tag: 'span',
40612                                             cls: 'caret'
40613                                         }
40614                                     ]
40615                                 }
40616                             ]
40617                         }
40618                     ]
40619                 },
40620                 {
40621                     tag : 'div',
40622                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40623                     cn : [
40624                         {
40625                             tag: 'div',
40626                             cls: this.hasFeedback ? 'has-feedback' : '',
40627                             cn: [
40628                                 input
40629                             ]
40630                         }
40631                     ]
40632                 }
40633             ]
40634             
40635         };
40636         
40637         if (this.fieldLabel.length) {
40638             var indicator = {
40639                 tag: 'i',
40640                 tooltip: 'This field is required'
40641             };
40642
40643             var label = {
40644                 tag: 'label',
40645                 'for':  id,
40646                 cls: 'control-label',
40647                 cn: []
40648             };
40649
40650             var label_text = {
40651                 tag: 'span',
40652                 html: this.fieldLabel
40653             };
40654
40655             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40656             label.cn = [
40657                 indicator,
40658                 label_text
40659             ];
40660
40661             if(this.indicatorpos == 'right') {
40662                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40663                 label.cn = [
40664                     label_text,
40665                     indicator
40666                 ];
40667             }
40668
40669             if(align == 'left') {
40670                 container = {
40671                     tag: 'div',
40672                     cn: [
40673                         container
40674                     ]
40675                 };
40676
40677                 if(this.labelWidth > 12){
40678                     label.style = "width: " + this.labelWidth + 'px';
40679                 }
40680                 if(this.labelWidth < 13 && this.labelmd == 0){
40681                     this.labelmd = this.labelWidth;
40682                 }
40683                 if(this.labellg > 0){
40684                     label.cls += ' col-lg-' + this.labellg;
40685                     input.cls += ' col-lg-' + (12 - this.labellg);
40686                 }
40687                 if(this.labelmd > 0){
40688                     label.cls += ' col-md-' + this.labelmd;
40689                     container.cls += ' col-md-' + (12 - this.labelmd);
40690                 }
40691                 if(this.labelsm > 0){
40692                     label.cls += ' col-sm-' + this.labelsm;
40693                     container.cls += ' col-sm-' + (12 - this.labelsm);
40694                 }
40695                 if(this.labelxs > 0){
40696                     label.cls += ' col-xs-' + this.labelxs;
40697                     container.cls += ' col-xs-' + (12 - this.labelxs);
40698                 }
40699             }
40700         }
40701
40702         cfg.cn = [
40703             label,
40704             container,
40705             hiddenInput
40706         ];
40707         
40708         var settings = this;
40709
40710         ['xs','sm','md','lg'].map(function(size){
40711             if (settings[size]) {
40712                 cfg.cls += ' col-' + size + '-' + settings[size];
40713             }
40714         });
40715         
40716         return cfg;
40717     },
40718     
40719     initEvents : function()
40720     {
40721         this.indicator = this.indicatorEl();
40722         
40723         this.initCurrencyEvent();
40724         
40725         this.initNumberEvent();
40726     },
40727     
40728     initCurrencyEvent : function()
40729     {
40730         if (!this.store) {
40731             throw "can not find store for combo";
40732         }
40733         
40734         this.store = Roo.factory(this.store, Roo.data);
40735         this.store.parent = this;
40736         
40737         this.createList();
40738         
40739         this.triggerEl = this.el.select('.input-group-addon', true).first();
40740         
40741         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40742         
40743         var _this = this;
40744         
40745         (function(){
40746             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40747             _this.list.setWidth(lw);
40748         }).defer(100);
40749         
40750         this.list.on('mouseover', this.onViewOver, this);
40751         this.list.on('mousemove', this.onViewMove, this);
40752         this.list.on('scroll', this.onViewScroll, this);
40753         
40754         if(!this.tpl){
40755             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40756         }
40757         
40758         this.view = new Roo.View(this.list, this.tpl, {
40759             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40760         });
40761         
40762         this.view.on('click', this.onViewClick, this);
40763         
40764         this.store.on('beforeload', this.onBeforeLoad, this);
40765         this.store.on('load', this.onLoad, this);
40766         this.store.on('loadexception', this.onLoadException, this);
40767         
40768         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40769             "up" : function(e){
40770                 this.inKeyMode = true;
40771                 this.selectPrev();
40772             },
40773
40774             "down" : function(e){
40775                 if(!this.isExpanded()){
40776                     this.onTriggerClick();
40777                 }else{
40778                     this.inKeyMode = true;
40779                     this.selectNext();
40780                 }
40781             },
40782
40783             "enter" : function(e){
40784                 this.collapse();
40785                 
40786                 if(this.fireEvent("specialkey", this, e)){
40787                     this.onViewClick(false);
40788                 }
40789                 
40790                 return true;
40791             },
40792
40793             "esc" : function(e){
40794                 this.collapse();
40795             },
40796
40797             "tab" : function(e){
40798                 this.collapse();
40799                 
40800                 if(this.fireEvent("specialkey", this, e)){
40801                     this.onViewClick(false);
40802                 }
40803                 
40804                 return true;
40805             },
40806
40807             scope : this,
40808
40809             doRelay : function(foo, bar, hname){
40810                 if(hname == 'down' || this.scope.isExpanded()){
40811                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40812                 }
40813                 return true;
40814             },
40815
40816             forceKeyDown: true
40817         });
40818         
40819         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40820         
40821     },
40822     
40823     initNumberEvent : function(e)
40824     {
40825         this.inputEl().on("keydown" , this.fireKey,  this);
40826         this.inputEl().on("focus", this.onFocus,  this);
40827         this.inputEl().on("blur", this.onBlur,  this);
40828         
40829         this.inputEl().relayEvent('keyup', this);
40830         
40831         if(this.indicator){
40832             this.indicator.addClass('invisible');
40833         }
40834  
40835         this.originalValue = this.getValue();
40836         
40837         if(this.validationEvent == 'keyup'){
40838             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40839             this.inputEl().on('keyup', this.filterValidation, this);
40840         }
40841         else if(this.validationEvent !== false){
40842             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40843         }
40844         
40845         if(this.selectOnFocus){
40846             this.on("focus", this.preFocus, this);
40847             
40848         }
40849         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40850             this.inputEl().on("keypress", this.filterKeys, this);
40851         } else {
40852             this.inputEl().relayEvent('keypress', this);
40853         }
40854         
40855         var allowed = "0123456789";
40856         
40857         if(this.allowDecimals){
40858             allowed += this.decimalSeparator;
40859         }
40860         
40861         if(this.allowNegative){
40862             allowed += "-";
40863         }
40864         
40865         if(this.thousandsDelimiter) {
40866             allowed += ",";
40867         }
40868         
40869         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40870         
40871         var keyPress = function(e){
40872             
40873             var k = e.getKey();
40874             
40875             var c = e.getCharCode();
40876             
40877             if(
40878                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40879                     allowed.indexOf(String.fromCharCode(c)) === -1
40880             ){
40881                 e.stopEvent();
40882                 return;
40883             }
40884             
40885             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40886                 return;
40887             }
40888             
40889             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40890                 e.stopEvent();
40891             }
40892         };
40893         
40894         this.inputEl().on("keypress", keyPress, this);
40895         
40896     },
40897     
40898     onTriggerClick : function(e)
40899     {   
40900         if(this.disabled){
40901             return;
40902         }
40903         
40904         this.page = 0;
40905         this.loadNext = false;
40906         
40907         if(this.isExpanded()){
40908             this.collapse();
40909             return;
40910         }
40911         
40912         this.hasFocus = true;
40913         
40914         if(this.triggerAction == 'all') {
40915             this.doQuery(this.allQuery, true);
40916             return;
40917         }
40918         
40919         this.doQuery(this.getRawValue());
40920     },
40921     
40922     getCurrency : function()
40923     {   
40924         var v = this.currencyEl().getValue();
40925         
40926         return v;
40927     },
40928     
40929     restrictHeight : function()
40930     {
40931         this.list.alignTo(this.currencyEl(), this.listAlign);
40932         this.list.alignTo(this.currencyEl(), this.listAlign);
40933     },
40934     
40935     onViewClick : function(view, doFocus, el, e)
40936     {
40937         var index = this.view.getSelectedIndexes()[0];
40938         
40939         var r = this.store.getAt(index);
40940         
40941         if(r){
40942             this.onSelect(r, index);
40943         }
40944     },
40945     
40946     onSelect : function(record, index){
40947         
40948         if(this.fireEvent('beforeselect', this, record, index) !== false){
40949         
40950             this.setFromCurrencyData(index > -1 ? record.data : false);
40951             
40952             this.collapse();
40953             
40954             this.fireEvent('select', this, record, index);
40955         }
40956     },
40957     
40958     setFromCurrencyData : function(o)
40959     {
40960         var currency = '';
40961         
40962         this.lastCurrency = o;
40963         
40964         if (this.currencyField) {
40965             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40966         } else {
40967             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40968         }
40969         
40970         this.lastSelectionText = currency;
40971         
40972         //setting default currency
40973         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40974             this.setCurrency(this.defaultCurrency);
40975             return;
40976         }
40977         
40978         this.setCurrency(currency);
40979     },
40980     
40981     setFromData : function(o)
40982     {
40983         var c = {};
40984         
40985         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40986         
40987         this.setFromCurrencyData(c);
40988         
40989         var value = '';
40990         
40991         if (this.name) {
40992             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40993         } else {
40994             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40995         }
40996         
40997         this.setValue(value);
40998         
40999     },
41000     
41001     setCurrency : function(v)
41002     {   
41003         this.currencyValue = v;
41004         
41005         if(this.rendered){
41006             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41007             this.validate();
41008         }
41009     },
41010     
41011     setValue : function(v)
41012     {
41013         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41014         
41015         this.value = v;
41016         
41017         if(this.rendered){
41018             
41019             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41020             
41021             this.inputEl().dom.value = (v == '') ? '' :
41022                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41023             
41024             if(!this.allowZero && v === '0') {
41025                 this.hiddenEl().dom.value = '';
41026                 this.inputEl().dom.value = '';
41027             }
41028             
41029             this.validate();
41030         }
41031     },
41032     
41033     getRawValue : function()
41034     {
41035         var v = this.inputEl().getValue();
41036         
41037         return v;
41038     },
41039     
41040     getValue : function()
41041     {
41042         return this.fixPrecision(this.parseValue(this.getRawValue()));
41043     },
41044     
41045     parseValue : function(value)
41046     {
41047         if(this.thousandsDelimiter) {
41048             value += "";
41049             r = new RegExp(",", "g");
41050             value = value.replace(r, "");
41051         }
41052         
41053         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41054         return isNaN(value) ? '' : value;
41055         
41056     },
41057     
41058     fixPrecision : function(value)
41059     {
41060         if(this.thousandsDelimiter) {
41061             value += "";
41062             r = new RegExp(",", "g");
41063             value = value.replace(r, "");
41064         }
41065         
41066         var nan = isNaN(value);
41067         
41068         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41069             return nan ? '' : value;
41070         }
41071         return parseFloat(value).toFixed(this.decimalPrecision);
41072     },
41073     
41074     decimalPrecisionFcn : function(v)
41075     {
41076         return Math.floor(v);
41077     },
41078     
41079     validateValue : function(value)
41080     {
41081         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41082             return false;
41083         }
41084         
41085         var num = this.parseValue(value);
41086         
41087         if(isNaN(num)){
41088             this.markInvalid(String.format(this.nanText, value));
41089             return false;
41090         }
41091         
41092         if(num < this.minValue){
41093             this.markInvalid(String.format(this.minText, this.minValue));
41094             return false;
41095         }
41096         
41097         if(num > this.maxValue){
41098             this.markInvalid(String.format(this.maxText, this.maxValue));
41099             return false;
41100         }
41101         
41102         return true;
41103     },
41104     
41105     validate : function()
41106     {
41107         if(this.disabled || this.allowBlank){
41108             this.markValid();
41109             return true;
41110         }
41111         
41112         var currency = this.getCurrency();
41113         
41114         if(this.validateValue(this.getRawValue()) && currency.length){
41115             this.markValid();
41116             return true;
41117         }
41118         
41119         this.markInvalid();
41120         return false;
41121     },
41122     
41123     getName: function()
41124     {
41125         return this.name;
41126     },
41127     
41128     beforeBlur : function()
41129     {
41130         if(!this.castInt){
41131             return;
41132         }
41133         
41134         var v = this.parseValue(this.getRawValue());
41135         
41136         if(v || v == 0){
41137             this.setValue(v);
41138         }
41139     },
41140     
41141     onBlur : function()
41142     {
41143         this.beforeBlur();
41144         
41145         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41146             //this.el.removeClass(this.focusClass);
41147         }
41148         
41149         this.hasFocus = false;
41150         
41151         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41152             this.validate();
41153         }
41154         
41155         var v = this.getValue();
41156         
41157         if(String(v) !== String(this.startValue)){
41158             this.fireEvent('change', this, v, this.startValue);
41159         }
41160         
41161         this.fireEvent("blur", this);
41162     },
41163     
41164     inputEl : function()
41165     {
41166         return this.el.select('.roo-money-amount-input', true).first();
41167     },
41168     
41169     currencyEl : function()
41170     {
41171         return this.el.select('.roo-money-currency-input', true).first();
41172     },
41173     
41174     hiddenEl : function()
41175     {
41176         return this.el.select('input.hidden-number-input',true).first();
41177     }
41178     
41179 });