roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     }
558    
559 });
560
561  /*
562  * - LGPL
563  *
564  * button
565  * 
566  */
567
568 /**
569  * @class Roo.bootstrap.Button
570  * @extends Roo.bootstrap.Component
571  * Bootstrap Button class
572  * @cfg {String} html The button content
573  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
574  * @cfg {String} size ( lg | sm | xs)
575  * @cfg {String} tag ( a | input | submit)
576  * @cfg {String} href empty or href
577  * @cfg {Boolean} disabled default false;
578  * @cfg {Boolean} isClose default false;
579  * @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)
580  * @cfg {String} badge text for badge
581  * @cfg {String} theme (default|glow)  
582  * @cfg {Boolean} inverse dark themed version
583  * @cfg {Boolean} toggle is it a slidy toggle button
584  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
585  * @cfg {String} ontext text for on slidy toggle state
586  * @cfg {String} offtext text for off slidy toggle state
587  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
588  * @cfg {Boolean} removeClass remove the standard class..
589  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
590  * 
591  * @constructor
592  * Create a new button
593  * @param {Object} config The config object
594  */
595
596
597 Roo.bootstrap.Button = function(config){
598     Roo.bootstrap.Button.superclass.constructor.call(this, config);
599     this.weightClass = ["btn-default", 
600                        "btn-primary", 
601                        "btn-success", 
602                        "btn-info", 
603                        "btn-warning",
604                        "btn-danger",
605                        "btn-link"
606                       ],  
607     this.addEvents({
608         // raw events
609         /**
610          * @event click
611          * When a butotn is pressed
612          * @param {Roo.bootstrap.Button} btn
613          * @param {Roo.EventObject} e
614          */
615         "click" : true,
616          /**
617          * @event toggle
618          * After the button has been toggles
619          * @param {Roo.bootstrap.Button} btn
620          * @param {Roo.EventObject} e
621          * @param {boolean} pressed (also available as button.pressed)
622          */
623         "toggle" : true
624     });
625 };
626
627 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
628     html: false,
629     active: false,
630     weight: '',
631     size: '',
632     tag: 'button',
633     href: '',
634     disabled: false,
635     isClose: false,
636     glyphicon: '',
637     badge: '',
638     theme: 'default',
639     inverse: false,
640     
641     toggle: false,
642     ontext: 'ON',
643     offtext: 'OFF',
644     defaulton: true,
645     preventDefault: true,
646     removeClass: false,
647     name: false,
648     target: false,
649      
650     pressed : null,
651      
652     
653     getAutoCreate : function(){
654         
655         var cfg = {
656             tag : 'button',
657             cls : 'roo-button',
658             html: ''
659         };
660         
661         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
662             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
663             this.tag = 'button';
664         } else {
665             cfg.tag = this.tag;
666         }
667         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
668         
669         if (this.toggle == true) {
670             cfg={
671                 tag: 'div',
672                 cls: 'slider-frame roo-button',
673                 cn: [
674                     {
675                         tag: 'span',
676                         'data-on-text':'ON',
677                         'data-off-text':'OFF',
678                         cls: 'slider-button',
679                         html: this.offtext
680                     }
681                 ]
682             };
683             
684             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
685                 cfg.cls += ' '+this.weight;
686             }
687             
688             return cfg;
689         }
690         
691         if (this.isClose) {
692             cfg.cls += ' close';
693             
694             cfg["aria-hidden"] = true;
695             
696             cfg.html = "&times;";
697             
698             return cfg;
699         }
700         
701          
702         if (this.theme==='default') {
703             cfg.cls = 'btn roo-button';
704             
705             //if (this.parentType != 'Navbar') {
706             this.weight = this.weight.length ?  this.weight : 'default';
707             //}
708             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
709                 
710                 cfg.cls += ' btn-' + this.weight;
711             }
712         } else if (this.theme==='glow') {
713             
714             cfg.tag = 'a';
715             cfg.cls = 'btn-glow roo-button';
716             
717             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
718                 
719                 cfg.cls += ' ' + this.weight;
720             }
721         }
722    
723         
724         if (this.inverse) {
725             this.cls += ' inverse';
726         }
727         
728         
729         if (this.active || this.pressed === true) {
730             cfg.cls += ' active';
731         }
732         
733         if (this.disabled) {
734             cfg.disabled = 'disabled';
735         }
736         
737         if (this.items) {
738             Roo.log('changing to ul' );
739             cfg.tag = 'ul';
740             this.glyphicon = 'caret';
741         }
742         
743         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
744          
745         //gsRoo.log(this.parentType);
746         if (this.parentType === 'Navbar' && !this.parent().bar) {
747             Roo.log('changing to li?');
748             
749             cfg.tag = 'li';
750             
751             cfg.cls = '';
752             cfg.cn =  [{
753                 tag : 'a',
754                 cls : 'roo-button',
755                 html : this.html,
756                 href : this.href || '#'
757             }];
758             if (this.menu) {
759                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
760                 cfg.cls += ' dropdown';
761             }   
762             
763             delete cfg.html;
764             
765         }
766         
767        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
768         
769         if (this.glyphicon) {
770             cfg.html = ' ' + cfg.html;
771             
772             cfg.cn = [
773                 {
774                     tag: 'span',
775                     cls: 'glyphicon glyphicon-' + this.glyphicon
776                 }
777             ];
778         }
779         
780         if (this.badge) {
781             cfg.html += ' ';
782             
783             cfg.tag = 'a';
784             
785 //            cfg.cls='btn roo-button';
786             
787             cfg.href=this.href;
788             
789             var value = cfg.html;
790             
791             if(this.glyphicon){
792                 value = {
793                             tag: 'span',
794                             cls: 'glyphicon glyphicon-' + this.glyphicon,
795                             html: this.html
796                         };
797                 
798             }
799             
800             cfg.cn = [
801                 value,
802                 {
803                     tag: 'span',
804                     cls: 'badge',
805                     html: this.badge
806                 }
807             ];
808             
809             cfg.html='';
810         }
811         
812         if (this.menu) {
813             cfg.cls += ' dropdown';
814             cfg.html = typeof(cfg.html) != 'undefined' ?
815                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
816         }
817         
818         if (cfg.tag !== 'a' && this.href !== '') {
819             throw "Tag must be a to set href.";
820         } else if (this.href.length > 0) {
821             cfg.href = this.href;
822         }
823         
824         if(this.removeClass){
825             cfg.cls = '';
826         }
827         
828         if(this.target){
829             cfg.target = this.target;
830         }
831         
832         return cfg;
833     },
834     initEvents: function() {
835        // Roo.log('init events?');
836 //        Roo.log(this.el.dom);
837         // add the menu...
838         
839         if (typeof (this.menu) != 'undefined') {
840             this.menu.parentType = this.xtype;
841             this.menu.triggerEl = this.el;
842             this.addxtype(Roo.apply({}, this.menu));
843         }
844
845
846        if (this.el.hasClass('roo-button')) {
847             this.el.on('click', this.onClick, this);
848        } else {
849             this.el.select('.roo-button').on('click', this.onClick, this);
850        }
851        
852        if(this.removeClass){
853            this.el.on('click', this.onClick, this);
854        }
855        
856        this.el.enableDisplayMode();
857         
858     },
859     onClick : function(e)
860     {
861         if (this.disabled) {
862             return;
863         }
864         
865         Roo.log('button on click ');
866         if(this.preventDefault){
867             e.preventDefault();
868         }
869         
870         if (this.pressed === true || this.pressed === false) {
871             this.toggleActive(e);
872         }
873         
874         
875         this.fireEvent('click', this, e);
876     },
877     
878     /**
879      * Enables this button
880      */
881     enable : function()
882     {
883         this.disabled = false;
884         this.el.removeClass('disabled');
885     },
886     
887     /**
888      * Disable this button
889      */
890     disable : function()
891     {
892         this.disabled = true;
893         this.el.addClass('disabled');
894     },
895      /**
896      * sets the active state on/off, 
897      * @param {Boolean} state (optional) Force a particular state
898      */
899     setActive : function(v) {
900         
901         this.el[v ? 'addClass' : 'removeClass']('active');
902         this.pressed = v;
903     },
904      /**
905      * toggles the current active state 
906      */
907     toggleActive : function(e)
908     {
909         this.setActive(!this.pressed);
910         this.fireEvent('toggle', this, e, !this.pressed);
911     },
912      /**
913      * get the current active state
914      * @return {boolean} true if it's active
915      */
916     isActive : function()
917     {
918         return this.el.hasClass('active');
919     },
920     /**
921      * set the text of the first selected button
922      */
923     setText : function(str)
924     {
925         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
926     },
927     /**
928      * get the text of the first selected button
929      */
930     getText : function()
931     {
932         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
933     },
934     
935     setWeight : function(str)
936     {
937         this.el.removeClass(this.weightClass);
938         this.el.addClass('btn-' + str);        
939     }
940     
941     
942 });
943
944  /*
945  * - LGPL
946  *
947  * column
948  * 
949  */
950
951 /**
952  * @class Roo.bootstrap.Column
953  * @extends Roo.bootstrap.Component
954  * Bootstrap Column class
955  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
956  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
957  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
958  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
959  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
960  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
961  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
962  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
963  *
964  * 
965  * @cfg {Boolean} hidden (true|false) hide the element
966  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
967  * @cfg {String} fa (ban|check|...) font awesome icon
968  * @cfg {Number} fasize (1|2|....) font awsome size
969
970  * @cfg {String} icon (info-sign|check|...) glyphicon name
971
972  * @cfg {String} html content of column.
973  * 
974  * @constructor
975  * Create a new Column
976  * @param {Object} config The config object
977  */
978
979 Roo.bootstrap.Column = function(config){
980     Roo.bootstrap.Column.superclass.constructor.call(this, config);
981 };
982
983 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
984     
985     xs: false,
986     sm: false,
987     md: false,
988     lg: false,
989     xsoff: false,
990     smoff: false,
991     mdoff: false,
992     lgoff: false,
993     html: '',
994     offset: 0,
995     alert: false,
996     fa: false,
997     icon : false,
998     hidden : false,
999     fasize : 1,
1000     
1001     getAutoCreate : function(){
1002         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1003         
1004         cfg = {
1005             tag: 'div',
1006             cls: 'column'
1007         };
1008         
1009         var settings=this;
1010         ['xs','sm','md','lg'].map(function(size){
1011             //Roo.log( size + ':' + settings[size]);
1012             
1013             if (settings[size+'off'] !== false) {
1014                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1015             }
1016             
1017             if (settings[size] === false) {
1018                 return;
1019             }
1020             
1021             if (!settings[size]) { // 0 = hidden
1022                 cfg.cls += ' hidden-' + size;
1023                 return;
1024             }
1025             cfg.cls += ' col-' + size + '-' + settings[size];
1026             
1027         });
1028         
1029         if (this.hidden) {
1030             cfg.cls += ' hidden';
1031         }
1032         
1033         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1034             cfg.cls +=' alert alert-' + this.alert;
1035         }
1036         
1037         
1038         if (this.html.length) {
1039             cfg.html = this.html;
1040         }
1041         if (this.fa) {
1042             var fasize = '';
1043             if (this.fasize > 1) {
1044                 fasize = ' fa-' + this.fasize + 'x';
1045             }
1046             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1047             
1048             
1049         }
1050         if (this.icon) {
1051             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1052         }
1053         
1054         return cfg;
1055     }
1056    
1057 });
1058
1059  
1060
1061  /*
1062  * - LGPL
1063  *
1064  * page container.
1065  * 
1066  */
1067
1068
1069 /**
1070  * @class Roo.bootstrap.Container
1071  * @extends Roo.bootstrap.Component
1072  * Bootstrap Container class
1073  * @cfg {Boolean} jumbotron is it a jumbotron element
1074  * @cfg {String} html content of element
1075  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1076  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1077  * @cfg {String} header content of header (for panel)
1078  * @cfg {String} footer content of footer (for panel)
1079  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1080  * @cfg {String} tag (header|aside|section) type of HTML tag.
1081  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1082  * @cfg {String} fa font awesome icon
1083  * @cfg {String} icon (info-sign|check|...) glyphicon name
1084  * @cfg {Boolean} hidden (true|false) hide the element
1085  * @cfg {Boolean} expandable (true|false) default false
1086  * @cfg {Boolean} expanded (true|false) default true
1087  * @cfg {String} rheader contet on the right of header
1088  * @cfg {Boolean} clickable (true|false) default false
1089
1090  *     
1091  * @constructor
1092  * Create a new Container
1093  * @param {Object} config The config object
1094  */
1095
1096 Roo.bootstrap.Container = function(config){
1097     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1098     
1099     this.addEvents({
1100         // raw events
1101          /**
1102          * @event expand
1103          * After the panel has been expand
1104          * 
1105          * @param {Roo.bootstrap.Container} this
1106          */
1107         "expand" : true,
1108         /**
1109          * @event collapse
1110          * After the panel has been collapsed
1111          * 
1112          * @param {Roo.bootstrap.Container} this
1113          */
1114         "collapse" : true,
1115         /**
1116          * @event click
1117          * When a element is chick
1118          * @param {Roo.bootstrap.Container} this
1119          * @param {Roo.EventObject} e
1120          */
1121         "click" : true
1122     });
1123 };
1124
1125 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1126     
1127     jumbotron : false,
1128     well: '',
1129     panel : '',
1130     header: '',
1131     footer : '',
1132     sticky: '',
1133     tag : false,
1134     alert : false,
1135     fa: false,
1136     icon : false,
1137     expandable : false,
1138     rheader : '',
1139     expanded : true,
1140     clickable: false,
1141   
1142      
1143     getChildContainer : function() {
1144         
1145         if(!this.el){
1146             return false;
1147         }
1148         
1149         if (this.panel.length) {
1150             return this.el.select('.panel-body',true).first();
1151         }
1152         
1153         return this.el;
1154     },
1155     
1156     
1157     getAutoCreate : function(){
1158         
1159         var cfg = {
1160             tag : this.tag || 'div',
1161             html : '',
1162             cls : ''
1163         };
1164         if (this.jumbotron) {
1165             cfg.cls = 'jumbotron';
1166         }
1167         
1168         
1169         
1170         // - this is applied by the parent..
1171         //if (this.cls) {
1172         //    cfg.cls = this.cls + '';
1173         //}
1174         
1175         if (this.sticky.length) {
1176             
1177             var bd = Roo.get(document.body);
1178             if (!bd.hasClass('bootstrap-sticky')) {
1179                 bd.addClass('bootstrap-sticky');
1180                 Roo.select('html',true).setStyle('height', '100%');
1181             }
1182              
1183             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1184         }
1185         
1186         
1187         if (this.well.length) {
1188             switch (this.well) {
1189                 case 'lg':
1190                 case 'sm':
1191                     cfg.cls +=' well well-' +this.well;
1192                     break;
1193                 default:
1194                     cfg.cls +=' well';
1195                     break;
1196             }
1197         }
1198         
1199         if (this.hidden) {
1200             cfg.cls += ' hidden';
1201         }
1202         
1203         
1204         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1205             cfg.cls +=' alert alert-' + this.alert;
1206         }
1207         
1208         var body = cfg;
1209         
1210         if (this.panel.length) {
1211             cfg.cls += ' panel panel-' + this.panel;
1212             cfg.cn = [];
1213             if (this.header.length) {
1214                 
1215                 var h = [];
1216                 
1217                 if(this.expandable){
1218                     
1219                     cfg.cls = cfg.cls + ' expandable';
1220                     
1221                     h.push({
1222                         tag: 'i',
1223                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1224                     });
1225                     
1226                 }
1227                 
1228                 h.push(
1229                     {
1230                         tag: 'span',
1231                         cls : 'panel-title',
1232                         html : (this.expandable ? '&nbsp;' : '') + this.header
1233                     },
1234                     {
1235                         tag: 'span',
1236                         cls: 'panel-header-right',
1237                         html: this.rheader
1238                     }
1239                 );
1240                 
1241                 cfg.cn.push({
1242                     cls : 'panel-heading',
1243                     style : this.expandable ? 'cursor: pointer' : '',
1244                     cn : h
1245                 });
1246                 
1247             }
1248             
1249             body = false;
1250             cfg.cn.push({
1251                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1252                 html : this.html
1253             });
1254             
1255             
1256             if (this.footer.length) {
1257                 cfg.cn.push({
1258                     cls : 'panel-footer',
1259                     html : this.footer
1260                     
1261                 });
1262             }
1263             
1264         }
1265         
1266         if (body) {
1267             body.html = this.html || cfg.html;
1268             // prefix with the icons..
1269             if (this.fa) {
1270                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1271             }
1272             if (this.icon) {
1273                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1274             }
1275             
1276             
1277         }
1278         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1279             cfg.cls =  'container';
1280         }
1281         
1282         return cfg;
1283     },
1284     
1285     initEvents: function() 
1286     {
1287         if(this.expandable){
1288             var headerEl = this.headerEl();
1289         
1290             if(headerEl){
1291                 headerEl.on('click', this.onToggleClick, this);
1292             }
1293         }
1294         
1295         if(this.clickable){
1296             this.el.on('click', this.onClick, this);
1297         }
1298         
1299     },
1300     
1301     onToggleClick : function()
1302     {
1303         var headerEl = this.headerEl();
1304         
1305         if(!headerEl){
1306             return;
1307         }
1308         
1309         if(this.expanded){
1310             this.collapse();
1311             return;
1312         }
1313         
1314         this.expand();
1315     },
1316     
1317     expand : function()
1318     {
1319         if(this.fireEvent('expand', this)) {
1320             
1321             this.expanded = true;
1322             
1323             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1324             
1325             this.el.select('.panel-body',true).first().removeClass('hide');
1326             
1327             var toggleEl = this.toggleEl();
1328
1329             if(!toggleEl){
1330                 return;
1331             }
1332
1333             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1334         }
1335         
1336     },
1337     
1338     collapse : function()
1339     {
1340         if(this.fireEvent('collapse', this)) {
1341             
1342             this.expanded = false;
1343             
1344             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1345             this.el.select('.panel-body',true).first().addClass('hide');
1346         
1347             var toggleEl = this.toggleEl();
1348
1349             if(!toggleEl){
1350                 return;
1351             }
1352
1353             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1354         }
1355     },
1356     
1357     toggleEl : function()
1358     {
1359         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1360             return;
1361         }
1362         
1363         return this.el.select('.panel-heading .fa',true).first();
1364     },
1365     
1366     headerEl : function()
1367     {
1368         if(!this.el || !this.panel.length || !this.header.length){
1369             return;
1370         }
1371         
1372         return this.el.select('.panel-heading',true).first()
1373     },
1374     
1375     bodyEl : function()
1376     {
1377         if(!this.el || !this.panel.length){
1378             return;
1379         }
1380         
1381         return this.el.select('.panel-body',true).first()
1382     },
1383     
1384     titleEl : function()
1385     {
1386         if(!this.el || !this.panel.length || !this.header.length){
1387             return;
1388         }
1389         
1390         return this.el.select('.panel-title',true).first();
1391     },
1392     
1393     setTitle : function(v)
1394     {
1395         var titleEl = this.titleEl();
1396         
1397         if(!titleEl){
1398             return;
1399         }
1400         
1401         titleEl.dom.innerHTML = v;
1402     },
1403     
1404     getTitle : function()
1405     {
1406         
1407         var titleEl = this.titleEl();
1408         
1409         if(!titleEl){
1410             return '';
1411         }
1412         
1413         return titleEl.dom.innerHTML;
1414     },
1415     
1416     setRightTitle : function(v)
1417     {
1418         var t = this.el.select('.panel-header-right',true).first();
1419         
1420         if(!t){
1421             return;
1422         }
1423         
1424         t.dom.innerHTML = v;
1425     },
1426     
1427     onClick : function(e)
1428     {
1429         e.preventDefault();
1430         
1431         this.fireEvent('click', this, e);
1432     }
1433 });
1434
1435  /*
1436  * - LGPL
1437  *
1438  * image
1439  * 
1440  */
1441
1442
1443 /**
1444  * @class Roo.bootstrap.Img
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Img class
1447  * @cfg {Boolean} imgResponsive false | true
1448  * @cfg {String} border rounded | circle | thumbnail
1449  * @cfg {String} src image source
1450  * @cfg {String} alt image alternative text
1451  * @cfg {String} href a tag href
1452  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1453  * @cfg {String} xsUrl xs image source
1454  * @cfg {String} smUrl sm image source
1455  * @cfg {String} mdUrl md image source
1456  * @cfg {String} lgUrl lg image source
1457  * 
1458  * @constructor
1459  * Create a new Input
1460  * @param {Object} config The config object
1461  */
1462
1463 Roo.bootstrap.Img = function(config){
1464     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1465     
1466     this.addEvents({
1467         // img events
1468         /**
1469          * @event click
1470          * The img click event for the img.
1471          * @param {Roo.EventObject} e
1472          */
1473         "click" : true
1474     });
1475 };
1476
1477 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1478     
1479     imgResponsive: true,
1480     border: '',
1481     src: 'about:blank',
1482     href: false,
1483     target: false,
1484     xsUrl: '',
1485     smUrl: '',
1486     mdUrl: '',
1487     lgUrl: '',
1488
1489     getAutoCreate : function()
1490     {   
1491         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1492             return this.createSingleImg();
1493         }
1494         
1495         var cfg = {
1496             tag: 'div',
1497             cls: 'roo-image-responsive-group',
1498             cn: []
1499         };
1500         var _this = this;
1501         
1502         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1503             
1504             if(!_this[size + 'Url']){
1505                 return;
1506             }
1507             
1508             var img = {
1509                 tag: 'img',
1510                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1511                 html: _this.html || cfg.html,
1512                 src: _this[size + 'Url']
1513             };
1514             
1515             img.cls += ' roo-image-responsive-' + size;
1516             
1517             var s = ['xs', 'sm', 'md', 'lg'];
1518             
1519             s.splice(s.indexOf(size), 1);
1520             
1521             Roo.each(s, function(ss){
1522                 img.cls += ' hidden-' + ss;
1523             });
1524             
1525             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1526                 cfg.cls += ' img-' + _this.border;
1527             }
1528             
1529             if(_this.alt){
1530                 cfg.alt = _this.alt;
1531             }
1532             
1533             if(_this.href){
1534                 var a = {
1535                     tag: 'a',
1536                     href: _this.href,
1537                     cn: [
1538                         img
1539                     ]
1540                 };
1541
1542                 if(this.target){
1543                     a.target = _this.target;
1544                 }
1545             }
1546             
1547             cfg.cn.push((_this.href) ? a : img);
1548             
1549         });
1550         
1551         return cfg;
1552     },
1553     
1554     createSingleImg : function()
1555     {
1556         var cfg = {
1557             tag: 'img',
1558             cls: (this.imgResponsive) ? 'img-responsive' : '',
1559             html : null,
1560             src : 'about:blank'  // just incase src get's set to undefined?!?
1561         };
1562         
1563         cfg.html = this.html || cfg.html;
1564         
1565         cfg.src = this.src || cfg.src;
1566         
1567         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1568             cfg.cls += ' img-' + this.border;
1569         }
1570         
1571         if(this.alt){
1572             cfg.alt = this.alt;
1573         }
1574         
1575         if(this.href){
1576             var a = {
1577                 tag: 'a',
1578                 href: this.href,
1579                 cn: [
1580                     cfg
1581                 ]
1582             };
1583             
1584             if(this.target){
1585                 a.target = this.target;
1586             }
1587             
1588         }
1589         
1590         return (this.href) ? a : cfg;
1591     },
1592     
1593     initEvents: function() 
1594     {
1595         if(!this.href){
1596             this.el.on('click', this.onClick, this);
1597         }
1598         
1599     },
1600     
1601     onClick : function(e)
1602     {
1603         Roo.log('img onclick');
1604         this.fireEvent('click', this, e);
1605     },
1606     /**
1607      * Sets the url of the image - used to update it
1608      * @param {String} url the url of the image
1609      */
1610     
1611     setSrc : function(url)
1612     {
1613         this.src =  url;
1614         
1615         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1616             this.el.dom.src =  url;
1617             return;
1618         }
1619         
1620         this.el.select('img', true).first().dom.src =  url;
1621     }
1622     
1623     
1624    
1625 });
1626
1627  /*
1628  * - LGPL
1629  *
1630  * image
1631  * 
1632  */
1633
1634
1635 /**
1636  * @class Roo.bootstrap.Link
1637  * @extends Roo.bootstrap.Component
1638  * Bootstrap Link Class
1639  * @cfg {String} alt image alternative text
1640  * @cfg {String} href a tag href
1641  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1642  * @cfg {String} html the content of the link.
1643  * @cfg {String} anchor name for the anchor link
1644  * @cfg {String} fa - favicon
1645
1646  * @cfg {Boolean} preventDefault (true | false) default false
1647
1648  * 
1649  * @constructor
1650  * Create a new Input
1651  * @param {Object} config The config object
1652  */
1653
1654 Roo.bootstrap.Link = function(config){
1655     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1656     
1657     this.addEvents({
1658         // img events
1659         /**
1660          * @event click
1661          * The img click event for the img.
1662          * @param {Roo.EventObject} e
1663          */
1664         "click" : true
1665     });
1666 };
1667
1668 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1669     
1670     href: false,
1671     target: false,
1672     preventDefault: false,
1673     anchor : false,
1674     alt : false,
1675     fa: false,
1676
1677
1678     getAutoCreate : function()
1679     {
1680         var html = this.html || '';
1681         
1682         if (this.fa !== false) {
1683             html = '<i class="fa fa-' + this.fa + '"></i>';
1684         }
1685         var cfg = {
1686             tag: 'a'
1687         };
1688         // anchor's do not require html/href...
1689         if (this.anchor === false) {
1690             cfg.html = html;
1691             cfg.href = this.href || '#';
1692         } else {
1693             cfg.name = this.anchor;
1694             if (this.html !== false || this.fa !== false) {
1695                 cfg.html = html;
1696             }
1697             if (this.href !== false) {
1698                 cfg.href = this.href;
1699             }
1700         }
1701         
1702         if(this.alt !== false){
1703             cfg.alt = this.alt;
1704         }
1705         
1706         
1707         if(this.target !== false) {
1708             cfg.target = this.target;
1709         }
1710         
1711         return cfg;
1712     },
1713     
1714     initEvents: function() {
1715         
1716         if(!this.href || this.preventDefault){
1717             this.el.on('click', this.onClick, this);
1718         }
1719     },
1720     
1721     onClick : function(e)
1722     {
1723         if(this.preventDefault){
1724             e.preventDefault();
1725         }
1726         //Roo.log('img onclick');
1727         this.fireEvent('click', this, e);
1728     }
1729    
1730 });
1731
1732  /*
1733  * - LGPL
1734  *
1735  * header
1736  * 
1737  */
1738
1739 /**
1740  * @class Roo.bootstrap.Header
1741  * @extends Roo.bootstrap.Component
1742  * Bootstrap Header class
1743  * @cfg {String} html content of header
1744  * @cfg {Number} level (1|2|3|4|5|6) default 1
1745  * 
1746  * @constructor
1747  * Create a new Header
1748  * @param {Object} config The config object
1749  */
1750
1751
1752 Roo.bootstrap.Header  = function(config){
1753     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1754 };
1755
1756 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1757     
1758     //href : false,
1759     html : false,
1760     level : 1,
1761     
1762     
1763     
1764     getAutoCreate : function(){
1765         
1766         
1767         
1768         var cfg = {
1769             tag: 'h' + (1 *this.level),
1770             html: this.html || ''
1771         } ;
1772         
1773         return cfg;
1774     }
1775    
1776 });
1777
1778  
1779
1780  /*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790  
1791 /**
1792  * @class Roo.bootstrap.MenuMgr
1793  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1794  * @singleton
1795  */
1796 Roo.bootstrap.MenuMgr = function(){
1797    var menus, active, groups = {}, attached = false, lastShow = new Date();
1798
1799    // private - called when first menu is created
1800    function init(){
1801        menus = {};
1802        active = new Roo.util.MixedCollection();
1803        Roo.get(document).addKeyListener(27, function(){
1804            if(active.length > 0){
1805                hideAll();
1806            }
1807        });
1808    }
1809
1810    // private
1811    function hideAll(){
1812        if(active && active.length > 0){
1813            var c = active.clone();
1814            c.each(function(m){
1815                m.hide();
1816            });
1817        }
1818    }
1819
1820    // private
1821    function onHide(m){
1822        active.remove(m);
1823        if(active.length < 1){
1824            Roo.get(document).un("mouseup", onMouseDown);
1825             
1826            attached = false;
1827        }
1828    }
1829
1830    // private
1831    function onShow(m){
1832        var last = active.last();
1833        lastShow = new Date();
1834        active.add(m);
1835        if(!attached){
1836           Roo.get(document).on("mouseup", onMouseDown);
1837            
1838            attached = true;
1839        }
1840        if(m.parentMenu){
1841           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1842           m.parentMenu.activeChild = m;
1843        }else if(last && last.isVisible()){
1844           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1845        }
1846    }
1847
1848    // private
1849    function onBeforeHide(m){
1850        if(m.activeChild){
1851            m.activeChild.hide();
1852        }
1853        if(m.autoHideTimer){
1854            clearTimeout(m.autoHideTimer);
1855            delete m.autoHideTimer;
1856        }
1857    }
1858
1859    // private
1860    function onBeforeShow(m){
1861        var pm = m.parentMenu;
1862        if(!pm && !m.allowOtherMenus){
1863            hideAll();
1864        }else if(pm && pm.activeChild && active != m){
1865            pm.activeChild.hide();
1866        }
1867    }
1868
1869    // private this should really trigger on mouseup..
1870    function onMouseDown(e){
1871         Roo.log("on Mouse Up");
1872         
1873         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1874             Roo.log("MenuManager hideAll");
1875             hideAll();
1876             e.stopEvent();
1877         }
1878         
1879         
1880    }
1881
1882    // private
1883    function onBeforeCheck(mi, state){
1884        if(state){
1885            var g = groups[mi.group];
1886            for(var i = 0, l = g.length; i < l; i++){
1887                if(g[i] != mi){
1888                    g[i].setChecked(false);
1889                }
1890            }
1891        }
1892    }
1893
1894    return {
1895
1896        /**
1897         * Hides all menus that are currently visible
1898         */
1899        hideAll : function(){
1900             hideAll();  
1901        },
1902
1903        // private
1904        register : function(menu){
1905            if(!menus){
1906                init();
1907            }
1908            menus[menu.id] = menu;
1909            menu.on("beforehide", onBeforeHide);
1910            menu.on("hide", onHide);
1911            menu.on("beforeshow", onBeforeShow);
1912            menu.on("show", onShow);
1913            var g = menu.group;
1914            if(g && menu.events["checkchange"]){
1915                if(!groups[g]){
1916                    groups[g] = [];
1917                }
1918                groups[g].push(menu);
1919                menu.on("checkchange", onCheck);
1920            }
1921        },
1922
1923         /**
1924          * Returns a {@link Roo.menu.Menu} object
1925          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1926          * be used to generate and return a new Menu instance.
1927          */
1928        get : function(menu){
1929            if(typeof menu == "string"){ // menu id
1930                return menus[menu];
1931            }else if(menu.events){  // menu instance
1932                return menu;
1933            }
1934            /*else if(typeof menu.length == 'number'){ // array of menu items?
1935                return new Roo.bootstrap.Menu({items:menu});
1936            }else{ // otherwise, must be a config
1937                return new Roo.bootstrap.Menu(menu);
1938            }
1939            */
1940            return false;
1941        },
1942
1943        // private
1944        unregister : function(menu){
1945            delete menus[menu.id];
1946            menu.un("beforehide", onBeforeHide);
1947            menu.un("hide", onHide);
1948            menu.un("beforeshow", onBeforeShow);
1949            menu.un("show", onShow);
1950            var g = menu.group;
1951            if(g && menu.events["checkchange"]){
1952                groups[g].remove(menu);
1953                menu.un("checkchange", onCheck);
1954            }
1955        },
1956
1957        // private
1958        registerCheckable : function(menuItem){
1959            var g = menuItem.group;
1960            if(g){
1961                if(!groups[g]){
1962                    groups[g] = [];
1963                }
1964                groups[g].push(menuItem);
1965                menuItem.on("beforecheckchange", onBeforeCheck);
1966            }
1967        },
1968
1969        // private
1970        unregisterCheckable : function(menuItem){
1971            var g = menuItem.group;
1972            if(g){
1973                groups[g].remove(menuItem);
1974                menuItem.un("beforecheckchange", onBeforeCheck);
1975            }
1976        }
1977    };
1978 }();/*
1979  * - LGPL
1980  *
1981  * menu
1982  * 
1983  */
1984
1985 /**
1986  * @class Roo.bootstrap.Menu
1987  * @extends Roo.bootstrap.Component
1988  * Bootstrap Menu class - container for MenuItems
1989  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1990  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1991  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1992  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1993  * 
1994  * @constructor
1995  * Create a new Menu
1996  * @param {Object} config The config object
1997  */
1998
1999
2000 Roo.bootstrap.Menu = function(config){
2001     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2002     if (this.registerMenu && this.type != 'treeview')  {
2003         Roo.bootstrap.MenuMgr.register(this);
2004     }
2005     this.addEvents({
2006         /**
2007          * @event beforeshow
2008          * Fires before this menu is displayed
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforeshow : true,
2012         /**
2013          * @event beforehide
2014          * Fires before this menu is hidden
2015          * @param {Roo.menu.Menu} this
2016          */
2017         beforehide : true,
2018         /**
2019          * @event show
2020          * Fires after this menu is displayed
2021          * @param {Roo.menu.Menu} this
2022          */
2023         show : true,
2024         /**
2025          * @event hide
2026          * Fires after this menu is hidden
2027          * @param {Roo.menu.Menu} this
2028          */
2029         hide : true,
2030         /**
2031          * @event click
2032          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2033          * @param {Roo.menu.Menu} this
2034          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2035          * @param {Roo.EventObject} e
2036          */
2037         click : true,
2038         /**
2039          * @event mouseover
2040          * Fires when the mouse is hovering over this menu
2041          * @param {Roo.menu.Menu} this
2042          * @param {Roo.EventObject} e
2043          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2044          */
2045         mouseover : true,
2046         /**
2047          * @event mouseout
2048          * Fires when the mouse exits this menu
2049          * @param {Roo.menu.Menu} this
2050          * @param {Roo.EventObject} e
2051          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2052          */
2053         mouseout : true,
2054         /**
2055          * @event itemclick
2056          * Fires when a menu item contained in this menu is clicked
2057          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2058          * @param {Roo.EventObject} e
2059          */
2060         itemclick: true
2061     });
2062     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2063 };
2064
2065 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2066     
2067    /// html : false,
2068     //align : '',
2069     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2070     type: false,
2071     /**
2072      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2073      */
2074     registerMenu : true,
2075     
2076     menuItems :false, // stores the menu items..
2077     
2078     hidden:true,
2079         
2080     parentMenu : false,
2081     
2082     stopEvent : true,
2083     
2084     isLink : false,
2085     
2086     getChildContainer : function() {
2087         return this.el;  
2088     },
2089     
2090     getAutoCreate : function(){
2091          
2092         //if (['right'].indexOf(this.align)!==-1) {
2093         //    cfg.cn[1].cls += ' pull-right'
2094         //}
2095         
2096         
2097         var cfg = {
2098             tag : 'ul',
2099             cls : 'dropdown-menu' ,
2100             style : 'z-index:1000'
2101             
2102         };
2103         
2104         if (this.type === 'submenu') {
2105             cfg.cls = 'submenu active';
2106         }
2107         if (this.type === 'treeview') {
2108             cfg.cls = 'treeview-menu';
2109         }
2110         
2111         return cfg;
2112     },
2113     initEvents : function() {
2114         
2115        // Roo.log("ADD event");
2116        // Roo.log(this.triggerEl.dom);
2117         
2118         this.triggerEl.on('click', this.onTriggerClick, this);
2119         
2120         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2121         
2122         this.triggerEl.addClass('dropdown-toggle');
2123         
2124         if (Roo.isTouch) {
2125             this.el.on('touchstart'  , this.onTouch, this);
2126         }
2127         this.el.on('click' , this.onClick, this);
2128
2129         this.el.on("mouseover", this.onMouseOver, this);
2130         this.el.on("mouseout", this.onMouseOut, this);
2131         
2132     },
2133     
2134     findTargetItem : function(e)
2135     {
2136         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2137         if(!t){
2138             return false;
2139         }
2140         //Roo.log(t);         Roo.log(t.id);
2141         if(t && t.id){
2142             //Roo.log(this.menuitems);
2143             return this.menuitems.get(t.id);
2144             
2145             //return this.items.get(t.menuItemId);
2146         }
2147         
2148         return false;
2149     },
2150     
2151     onTouch : function(e) 
2152     {
2153         Roo.log("menu.onTouch");
2154         //e.stopEvent(); this make the user popdown broken
2155         this.onClick(e);
2156     },
2157     
2158     onClick : function(e)
2159     {
2160         Roo.log("menu.onClick");
2161         
2162         var t = this.findTargetItem(e);
2163         if(!t || t.isContainer){
2164             return;
2165         }
2166         Roo.log(e);
2167         /*
2168         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2169             if(t == this.activeItem && t.shouldDeactivate(e)){
2170                 this.activeItem.deactivate();
2171                 delete this.activeItem;
2172                 return;
2173             }
2174             if(t.canActivate){
2175                 this.setActiveItem(t, true);
2176             }
2177             return;
2178             
2179             
2180         }
2181         */
2182        
2183         Roo.log('pass click event');
2184         
2185         t.onClick(e);
2186         
2187         this.fireEvent("click", this, t, e);
2188         
2189         var _this = this;
2190         
2191         if(!t.href.length || t.href == '#'){
2192             (function() { _this.hide(); }).defer(100);
2193         }
2194         
2195     },
2196     
2197     onMouseOver : function(e){
2198         var t  = this.findTargetItem(e);
2199         //Roo.log(t);
2200         //if(t){
2201         //    if(t.canActivate && !t.disabled){
2202         //        this.setActiveItem(t, true);
2203         //    }
2204         //}
2205         
2206         this.fireEvent("mouseover", this, e, t);
2207     },
2208     isVisible : function(){
2209         return !this.hidden;
2210     },
2211      onMouseOut : function(e){
2212         var t  = this.findTargetItem(e);
2213         
2214         //if(t ){
2215         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2216         //        this.activeItem.deactivate();
2217         //        delete this.activeItem;
2218         //    }
2219         //}
2220         this.fireEvent("mouseout", this, e, t);
2221     },
2222     
2223     
2224     /**
2225      * Displays this menu relative to another element
2226      * @param {String/HTMLElement/Roo.Element} element The element to align to
2227      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2228      * the element (defaults to this.defaultAlign)
2229      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2230      */
2231     show : function(el, pos, parentMenu){
2232         this.parentMenu = parentMenu;
2233         if(!this.el){
2234             this.render();
2235         }
2236         this.fireEvent("beforeshow", this);
2237         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2238     },
2239      /**
2240      * Displays this menu at a specific xy position
2241      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2242      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2243      */
2244     showAt : function(xy, parentMenu, /* private: */_e){
2245         this.parentMenu = parentMenu;
2246         if(!this.el){
2247             this.render();
2248         }
2249         if(_e !== false){
2250             this.fireEvent("beforeshow", this);
2251             //xy = this.el.adjustForConstraints(xy);
2252         }
2253         
2254         //this.el.show();
2255         this.hideMenuItems();
2256         this.hidden = false;
2257         this.triggerEl.addClass('open');
2258         
2259         // reassign x when hitting right
2260         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2261             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2262         }
2263         
2264         // reassign y when hitting bottom
2265         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2266             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2267         }
2268         
2269         // but the list may align on trigger left or trigger top... should it be a properity?
2270         
2271         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2272             this.el.setXY(xy);
2273         }
2274         
2275         this.focus();
2276         this.fireEvent("show", this);
2277     },
2278     
2279     focus : function(){
2280         return;
2281         if(!this.hidden){
2282             this.doFocus.defer(50, this);
2283         }
2284     },
2285
2286     doFocus : function(){
2287         if(!this.hidden){
2288             this.focusEl.focus();
2289         }
2290     },
2291
2292     /**
2293      * Hides this menu and optionally all parent menus
2294      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2295      */
2296     hide : function(deep)
2297     {
2298         
2299         this.hideMenuItems();
2300         if(this.el && this.isVisible()){
2301             this.fireEvent("beforehide", this);
2302             if(this.activeItem){
2303                 this.activeItem.deactivate();
2304                 this.activeItem = null;
2305             }
2306             this.triggerEl.removeClass('open');;
2307             this.hidden = true;
2308             this.fireEvent("hide", this);
2309         }
2310         if(deep === true && this.parentMenu){
2311             this.parentMenu.hide(true);
2312         }
2313     },
2314     
2315     onTriggerClick : function(e)
2316     {
2317         Roo.log('trigger click');
2318         
2319         var target = e.getTarget();
2320         
2321         Roo.log(target.nodeName.toLowerCase());
2322         
2323         if(target.nodeName.toLowerCase() === 'i'){
2324             e.preventDefault();
2325         }
2326         
2327     },
2328     
2329     onTriggerPress  : function(e)
2330     {
2331         Roo.log('trigger press');
2332         //Roo.log(e.getTarget());
2333        // Roo.log(this.triggerEl.dom);
2334        
2335         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2336         var pel = Roo.get(e.getTarget());
2337         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2338             Roo.log('is treeview or dropdown?');
2339             return;
2340         }
2341         
2342         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2343             return;
2344         }
2345         
2346         if (this.isVisible()) {
2347             Roo.log('hide');
2348             this.hide();
2349         } else {
2350             Roo.log('show');
2351             this.show(this.triggerEl, false, false);
2352         }
2353         
2354         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355             e.stopEvent();
2356         }
2357         
2358     },
2359        
2360     
2361     hideMenuItems : function()
2362     {
2363         Roo.log("hide Menu Items");
2364         if (!this.el) { 
2365             return;
2366         }
2367         //$(backdrop).remove()
2368         this.el.select('.open',true).each(function(aa) {
2369             
2370             aa.removeClass('open');
2371           //var parent = getParent($(this))
2372           //var relatedTarget = { relatedTarget: this }
2373           
2374            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2375           //if (e.isDefaultPrevented()) return
2376            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2377         });
2378     },
2379     addxtypeChild : function (tree, cntr) {
2380         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2381           
2382         this.menuitems.add(comp);
2383         return comp;
2384
2385     },
2386     getEl : function()
2387     {
2388         Roo.log(this.el);
2389         return this.el;
2390     },
2391     
2392     clear : function()
2393     {
2394         this.getEl().dom.innerHTML = '';
2395         this.menuitems.clear();
2396     }
2397 });
2398
2399  
2400  /*
2401  * - LGPL
2402  *
2403  * menu item
2404  * 
2405  */
2406
2407
2408 /**
2409  * @class Roo.bootstrap.MenuItem
2410  * @extends Roo.bootstrap.Component
2411  * Bootstrap MenuItem class
2412  * @cfg {String} html the menu label
2413  * @cfg {String} href the link
2414  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2415  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2416  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2417  * @cfg {String} fa favicon to show on left of menu item.
2418  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2419  * 
2420  * 
2421  * @constructor
2422  * Create a new MenuItem
2423  * @param {Object} config The config object
2424  */
2425
2426
2427 Roo.bootstrap.MenuItem = function(config){
2428     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2429     this.addEvents({
2430         // raw events
2431         /**
2432          * @event click
2433          * The raw click event for the entire grid.
2434          * @param {Roo.bootstrap.MenuItem} this
2435          * @param {Roo.EventObject} e
2436          */
2437         "click" : true
2438     });
2439 };
2440
2441 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2442     
2443     href : false,
2444     html : false,
2445     preventDefault: false,
2446     isContainer : false,
2447     active : false,
2448     fa: false,
2449     
2450     getAutoCreate : function(){
2451         
2452         if(this.isContainer){
2453             return {
2454                 tag: 'li',
2455                 cls: 'dropdown-menu-item'
2456             };
2457         }
2458         var ctag = {
2459             tag: 'span',
2460             html: 'Link'
2461         };
2462         
2463         var anc = {
2464             tag : 'a',
2465             href : '#',
2466             cn : [  ]
2467         };
2468         
2469         if (this.fa !== false) {
2470             anc.cn.push({
2471                 tag : 'i',
2472                 cls : 'fa fa-' + this.fa
2473             });
2474         }
2475         
2476         anc.cn.push(ctag);
2477         
2478         
2479         var cfg= {
2480             tag: 'li',
2481             cls: 'dropdown-menu-item',
2482             cn: [ anc ]
2483         };
2484         if (this.parent().type == 'treeview') {
2485             cfg.cls = 'treeview-menu';
2486         }
2487         if (this.active) {
2488             cfg.cls += ' active';
2489         }
2490         
2491         
2492         
2493         anc.href = this.href || cfg.cn[0].href ;
2494         ctag.html = this.html || cfg.cn[0].html ;
2495         return cfg;
2496     },
2497     
2498     initEvents: function()
2499     {
2500         if (this.parent().type == 'treeview') {
2501             this.el.select('a').on('click', this.onClick, this);
2502         }
2503         
2504         if (this.menu) {
2505             this.menu.parentType = this.xtype;
2506             this.menu.triggerEl = this.el;
2507             this.menu = this.addxtype(Roo.apply({}, this.menu));
2508         }
2509         
2510     },
2511     onClick : function(e)
2512     {
2513         Roo.log('item on click ');
2514         
2515         if(this.preventDefault){
2516             e.preventDefault();
2517         }
2518         //this.parent().hideMenuItems();
2519         
2520         this.fireEvent('click', this, e);
2521     },
2522     getEl : function()
2523     {
2524         return this.el;
2525     } 
2526 });
2527
2528  
2529
2530  /*
2531  * - LGPL
2532  *
2533  * menu separator
2534  * 
2535  */
2536
2537
2538 /**
2539  * @class Roo.bootstrap.MenuSeparator
2540  * @extends Roo.bootstrap.Component
2541  * Bootstrap MenuSeparator class
2542  * 
2543  * @constructor
2544  * Create a new MenuItem
2545  * @param {Object} config The config object
2546  */
2547
2548
2549 Roo.bootstrap.MenuSeparator = function(config){
2550     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2551 };
2552
2553 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2554     
2555     getAutoCreate : function(){
2556         var cfg = {
2557             cls: 'divider',
2558             tag : 'li'
2559         };
2560         
2561         return cfg;
2562     }
2563    
2564 });
2565
2566  
2567
2568  
2569 /*
2570 * Licence: LGPL
2571 */
2572
2573 /**
2574  * @class Roo.bootstrap.Modal
2575  * @extends Roo.bootstrap.Component
2576  * Bootstrap Modal class
2577  * @cfg {String} title Title of dialog
2578  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2579  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2580  * @cfg {Boolean} specificTitle default false
2581  * @cfg {Array} buttons Array of buttons or standard button set..
2582  * @cfg {String} buttonPosition (left|right|center) default right
2583  * @cfg {Boolean} animate default true
2584  * @cfg {Boolean} allow_close default true
2585  * @cfg {Boolean} fitwindow default false
2586  * @cfg {String} size (sm|lg) default empty
2587  * @cfg {Number} max_width set the max width of modal
2588  *
2589  *
2590  * @constructor
2591  * Create a new Modal Dialog
2592  * @param {Object} config The config object
2593  */
2594
2595 Roo.bootstrap.Modal = function(config){
2596     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2597     this.addEvents({
2598         // raw events
2599         /**
2600          * @event btnclick
2601          * The raw btnclick event for the button
2602          * @param {Roo.EventObject} e
2603          */
2604         "btnclick" : true,
2605         /**
2606          * @event resize
2607          * Fire when dialog resize
2608          * @param {Roo.bootstrap.Modal} this
2609          * @param {Roo.EventObject} e
2610          */
2611         "resize" : true
2612     });
2613     this.buttons = this.buttons || [];
2614
2615     if (this.tmpl) {
2616         this.tmpl = Roo.factory(this.tmpl);
2617     }
2618
2619 };
2620
2621 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2622
2623     title : 'test dialog',
2624
2625     buttons : false,
2626
2627     // set on load...
2628
2629     html: false,
2630
2631     tmp: false,
2632
2633     specificTitle: false,
2634
2635     buttonPosition: 'right',
2636
2637     allow_close : true,
2638
2639     animate : true,
2640
2641     fitwindow: false,
2642     
2643      // private
2644     dialogEl: false,
2645     bodyEl:  false,
2646     footerEl:  false,
2647     titleEl:  false,
2648     closeEl:  false,
2649
2650     size: '',
2651     
2652     max_width: 0,
2653     
2654     max_height: 0,
2655     
2656     fit_content: false,
2657
2658     onRender : function(ct, position)
2659     {
2660         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2661
2662         if(!this.el){
2663             var cfg = Roo.apply({},  this.getAutoCreate());
2664             cfg.id = Roo.id();
2665             //if(!cfg.name){
2666             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2667             //}
2668             //if (!cfg.name.length) {
2669             //    delete cfg.name;
2670            // }
2671             if (this.cls) {
2672                 cfg.cls += ' ' + this.cls;
2673             }
2674             if (this.style) {
2675                 cfg.style = this.style;
2676             }
2677             this.el = Roo.get(document.body).createChild(cfg, position);
2678         }
2679         //var type = this.el.dom.type;
2680
2681
2682         if(this.tabIndex !== undefined){
2683             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2684         }
2685
2686         this.dialogEl = this.el.select('.modal-dialog',true).first();
2687         this.bodyEl = this.el.select('.modal-body',true).first();
2688         this.closeEl = this.el.select('.modal-header .close', true).first();
2689         this.headerEl = this.el.select('.modal-header',true).first();
2690         this.titleEl = this.el.select('.modal-title',true).first();
2691         this.footerEl = this.el.select('.modal-footer',true).first();
2692
2693         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2694         
2695         //this.el.addClass("x-dlg-modal");
2696
2697         if (this.buttons.length) {
2698             Roo.each(this.buttons, function(bb) {
2699                 var b = Roo.apply({}, bb);
2700                 b.xns = b.xns || Roo.bootstrap;
2701                 b.xtype = b.xtype || 'Button';
2702                 if (typeof(b.listeners) == 'undefined') {
2703                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2704                 }
2705
2706                 var btn = Roo.factory(b);
2707
2708                 btn.render(this.el.select('.modal-footer div').first());
2709
2710             },this);
2711         }
2712         // render the children.
2713         var nitems = [];
2714
2715         if(typeof(this.items) != 'undefined'){
2716             var items = this.items;
2717             delete this.items;
2718
2719             for(var i =0;i < items.length;i++) {
2720                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2721             }
2722         }
2723
2724         this.items = nitems;
2725
2726         // where are these used - they used to be body/close/footer
2727
2728
2729         this.initEvents();
2730         //this.el.addClass([this.fieldClass, this.cls]);
2731
2732     },
2733
2734     getAutoCreate : function()
2735     {
2736         var bdy = {
2737                 cls : 'modal-body',
2738                 html : this.html || ''
2739         };
2740
2741         var title = {
2742             tag: 'h4',
2743             cls : 'modal-title',
2744             html : this.title
2745         };
2746
2747         if(this.specificTitle){
2748             title = this.title;
2749
2750         };
2751
2752         var header = [];
2753         if (this.allow_close) {
2754             header.push({
2755                 tag: 'button',
2756                 cls : 'close',
2757                 html : '&times'
2758             });
2759         }
2760
2761         header.push(title);
2762
2763         var size = '';
2764
2765         if(this.size.length){
2766             size = 'modal-' + this.size;
2767         }
2768
2769         var modal = {
2770             cls: "modal",
2771              cn : [
2772                 {
2773                     cls: "modal-dialog " + size,
2774                     cn : [
2775                         {
2776                             cls : "modal-content",
2777                             cn : [
2778                                 {
2779                                     cls : 'modal-header',
2780                                     cn : header
2781                                 },
2782                                 bdy,
2783                                 {
2784                                     cls : 'modal-footer',
2785                                     cn : [
2786                                         {
2787                                             tag: 'div',
2788                                             cls: 'btn-' + this.buttonPosition
2789                                         }
2790                                     ]
2791
2792                                 }
2793
2794
2795                             ]
2796
2797                         }
2798                     ]
2799
2800                 }
2801             ]
2802         };
2803
2804         if(this.animate){
2805             modal.cls += ' fade';
2806         }
2807
2808         return modal;
2809
2810     },
2811     getChildContainer : function() {
2812
2813          return this.bodyEl;
2814
2815     },
2816     getButtonContainer : function() {
2817          return this.el.select('.modal-footer div',true).first();
2818
2819     },
2820     initEvents : function()
2821     {
2822         if (this.allow_close) {
2823             this.closeEl.on('click', this.hide, this);
2824         }
2825         Roo.EventManager.onWindowResize(this.resize, this, true);
2826
2827
2828     },
2829
2830     resize : function()
2831     {
2832         this.maskEl.setSize(
2833             Roo.lib.Dom.getViewWidth(true),
2834             Roo.lib.Dom.getViewHeight(true)
2835         );
2836         
2837         if (this.fitwindow) {
2838             this.setSize(
2839                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2840                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2841             );
2842             return;
2843         }
2844         
2845         if(this.max_width !== 0) {
2846             
2847             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2848             
2849             if(this.height) {
2850                 this.setSize(w, this.height);
2851                 return;
2852             }
2853             
2854             if(this.max_height) {
2855                 this.setSize(w,Math.min(
2856                     this.max_height,
2857                     Roo.lib.Dom.getViewportHeight(true) - 60
2858                 ));
2859                 
2860                 return;
2861             }
2862             
2863             if(!this.fit_content) {
2864                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2865                 return;
2866             }
2867             
2868             this.setSize(w, Math.min(
2869                 60 +
2870                 this.headerEl.getHeight() + 
2871                 this.footerEl.getHeight() + 
2872                 this.getChildHeight(this.bodyEl.dom.childNodes),
2873                 Roo.lib.Dom.getViewportHeight(true) - 60)
2874             );
2875         }
2876         
2877     },
2878
2879     setSize : function(w,h)
2880     {
2881         if (!w && !h) {
2882             return;
2883         }
2884         
2885         this.resizeTo(w,h);
2886     },
2887
2888     show : function() {
2889
2890         if (!this.rendered) {
2891             this.render();
2892         }
2893
2894         //this.el.setStyle('display', 'block');
2895         this.el.removeClass('hideing');        
2896         this.el.addClass('show');
2897  
2898         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2899             var _this = this;
2900             (function(){
2901                 this.el.addClass('in');
2902             }).defer(50, this);
2903         }else{
2904             this.el.addClass('in');
2905         }
2906
2907         // not sure how we can show data in here..
2908         //if (this.tmpl) {
2909         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2910         //}
2911
2912         Roo.get(document.body).addClass("x-body-masked");
2913         
2914         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2915         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2916         this.maskEl.addClass('show');
2917         
2918         this.resize();
2919         
2920         this.fireEvent('show', this);
2921
2922         // set zindex here - otherwise it appears to be ignored...
2923         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2924
2925         (function () {
2926             this.items.forEach( function(e) {
2927                 e.layout ? e.layout() : false;
2928
2929             });
2930         }).defer(100,this);
2931
2932     },
2933     hide : function()
2934     {
2935         if(this.fireEvent("beforehide", this) !== false){
2936             this.maskEl.removeClass('show');
2937             Roo.get(document.body).removeClass("x-body-masked");
2938             this.el.removeClass('in');
2939             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2940
2941             if(this.animate){ // why
2942                 this.el.addClass('hideing');
2943                 (function(){
2944                     if (!this.el.hasClass('hideing')) {
2945                         return; // it's been shown again...
2946                     }
2947                     this.el.removeClass('show');
2948                     this.el.removeClass('hideing');
2949                 }).defer(150,this);
2950                 
2951             }else{
2952                  this.el.removeClass('show');
2953             }
2954             this.fireEvent('hide', this);
2955         }
2956     },
2957     isVisible : function()
2958     {
2959         
2960         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2961         
2962     },
2963
2964     addButton : function(str, cb)
2965     {
2966
2967
2968         var b = Roo.apply({}, { html : str } );
2969         b.xns = b.xns || Roo.bootstrap;
2970         b.xtype = b.xtype || 'Button';
2971         if (typeof(b.listeners) == 'undefined') {
2972             b.listeners = { click : cb.createDelegate(this)  };
2973         }
2974
2975         var btn = Roo.factory(b);
2976
2977         btn.render(this.el.select('.modal-footer div').first());
2978
2979         return btn;
2980
2981     },
2982
2983     setDefaultButton : function(btn)
2984     {
2985         //this.el.select('.modal-footer').()
2986     },
2987     diff : false,
2988
2989     resizeTo: function(w,h)
2990     {
2991         // skip.. ?? why??
2992
2993         this.dialogEl.setWidth(w);
2994         if (this.diff === false) {
2995             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2996         }
2997
2998         this.bodyEl.setHeight(h - this.diff);
2999
3000         this.fireEvent('resize', this);
3001
3002     },
3003     setContentSize  : function(w, h)
3004     {
3005
3006     },
3007     onButtonClick: function(btn,e)
3008     {
3009         //Roo.log([a,b,c]);
3010         this.fireEvent('btnclick', btn.name, e);
3011     },
3012      /**
3013      * Set the title of the Dialog
3014      * @param {String} str new Title
3015      */
3016     setTitle: function(str) {
3017         this.titleEl.dom.innerHTML = str;
3018     },
3019     /**
3020      * Set the body of the Dialog
3021      * @param {String} str new Title
3022      */
3023     setBody: function(str) {
3024         this.bodyEl.dom.innerHTML = str;
3025     },
3026     /**
3027      * Set the body of the Dialog using the template
3028      * @param {Obj} data - apply this data to the template and replace the body contents.
3029      */
3030     applyBody: function(obj)
3031     {
3032         if (!this.tmpl) {
3033             Roo.log("Error - using apply Body without a template");
3034             //code
3035         }
3036         this.tmpl.overwrite(this.bodyEl, obj);
3037     },
3038     
3039     getChildHeight : function(child_nodes)
3040     {
3041         if(
3042             !child_nodes ||
3043             child_nodes.length == 0
3044         ) {
3045             return;
3046         }
3047         
3048         var child_height = 0;
3049         
3050         for(var i = 0; i < child_nodes.length; i++) {
3051             
3052             /*
3053             * for modal with tabs...
3054             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3055                 
3056                 var layout_childs = child_nodes[i].childNodes;
3057                 
3058                 for(var j = 0; j < layout_childs.length; j++) {
3059                     
3060                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3061                         
3062                         var layout_body_childs = layout_childs[j].childNodes;
3063                         
3064                         for(var k = 0; k < layout_body_childs.length; k++) {
3065                             
3066                             if(layout_body_childs[k].classList.contains('navbar')) {
3067                                 child_height += layout_body_childs[k].offsetHeight;
3068                                 continue;
3069                             }
3070                             
3071                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3072                                 
3073                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3074                                 
3075                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3076                                     
3077                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3078                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3079                                         continue;
3080                                     }
3081                                     
3082                                 }
3083                                 
3084                             }
3085                             
3086                         }
3087                     }
3088                 }
3089                 continue;
3090             }
3091             */
3092             
3093             child_height += child_nodes[i].offsetHeight;
3094             // Roo.log(child_nodes[i].offsetHeight);
3095         }
3096         
3097         return child_height;
3098     }
3099
3100 });
3101
3102
3103 Roo.apply(Roo.bootstrap.Modal,  {
3104     /**
3105          * Button config that displays a single OK button
3106          * @type Object
3107          */
3108         OK :  [{
3109             name : 'ok',
3110             weight : 'primary',
3111             html : 'OK'
3112         }],
3113         /**
3114          * Button config that displays Yes and No buttons
3115          * @type Object
3116          */
3117         YESNO : [
3118             {
3119                 name  : 'no',
3120                 html : 'No'
3121             },
3122             {
3123                 name  :'yes',
3124                 weight : 'primary',
3125                 html : 'Yes'
3126             }
3127         ],
3128
3129         /**
3130          * Button config that displays OK and Cancel buttons
3131          * @type Object
3132          */
3133         OKCANCEL : [
3134             {
3135                name : 'cancel',
3136                 html : 'Cancel'
3137             },
3138             {
3139                 name : 'ok',
3140                 weight : 'primary',
3141                 html : 'OK'
3142             }
3143         ],
3144         /**
3145          * Button config that displays Yes, No and Cancel buttons
3146          * @type Object
3147          */
3148         YESNOCANCEL : [
3149             {
3150                 name : 'yes',
3151                 weight : 'primary',
3152                 html : 'Yes'
3153             },
3154             {
3155                 name : 'no',
3156                 html : 'No'
3157             },
3158             {
3159                 name : 'cancel',
3160                 html : 'Cancel'
3161             }
3162         ],
3163         
3164         zIndex : 10001
3165 });
3166 /*
3167  * - LGPL
3168  *
3169  * messagebox - can be used as a replace
3170  * 
3171  */
3172 /**
3173  * @class Roo.MessageBox
3174  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3175  * Example usage:
3176  *<pre><code>
3177 // Basic alert:
3178 Roo.Msg.alert('Status', 'Changes saved successfully.');
3179
3180 // Prompt for user data:
3181 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3182     if (btn == 'ok'){
3183         // process text value...
3184     }
3185 });
3186
3187 // Show a dialog using config options:
3188 Roo.Msg.show({
3189    title:'Save Changes?',
3190    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3191    buttons: Roo.Msg.YESNOCANCEL,
3192    fn: processResult,
3193    animEl: 'elId'
3194 });
3195 </code></pre>
3196  * @singleton
3197  */
3198 Roo.bootstrap.MessageBox = function(){
3199     var dlg, opt, mask, waitTimer;
3200     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3201     var buttons, activeTextEl, bwidth;
3202
3203     
3204     // private
3205     var handleButton = function(button){
3206         dlg.hide();
3207         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3208     };
3209
3210     // private
3211     var handleHide = function(){
3212         if(opt && opt.cls){
3213             dlg.el.removeClass(opt.cls);
3214         }
3215         //if(waitTimer){
3216         //    Roo.TaskMgr.stop(waitTimer);
3217         //    waitTimer = null;
3218         //}
3219     };
3220
3221     // private
3222     var updateButtons = function(b){
3223         var width = 0;
3224         if(!b){
3225             buttons["ok"].hide();
3226             buttons["cancel"].hide();
3227             buttons["yes"].hide();
3228             buttons["no"].hide();
3229             //dlg.footer.dom.style.display = 'none';
3230             return width;
3231         }
3232         dlg.footerEl.dom.style.display = '';
3233         for(var k in buttons){
3234             if(typeof buttons[k] != "function"){
3235                 if(b[k]){
3236                     buttons[k].show();
3237                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3238                     width += buttons[k].el.getWidth()+15;
3239                 }else{
3240                     buttons[k].hide();
3241                 }
3242             }
3243         }
3244         return width;
3245     };
3246
3247     // private
3248     var handleEsc = function(d, k, e){
3249         if(opt && opt.closable !== false){
3250             dlg.hide();
3251         }
3252         if(e){
3253             e.stopEvent();
3254         }
3255     };
3256
3257     return {
3258         /**
3259          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3260          * @return {Roo.BasicDialog} The BasicDialog element
3261          */
3262         getDialog : function(){
3263            if(!dlg){
3264                 dlg = new Roo.bootstrap.Modal( {
3265                     //draggable: true,
3266                     //resizable:false,
3267                     //constraintoviewport:false,
3268                     //fixedcenter:true,
3269                     //collapsible : false,
3270                     //shim:true,
3271                     //modal: true,
3272                 //    width: 'auto',
3273                   //  height:100,
3274                     //buttonAlign:"center",
3275                     closeClick : function(){
3276                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3277                             handleButton("no");
3278                         }else{
3279                             handleButton("cancel");
3280                         }
3281                     }
3282                 });
3283                 dlg.render();
3284                 dlg.on("hide", handleHide);
3285                 mask = dlg.mask;
3286                 //dlg.addKeyListener(27, handleEsc);
3287                 buttons = {};
3288                 this.buttons = buttons;
3289                 var bt = this.buttonText;
3290                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3291                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3292                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3293                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3294                 //Roo.log(buttons);
3295                 bodyEl = dlg.bodyEl.createChild({
3296
3297                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3298                         '<textarea class="roo-mb-textarea"></textarea>' +
3299                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3300                 });
3301                 msgEl = bodyEl.dom.firstChild;
3302                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3303                 textboxEl.enableDisplayMode();
3304                 textboxEl.addKeyListener([10,13], function(){
3305                     if(dlg.isVisible() && opt && opt.buttons){
3306                         if(opt.buttons.ok){
3307                             handleButton("ok");
3308                         }else if(opt.buttons.yes){
3309                             handleButton("yes");
3310                         }
3311                     }
3312                 });
3313                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3314                 textareaEl.enableDisplayMode();
3315                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3316                 progressEl.enableDisplayMode();
3317                 
3318                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3319                 var pf = progressEl.dom.firstChild;
3320                 if (pf) {
3321                     pp = Roo.get(pf.firstChild);
3322                     pp.setHeight(pf.offsetHeight);
3323                 }
3324                 
3325             }
3326             return dlg;
3327         },
3328
3329         /**
3330          * Updates the message box body text
3331          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3332          * the XHTML-compliant non-breaking space character '&amp;#160;')
3333          * @return {Roo.MessageBox} This message box
3334          */
3335         updateText : function(text)
3336         {
3337             if(!dlg.isVisible() && !opt.width){
3338                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3339                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3340             }
3341             msgEl.innerHTML = text || '&#160;';
3342       
3343             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3344             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3345             var w = Math.max(
3346                     Math.min(opt.width || cw , this.maxWidth), 
3347                     Math.max(opt.minWidth || this.minWidth, bwidth)
3348             );
3349             if(opt.prompt){
3350                 activeTextEl.setWidth(w);
3351             }
3352             if(dlg.isVisible()){
3353                 dlg.fixedcenter = false;
3354             }
3355             // to big, make it scroll. = But as usual stupid IE does not support
3356             // !important..
3357             
3358             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3359                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3360                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3361             } else {
3362                 bodyEl.dom.style.height = '';
3363                 bodyEl.dom.style.overflowY = '';
3364             }
3365             if (cw > w) {
3366                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3367             } else {
3368                 bodyEl.dom.style.overflowX = '';
3369             }
3370             
3371             dlg.setContentSize(w, bodyEl.getHeight());
3372             if(dlg.isVisible()){
3373                 dlg.fixedcenter = true;
3374             }
3375             return this;
3376         },
3377
3378         /**
3379          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3380          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3381          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3382          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3383          * @return {Roo.MessageBox} This message box
3384          */
3385         updateProgress : function(value, text){
3386             if(text){
3387                 this.updateText(text);
3388             }
3389             
3390             if (pp) { // weird bug on my firefox - for some reason this is not defined
3391                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3392                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3393             }
3394             return this;
3395         },        
3396
3397         /**
3398          * Returns true if the message box is currently displayed
3399          * @return {Boolean} True if the message box is visible, else false
3400          */
3401         isVisible : function(){
3402             return dlg && dlg.isVisible();  
3403         },
3404
3405         /**
3406          * Hides the message box if it is displayed
3407          */
3408         hide : function(){
3409             if(this.isVisible()){
3410                 dlg.hide();
3411             }  
3412         },
3413
3414         /**
3415          * Displays a new message box, or reinitializes an existing message box, based on the config options
3416          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3417          * The following config object properties are supported:
3418          * <pre>
3419 Property    Type             Description
3420 ----------  ---------------  ------------------------------------------------------------------------------------
3421 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3422                                    closes (defaults to undefined)
3423 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3424                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3425 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3426                                    progress and wait dialogs will ignore this property and always hide the
3427                                    close button as they can only be closed programmatically.
3428 cls               String           A custom CSS class to apply to the message box element
3429 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3430                                    displayed (defaults to 75)
3431 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3432                                    function will be btn (the name of the button that was clicked, if applicable,
3433                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3434                                    Progress and wait dialogs will ignore this option since they do not respond to
3435                                    user actions and can only be closed programmatically, so any required function
3436                                    should be called by the same code after it closes the dialog.
3437 icon              String           A CSS class that provides a background image to be used as an icon for
3438                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3439 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3440 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3441 modal             Boolean          False to allow user interaction with the page while the message box is
3442                                    displayed (defaults to true)
3443 msg               String           A string that will replace the existing message box body text (defaults
3444                                    to the XHTML-compliant non-breaking space character '&#160;')
3445 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3446 progress          Boolean          True to display a progress bar (defaults to false)
3447 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3448 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3449 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3450 title             String           The title text
3451 value             String           The string value to set into the active textbox element if displayed
3452 wait              Boolean          True to display a progress bar (defaults to false)
3453 width             Number           The width of the dialog in pixels
3454 </pre>
3455          *
3456          * Example usage:
3457          * <pre><code>
3458 Roo.Msg.show({
3459    title: 'Address',
3460    msg: 'Please enter your address:',
3461    width: 300,
3462    buttons: Roo.MessageBox.OKCANCEL,
3463    multiline: true,
3464    fn: saveAddress,
3465    animEl: 'addAddressBtn'
3466 });
3467 </code></pre>
3468          * @param {Object} config Configuration options
3469          * @return {Roo.MessageBox} This message box
3470          */
3471         show : function(options)
3472         {
3473             
3474             // this causes nightmares if you show one dialog after another
3475             // especially on callbacks..
3476              
3477             if(this.isVisible()){
3478                 
3479                 this.hide();
3480                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3481                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3482                 Roo.log("New Dialog Message:" +  options.msg )
3483                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3484                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3485                 
3486             }
3487             var d = this.getDialog();
3488             opt = options;
3489             d.setTitle(opt.title || "&#160;");
3490             d.closeEl.setDisplayed(opt.closable !== false);
3491             activeTextEl = textboxEl;
3492             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3493             if(opt.prompt){
3494                 if(opt.multiline){
3495                     textboxEl.hide();
3496                     textareaEl.show();
3497                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3498                         opt.multiline : this.defaultTextHeight);
3499                     activeTextEl = textareaEl;
3500                 }else{
3501                     textboxEl.show();
3502                     textareaEl.hide();
3503                 }
3504             }else{
3505                 textboxEl.hide();
3506                 textareaEl.hide();
3507             }
3508             progressEl.setDisplayed(opt.progress === true);
3509             this.updateProgress(0);
3510             activeTextEl.dom.value = opt.value || "";
3511             if(opt.prompt){
3512                 dlg.setDefaultButton(activeTextEl);
3513             }else{
3514                 var bs = opt.buttons;
3515                 var db = null;
3516                 if(bs && bs.ok){
3517                     db = buttons["ok"];
3518                 }else if(bs && bs.yes){
3519                     db = buttons["yes"];
3520                 }
3521                 dlg.setDefaultButton(db);
3522             }
3523             bwidth = updateButtons(opt.buttons);
3524             this.updateText(opt.msg);
3525             if(opt.cls){
3526                 d.el.addClass(opt.cls);
3527             }
3528             d.proxyDrag = opt.proxyDrag === true;
3529             d.modal = opt.modal !== false;
3530             d.mask = opt.modal !== false ? mask : false;
3531             if(!d.isVisible()){
3532                 // force it to the end of the z-index stack so it gets a cursor in FF
3533                 document.body.appendChild(dlg.el.dom);
3534                 d.animateTarget = null;
3535                 d.show(options.animEl);
3536             }
3537             return this;
3538         },
3539
3540         /**
3541          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3542          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3543          * and closing the message box when the process is complete.
3544          * @param {String} title The title bar text
3545          * @param {String} msg The message box body text
3546          * @return {Roo.MessageBox} This message box
3547          */
3548         progress : function(title, msg){
3549             this.show({
3550                 title : title,
3551                 msg : msg,
3552                 buttons: false,
3553                 progress:true,
3554                 closable:false,
3555                 minWidth: this.minProgressWidth,
3556                 modal : true
3557             });
3558             return this;
3559         },
3560
3561         /**
3562          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3563          * If a callback function is passed it will be called after the user clicks the button, and the
3564          * id of the button that was clicked will be passed as the only parameter to the callback
3565          * (could also be the top-right close button).
3566          * @param {String} title The title bar text
3567          * @param {String} msg The message box body text
3568          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3569          * @param {Object} scope (optional) The scope of the callback function
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         alert : function(title, msg, fn, scope)
3573         {
3574             this.show({
3575                 title : title,
3576                 msg : msg,
3577                 buttons: this.OK,
3578                 fn: fn,
3579                 closable : false,
3580                 scope : scope,
3581                 modal : true
3582             });
3583             return this;
3584         },
3585
3586         /**
3587          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3588          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3589          * You are responsible for closing the message box when the process is complete.
3590          * @param {String} msg The message box body text
3591          * @param {String} title (optional) The title bar text
3592          * @return {Roo.MessageBox} This message box
3593          */
3594         wait : function(msg, title){
3595             this.show({
3596                 title : title,
3597                 msg : msg,
3598                 buttons: false,
3599                 closable:false,
3600                 progress:true,
3601                 modal:true,
3602                 width:300,
3603                 wait:true
3604             });
3605             waitTimer = Roo.TaskMgr.start({
3606                 run: function(i){
3607                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3608                 },
3609                 interval: 1000
3610             });
3611             return this;
3612         },
3613
3614         /**
3615          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3616          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3617          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3618          * @param {String} title The title bar text
3619          * @param {String} msg The message box body text
3620          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3621          * @param {Object} scope (optional) The scope of the callback function
3622          * @return {Roo.MessageBox} This message box
3623          */
3624         confirm : function(title, msg, fn, scope){
3625             this.show({
3626                 title : title,
3627                 msg : msg,
3628                 buttons: this.YESNO,
3629                 fn: fn,
3630                 scope : scope,
3631                 modal : true
3632             });
3633             return this;
3634         },
3635
3636         /**
3637          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3638          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3639          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3640          * (could also be the top-right close button) and the text that was entered will be passed as the two
3641          * parameters to the callback.
3642          * @param {String} title The title bar text
3643          * @param {String} msg The message box body text
3644          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3645          * @param {Object} scope (optional) The scope of the callback function
3646          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3647          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3648          * @return {Roo.MessageBox} This message box
3649          */
3650         prompt : function(title, msg, fn, scope, multiline){
3651             this.show({
3652                 title : title,
3653                 msg : msg,
3654                 buttons: this.OKCANCEL,
3655                 fn: fn,
3656                 minWidth:250,
3657                 scope : scope,
3658                 prompt:true,
3659                 multiline: multiline,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Button config that displays a single OK button
3667          * @type Object
3668          */
3669         OK : {ok:true},
3670         /**
3671          * Button config that displays Yes and No buttons
3672          * @type Object
3673          */
3674         YESNO : {yes:true, no:true},
3675         /**
3676          * Button config that displays OK and Cancel buttons
3677          * @type Object
3678          */
3679         OKCANCEL : {ok:true, cancel:true},
3680         /**
3681          * Button config that displays Yes, No and Cancel buttons
3682          * @type Object
3683          */
3684         YESNOCANCEL : {yes:true, no:true, cancel:true},
3685
3686         /**
3687          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3688          * @type Number
3689          */
3690         defaultTextHeight : 75,
3691         /**
3692          * The maximum width in pixels of the message box (defaults to 600)
3693          * @type Number
3694          */
3695         maxWidth : 600,
3696         /**
3697          * The minimum width in pixels of the message box (defaults to 100)
3698          * @type Number
3699          */
3700         minWidth : 100,
3701         /**
3702          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3703          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3704          * @type Number
3705          */
3706         minProgressWidth : 250,
3707         /**
3708          * An object containing the default button text strings that can be overriden for localized language support.
3709          * Supported properties are: ok, cancel, yes and no.
3710          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3711          * @type Object
3712          */
3713         buttonText : {
3714             ok : "OK",
3715             cancel : "Cancel",
3716             yes : "Yes",
3717             no : "No"
3718         }
3719     };
3720 }();
3721
3722 /**
3723  * Shorthand for {@link Roo.MessageBox}
3724  */
3725 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3726 Roo.Msg = Roo.Msg || Roo.MessageBox;
3727 /*
3728  * - LGPL
3729  *
3730  * navbar
3731  * 
3732  */
3733
3734 /**
3735  * @class Roo.bootstrap.Navbar
3736  * @extends Roo.bootstrap.Component
3737  * Bootstrap Navbar class
3738
3739  * @constructor
3740  * Create a new Navbar
3741  * @param {Object} config The config object
3742  */
3743
3744
3745 Roo.bootstrap.Navbar = function(config){
3746     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3747     this.addEvents({
3748         // raw events
3749         /**
3750          * @event beforetoggle
3751          * Fire before toggle the menu
3752          * @param {Roo.EventObject} e
3753          */
3754         "beforetoggle" : true
3755     });
3756 };
3757
3758 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3759     
3760     
3761    
3762     // private
3763     navItems : false,
3764     loadMask : false,
3765     
3766     
3767     getAutoCreate : function(){
3768         
3769         
3770         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3771         
3772     },
3773     
3774     initEvents :function ()
3775     {
3776         //Roo.log(this.el.select('.navbar-toggle',true));
3777         this.el.select('.navbar-toggle',true).on('click', function() {
3778             if(this.fireEvent('beforetoggle', this) !== false){
3779                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3780             }
3781             
3782         }, this);
3783         
3784         var mark = {
3785             tag: "div",
3786             cls:"x-dlg-mask"
3787         };
3788         
3789         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3790         
3791         var size = this.el.getSize();
3792         this.maskEl.setSize(size.width, size.height);
3793         this.maskEl.enableDisplayMode("block");
3794         this.maskEl.hide();
3795         
3796         if(this.loadMask){
3797             this.maskEl.show();
3798         }
3799     },
3800     
3801     
3802     getChildContainer : function()
3803     {
3804         if (this.el.select('.collapse').getCount()) {
3805             return this.el.select('.collapse',true).first();
3806         }
3807         
3808         return this.el;
3809     },
3810     
3811     mask : function()
3812     {
3813         this.maskEl.show();
3814     },
3815     
3816     unmask : function()
3817     {
3818         this.maskEl.hide();
3819     } 
3820     
3821     
3822     
3823     
3824 });
3825
3826
3827
3828  
3829
3830  /*
3831  * - LGPL
3832  *
3833  * navbar
3834  * 
3835  */
3836
3837 /**
3838  * @class Roo.bootstrap.NavSimplebar
3839  * @extends Roo.bootstrap.Navbar
3840  * Bootstrap Sidebar class
3841  *
3842  * @cfg {Boolean} inverse is inverted color
3843  * 
3844  * @cfg {String} type (nav | pills | tabs)
3845  * @cfg {Boolean} arrangement stacked | justified
3846  * @cfg {String} align (left | right) alignment
3847  * 
3848  * @cfg {Boolean} main (true|false) main nav bar? default false
3849  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3850  * 
3851  * @cfg {String} tag (header|footer|nav|div) default is nav 
3852
3853  * 
3854  * 
3855  * 
3856  * @constructor
3857  * Create a new Sidebar
3858  * @param {Object} config The config object
3859  */
3860
3861
3862 Roo.bootstrap.NavSimplebar = function(config){
3863     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3864 };
3865
3866 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3867     
3868     inverse: false,
3869     
3870     type: false,
3871     arrangement: '',
3872     align : false,
3873     
3874     
3875     
3876     main : false,
3877     
3878     
3879     tag : false,
3880     
3881     
3882     getAutoCreate : function(){
3883         
3884         
3885         var cfg = {
3886             tag : this.tag || 'div',
3887             cls : 'navbar'
3888         };
3889           
3890         
3891         cfg.cn = [
3892             {
3893                 cls: 'nav',
3894                 tag : 'ul'
3895             }
3896         ];
3897         
3898          
3899         this.type = this.type || 'nav';
3900         if (['tabs','pills'].indexOf(this.type)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.type
3902         
3903         
3904         } else {
3905             if (this.type!=='nav') {
3906                 Roo.log('nav type must be nav/tabs/pills')
3907             }
3908             cfg.cn[0].cls += ' navbar-nav'
3909         }
3910         
3911         
3912         
3913         
3914         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3915             cfg.cn[0].cls += ' nav-' + this.arrangement;
3916         }
3917         
3918         
3919         if (this.align === 'right') {
3920             cfg.cn[0].cls += ' navbar-right';
3921         }
3922         
3923         if (this.inverse) {
3924             cfg.cls += ' navbar-inverse';
3925             
3926         }
3927         
3928         
3929         return cfg;
3930     
3931         
3932     }
3933     
3934     
3935     
3936 });
3937
3938
3939
3940  
3941
3942  
3943        /*
3944  * - LGPL
3945  *
3946  * navbar
3947  * navbar-fixed-top
3948  * navbar-expand-md  fixed-top 
3949  */
3950
3951 /**
3952  * @class Roo.bootstrap.NavHeaderbar
3953  * @extends Roo.bootstrap.NavSimplebar
3954  * Bootstrap Sidebar class
3955  *
3956  * @cfg {String} brand what is brand
3957  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3958  * @cfg {String} brand_href href of the brand
3959  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3960  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3961  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3962  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3963  * 
3964  * @constructor
3965  * Create a new Sidebar
3966  * @param {Object} config The config object
3967  */
3968
3969
3970 Roo.bootstrap.NavHeaderbar = function(config){
3971     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3972       
3973 };
3974
3975 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3976     
3977     position: '',
3978     brand: '',
3979     brand_href: false,
3980     srButton : true,
3981     autohide : false,
3982     desktopCenter : false,
3983    
3984     
3985     getAutoCreate : function(){
3986         
3987         var   cfg = {
3988             tag: this.nav || 'nav',
3989             cls: 'navbar navbar-expand-md',
3990             role: 'navigation',
3991             cn: []
3992         };
3993         
3994         var cn = cfg.cn;
3995         if (this.desktopCenter) {
3996             cn.push({cls : 'container', cn : []});
3997             cn = cn[0].cn;
3998         }
3999         
4000         if(this.srButton){
4001             cn.push({
4002                 tag: 'div',
4003                 cls: 'navbar-header',
4004                 cn: [
4005                     {
4006                         tag: 'button',
4007                         type: 'button',
4008                         cls: 'navbar-toggle navbar-toggler',
4009                         'data-toggle': 'collapse',
4010                         cn: [
4011                             {
4012                                 tag: 'span',
4013                                 cls: 'sr-only',
4014                                 html: 'Toggle navigation'
4015                             },
4016                             {
4017                                 tag: 'span',
4018                                 cls: 'icon-bar navbar-toggler-icon'
4019                             },
4020                             {
4021                                 tag: 'span',
4022                                 cls: 'icon-bar'
4023                             },
4024                             {
4025                                 tag: 'span',
4026                                 cls: 'icon-bar'
4027                             }
4028                         ]
4029                     }
4030                 ]
4031             });
4032         }
4033         
4034         cn.push({
4035             tag: 'div',
4036             cls: 'collapse navbar-collapse',
4037             cn : []
4038         });
4039         
4040         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4041         
4042         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4043             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4044             
4045             // tag can override this..
4046             
4047             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4048         }
4049         
4050         if (this.brand !== '') {
4051             cn[0].cn.push({
4052                 tag: 'a',
4053                 href: this.brand_href ? this.brand_href : '#',
4054                 cls: 'navbar-brand',
4055                 cn: [
4056                 this.brand
4057                 ]
4058             });
4059         }
4060         
4061         if(this.main){
4062             cfg.cls += ' main-nav';
4063         }
4064         
4065         
4066         return cfg;
4067
4068         
4069     },
4070     getHeaderChildContainer : function()
4071     {
4072         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4073             return this.el.select('.navbar-header',true).first();
4074         }
4075         
4076         return this.getChildContainer();
4077     },
4078     
4079     
4080     initEvents : function()
4081     {
4082         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4083         
4084         if (this.autohide) {
4085             
4086             var prevScroll = 0;
4087             var ft = this.el;
4088             
4089             Roo.get(document).on('scroll',function(e) {
4090                 var ns = Roo.get(document).getScroll().top;
4091                 var os = prevScroll;
4092                 prevScroll = ns;
4093                 
4094                 if(ns > os){
4095                     ft.removeClass('slideDown');
4096                     ft.addClass('slideUp');
4097                     return;
4098                 }
4099                 ft.removeClass('slideUp');
4100                 ft.addClass('slideDown');
4101                  
4102               
4103           },this);
4104         }
4105     }    
4106     
4107 });
4108
4109
4110
4111  
4112
4113  /*
4114  * - LGPL
4115  *
4116  * navbar
4117  * 
4118  */
4119
4120 /**
4121  * @class Roo.bootstrap.NavSidebar
4122  * @extends Roo.bootstrap.Navbar
4123  * Bootstrap Sidebar class
4124  * 
4125  * @constructor
4126  * Create a new Sidebar
4127  * @param {Object} config The config object
4128  */
4129
4130
4131 Roo.bootstrap.NavSidebar = function(config){
4132     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4133 };
4134
4135 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4136     
4137     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4138     
4139     getAutoCreate : function(){
4140         
4141         
4142         return  {
4143             tag: 'div',
4144             cls: 'sidebar sidebar-nav'
4145         };
4146     
4147         
4148     }
4149     
4150     
4151     
4152 });
4153
4154
4155
4156  
4157
4158  /*
4159  * - LGPL
4160  *
4161  * nav group
4162  * 
4163  */
4164
4165 /**
4166  * @class Roo.bootstrap.NavGroup
4167  * @extends Roo.bootstrap.Component
4168  * Bootstrap NavGroup class
4169  * @cfg {String} align (left|right)
4170  * @cfg {Boolean} inverse
4171  * @cfg {String} type (nav|pills|tab) default nav
4172  * @cfg {String} navId - reference Id for navbar.
4173
4174  * 
4175  * @constructor
4176  * Create a new nav group
4177  * @param {Object} config The config object
4178  */
4179
4180 Roo.bootstrap.NavGroup = function(config){
4181     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4182     this.navItems = [];
4183    
4184     Roo.bootstrap.NavGroup.register(this);
4185      this.addEvents({
4186         /**
4187              * @event changed
4188              * Fires when the active item changes
4189              * @param {Roo.bootstrap.NavGroup} this
4190              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4191              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4192          */
4193         'changed': true
4194      });
4195     
4196 };
4197
4198 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4199     
4200     align: '',
4201     inverse: false,
4202     form: false,
4203     type: 'nav',
4204     navId : '',
4205     // private
4206     
4207     navItems : false, 
4208     
4209     getAutoCreate : function()
4210     {
4211         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4212         
4213         cfg = {
4214             tag : 'ul',
4215             cls: 'nav' 
4216         };
4217         
4218         if (['tabs','pills'].indexOf(this.type)!==-1) {
4219             cfg.cls += ' nav-' + this.type
4220         } else {
4221             if (this.type!=='nav') {
4222                 Roo.log('nav type must be nav/tabs/pills')
4223             }
4224             cfg.cls += ' navbar-nav mr-auto'
4225         }
4226         
4227         if (this.parent() && this.parent().sidebar) {
4228             cfg = {
4229                 tag: 'ul',
4230                 cls: 'dashboard-menu sidebar-menu'
4231             };
4232             
4233             return cfg;
4234         }
4235         
4236         if (this.form === true) {
4237             cfg = {
4238                 tag: 'form',
4239                 cls: 'navbar-form'
4240             };
4241             
4242             if (this.align === 'right') {
4243                 cfg.cls += ' navbar-right';
4244             } else {
4245                 cfg.cls += ' navbar-left';
4246             }
4247         }
4248         
4249         if (this.align === 'right') {
4250             cfg.cls += ' navbar-right';
4251         }
4252         
4253         if (this.inverse) {
4254             cfg.cls += ' navbar-inverse';
4255             
4256         }
4257         
4258         
4259         return cfg;
4260     },
4261     /**
4262     * sets the active Navigation item
4263     * @param {Roo.bootstrap.NavItem} the new current navitem
4264     */
4265     setActiveItem : function(item)
4266     {
4267         var prev = false;
4268         Roo.each(this.navItems, function(v){
4269             if (v == item) {
4270                 return ;
4271             }
4272             if (v.isActive()) {
4273                 v.setActive(false, true);
4274                 prev = v;
4275                 
4276             }
4277             
4278         });
4279
4280         item.setActive(true, true);
4281         this.fireEvent('changed', this, item, prev);
4282         
4283         
4284     },
4285     /**
4286     * gets the active Navigation item
4287     * @return {Roo.bootstrap.NavItem} the current navitem
4288     */
4289     getActive : function()
4290     {
4291         
4292         var prev = false;
4293         Roo.each(this.navItems, function(v){
4294             
4295             if (v.isActive()) {
4296                 prev = v;
4297                 
4298             }
4299             
4300         });
4301         return prev;
4302     },
4303     
4304     indexOfNav : function()
4305     {
4306         
4307         var prev = false;
4308         Roo.each(this.navItems, function(v,i){
4309             
4310             if (v.isActive()) {
4311                 prev = i;
4312                 
4313             }
4314             
4315         });
4316         return prev;
4317     },
4318     /**
4319     * adds a Navigation item
4320     * @param {Roo.bootstrap.NavItem} the navitem to add
4321     */
4322     addItem : function(cfg)
4323     {
4324         var cn = new Roo.bootstrap.NavItem(cfg);
4325         this.register(cn);
4326         cn.parentId = this.id;
4327         cn.onRender(this.el, null);
4328         return cn;
4329     },
4330     /**
4331     * register a Navigation item
4332     * @param {Roo.bootstrap.NavItem} the navitem to add
4333     */
4334     register : function(item)
4335     {
4336         this.navItems.push( item);
4337         item.navId = this.navId;
4338     
4339     },
4340     
4341     /**
4342     * clear all the Navigation item
4343     */
4344    
4345     clearAll : function()
4346     {
4347         this.navItems = [];
4348         this.el.dom.innerHTML = '';
4349     },
4350     
4351     getNavItem: function(tabId)
4352     {
4353         var ret = false;
4354         Roo.each(this.navItems, function(e) {
4355             if (e.tabId == tabId) {
4356                ret =  e;
4357                return false;
4358             }
4359             return true;
4360             
4361         });
4362         return ret;
4363     },
4364     
4365     setActiveNext : function()
4366     {
4367         var i = this.indexOfNav(this.getActive());
4368         if (i > this.navItems.length) {
4369             return;
4370         }
4371         this.setActiveItem(this.navItems[i+1]);
4372     },
4373     setActivePrev : function()
4374     {
4375         var i = this.indexOfNav(this.getActive());
4376         if (i  < 1) {
4377             return;
4378         }
4379         this.setActiveItem(this.navItems[i-1]);
4380     },
4381     clearWasActive : function(except) {
4382         Roo.each(this.navItems, function(e) {
4383             if (e.tabId != except.tabId && e.was_active) {
4384                e.was_active = false;
4385                return false;
4386             }
4387             return true;
4388             
4389         });
4390     },
4391     getWasActive : function ()
4392     {
4393         var r = false;
4394         Roo.each(this.navItems, function(e) {
4395             if (e.was_active) {
4396                r = e;
4397                return false;
4398             }
4399             return true;
4400             
4401         });
4402         return r;
4403     }
4404     
4405     
4406 });
4407
4408  
4409 Roo.apply(Roo.bootstrap.NavGroup, {
4410     
4411     groups: {},
4412      /**
4413     * register a Navigation Group
4414     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4415     */
4416     register : function(navgrp)
4417     {
4418         this.groups[navgrp.navId] = navgrp;
4419         
4420     },
4421     /**
4422     * fetch a Navigation Group based on the navigation ID
4423     * @param {string} the navgroup to add
4424     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4425     */
4426     get: function(navId) {
4427         if (typeof(this.groups[navId]) == 'undefined') {
4428             return false;
4429             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4430         }
4431         return this.groups[navId] ;
4432     }
4433     
4434     
4435     
4436 });
4437
4438  /*
4439  * - LGPL
4440  *
4441  * row
4442  * 
4443  */
4444
4445 /**
4446  * @class Roo.bootstrap.NavItem
4447  * @extends Roo.bootstrap.Component
4448  * Bootstrap Navbar.NavItem class
4449  * @cfg {String} href  link to
4450  * @cfg {String} html content of button
4451  * @cfg {String} badge text inside badge
4452  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4453  * @cfg {String} glyphicon name of glyphicon
4454  * @cfg {String} icon name of font awesome icon
4455  * @cfg {Boolean} active Is item active
4456  * @cfg {Boolean} disabled Is item disabled
4457  
4458  * @cfg {Boolean} preventDefault (true | false) default false
4459  * @cfg {String} tabId the tab that this item activates.
4460  * @cfg {String} tagtype (a|span) render as a href or span?
4461  * @cfg {Boolean} animateRef (true|false) link to element default false  
4462   
4463  * @constructor
4464  * Create a new Navbar Item
4465  * @param {Object} config The config object
4466  */
4467 Roo.bootstrap.NavItem = function(config){
4468     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4469     this.addEvents({
4470         // raw events
4471         /**
4472          * @event click
4473          * The raw click event for the entire grid.
4474          * @param {Roo.EventObject} e
4475          */
4476         "click" : true,
4477          /**
4478             * @event changed
4479             * Fires when the active item active state changes
4480             * @param {Roo.bootstrap.NavItem} this
4481             * @param {boolean} state the new state
4482              
4483          */
4484         'changed': true,
4485         /**
4486             * @event scrollto
4487             * Fires when scroll to element
4488             * @param {Roo.bootstrap.NavItem} this
4489             * @param {Object} options
4490             * @param {Roo.EventObject} e
4491              
4492          */
4493         'scrollto': true
4494     });
4495    
4496 };
4497
4498 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4499     
4500     href: false,
4501     html: '',
4502     badge: '',
4503     icon: false,
4504     glyphicon: false,
4505     active: false,
4506     preventDefault : false,
4507     tabId : false,
4508     tagtype : 'a',
4509     disabled : false,
4510     animateRef : false,
4511     was_active : false,
4512     
4513     getAutoCreate : function(){
4514          
4515         var cfg = {
4516             tag: 'li',
4517             cls: 'nav-item'
4518             
4519         };
4520         
4521         if (this.active) {
4522             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4523         }
4524         if (this.disabled) {
4525             cfg.cls += ' disabled';
4526         }
4527         
4528         if (this.href || this.html || this.glyphicon || this.icon) {
4529             cfg.cn = [
4530                 {
4531                     tag: this.tagtype,
4532                     href : this.href || "#",
4533                     html: this.html || ''
4534                 }
4535             ];
4536             if (this.tagtype == 'a') {
4537                 cfg.cn[0].cls = 'nav-link';
4538             }
4539             if (this.icon) {
4540                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4541             }
4542
4543             if(this.glyphicon) {
4544                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4545             }
4546             
4547             if (this.menu) {
4548                 
4549                 cfg.cn[0].html += " <span class='caret'></span>";
4550              
4551             }
4552             
4553             if (this.badge !== '') {
4554                  
4555                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4556             }
4557         }
4558         
4559         
4560         
4561         return cfg;
4562     },
4563     initEvents: function() 
4564     {
4565         if (typeof (this.menu) != 'undefined') {
4566             this.menu.parentType = this.xtype;
4567             this.menu.triggerEl = this.el;
4568             this.menu = this.addxtype(Roo.apply({}, this.menu));
4569         }
4570         
4571         this.el.select('a',true).on('click', this.onClick, this);
4572         
4573         if(this.tagtype == 'span'){
4574             this.el.select('span',true).on('click', this.onClick, this);
4575         }
4576        
4577         // at this point parent should be available..
4578         this.parent().register(this);
4579     },
4580     
4581     onClick : function(e)
4582     {
4583         if (e.getTarget('.dropdown-menu-item')) {
4584             // did you click on a menu itemm.... - then don't trigger onclick..
4585             return;
4586         }
4587         
4588         if(
4589                 this.preventDefault || 
4590                 this.href == '#' 
4591         ){
4592             Roo.log("NavItem - prevent Default?");
4593             e.preventDefault();
4594         }
4595         
4596         if (this.disabled) {
4597             return;
4598         }
4599         
4600         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4601         if (tg && tg.transition) {
4602             Roo.log("waiting for the transitionend");
4603             return;
4604         }
4605         
4606         
4607         
4608         //Roo.log("fire event clicked");
4609         if(this.fireEvent('click', this, e) === false){
4610             return;
4611         };
4612         
4613         if(this.tagtype == 'span'){
4614             return;
4615         }
4616         
4617         //Roo.log(this.href);
4618         var ael = this.el.select('a',true).first();
4619         //Roo.log(ael);
4620         
4621         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4622             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4623             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4624                 return; // ignore... - it's a 'hash' to another page.
4625             }
4626             Roo.log("NavItem - prevent Default?");
4627             e.preventDefault();
4628             this.scrollToElement(e);
4629         }
4630         
4631         
4632         var p =  this.parent();
4633    
4634         if (['tabs','pills'].indexOf(p.type)!==-1) {
4635             if (typeof(p.setActiveItem) !== 'undefined') {
4636                 p.setActiveItem(this);
4637             }
4638         }
4639         
4640         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4641         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4642             // remove the collapsed menu expand...
4643             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4644         }
4645     },
4646     
4647     isActive: function () {
4648         return this.active
4649     },
4650     setActive : function(state, fire, is_was_active)
4651     {
4652         if (this.active && !state && this.navId) {
4653             this.was_active = true;
4654             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4655             if (nv) {
4656                 nv.clearWasActive(this);
4657             }
4658             
4659         }
4660         this.active = state;
4661         
4662         if (!state ) {
4663             this.el.removeClass('active');
4664         } else if (!this.el.hasClass('active')) {
4665             this.el.addClass('active');
4666         }
4667         if (fire) {
4668             this.fireEvent('changed', this, state);
4669         }
4670         
4671         // show a panel if it's registered and related..
4672         
4673         if (!this.navId || !this.tabId || !state || is_was_active) {
4674             return;
4675         }
4676         
4677         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4678         if (!tg) {
4679             return;
4680         }
4681         var pan = tg.getPanelByName(this.tabId);
4682         if (!pan) {
4683             return;
4684         }
4685         // if we can not flip to new panel - go back to old nav highlight..
4686         if (false == tg.showPanel(pan)) {
4687             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4688             if (nv) {
4689                 var onav = nv.getWasActive();
4690                 if (onav) {
4691                     onav.setActive(true, false, true);
4692                 }
4693             }
4694             
4695         }
4696         
4697         
4698         
4699     },
4700      // this should not be here...
4701     setDisabled : function(state)
4702     {
4703         this.disabled = state;
4704         if (!state ) {
4705             this.el.removeClass('disabled');
4706         } else if (!this.el.hasClass('disabled')) {
4707             this.el.addClass('disabled');
4708         }
4709         
4710     },
4711     
4712     /**
4713      * Fetch the element to display the tooltip on.
4714      * @return {Roo.Element} defaults to this.el
4715      */
4716     tooltipEl : function()
4717     {
4718         return this.el.select('' + this.tagtype + '', true).first();
4719     },
4720     
4721     scrollToElement : function(e)
4722     {
4723         var c = document.body;
4724         
4725         /*
4726          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4727          */
4728         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4729             c = document.documentElement;
4730         }
4731         
4732         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4733         
4734         if(!target){
4735             return;
4736         }
4737
4738         var o = target.calcOffsetsTo(c);
4739         
4740         var options = {
4741             target : target,
4742             value : o[1]
4743         };
4744         
4745         this.fireEvent('scrollto', this, options, e);
4746         
4747         Roo.get(c).scrollTo('top', options.value, true);
4748         
4749         return;
4750     }
4751 });
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * sidebar item
4758  *
4759  *  li
4760  *    <span> icon </span>
4761  *    <span> text </span>
4762  *    <span>badge </span>
4763  */
4764
4765 /**
4766  * @class Roo.bootstrap.NavSidebarItem
4767  * @extends Roo.bootstrap.NavItem
4768  * Bootstrap Navbar.NavSidebarItem class
4769  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4770  * {Boolean} open is the menu open
4771  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4772  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4773  * {String} buttonSize (sm|md|lg)the extra classes for the button
4774  * {Boolean} showArrow show arrow next to the text (default true)
4775  * @constructor
4776  * Create a new Navbar Button
4777  * @param {Object} config The config object
4778  */
4779 Roo.bootstrap.NavSidebarItem = function(config){
4780     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4781     this.addEvents({
4782         // raw events
4783         /**
4784          * @event click
4785          * The raw click event for the entire grid.
4786          * @param {Roo.EventObject} e
4787          */
4788         "click" : true,
4789          /**
4790             * @event changed
4791             * Fires when the active item active state changes
4792             * @param {Roo.bootstrap.NavSidebarItem} this
4793             * @param {boolean} state the new state
4794              
4795          */
4796         'changed': true
4797     });
4798    
4799 };
4800
4801 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4802     
4803     badgeWeight : 'default',
4804     
4805     open: false,
4806     
4807     buttonView : false,
4808     
4809     buttonWeight : 'default',
4810     
4811     buttonSize : 'md',
4812     
4813     showArrow : true,
4814     
4815     getAutoCreate : function(){
4816         
4817         
4818         var a = {
4819                 tag: 'a',
4820                 href : this.href || '#',
4821                 cls: '',
4822                 html : '',
4823                 cn : []
4824         };
4825         
4826         if(this.buttonView){
4827             a = {
4828                 tag: 'button',
4829                 href : this.href || '#',
4830                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4831                 html : this.html,
4832                 cn : []
4833             };
4834         }
4835         
4836         var cfg = {
4837             tag: 'li',
4838             cls: '',
4839             cn: [ a ]
4840         };
4841         
4842         if (this.active) {
4843             cfg.cls += ' active';
4844         }
4845         
4846         if (this.disabled) {
4847             cfg.cls += ' disabled';
4848         }
4849         if (this.open) {
4850             cfg.cls += ' open x-open';
4851         }
4852         // left icon..
4853         if (this.glyphicon || this.icon) {
4854             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4855             a.cn.push({ tag : 'i', cls : c }) ;
4856         }
4857         
4858         if(!this.buttonView){
4859             var span = {
4860                 tag: 'span',
4861                 html : this.html || ''
4862             };
4863
4864             a.cn.push(span);
4865             
4866         }
4867         
4868         if (this.badge !== '') {
4869             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4870         }
4871         
4872         if (this.menu) {
4873             
4874             if(this.showArrow){
4875                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4876             }
4877             
4878             a.cls += ' dropdown-toggle treeview' ;
4879         }
4880         
4881         return cfg;
4882     },
4883     
4884     initEvents : function()
4885     { 
4886         if (typeof (this.menu) != 'undefined') {
4887             this.menu.parentType = this.xtype;
4888             this.menu.triggerEl = this.el;
4889             this.menu = this.addxtype(Roo.apply({}, this.menu));
4890         }
4891         
4892         this.el.on('click', this.onClick, this);
4893         
4894         if(this.badge !== ''){
4895             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4896         }
4897         
4898     },
4899     
4900     onClick : function(e)
4901     {
4902         if(this.disabled){
4903             e.preventDefault();
4904             return;
4905         }
4906         
4907         if(this.preventDefault){
4908             e.preventDefault();
4909         }
4910         
4911         this.fireEvent('click', this);
4912     },
4913     
4914     disable : function()
4915     {
4916         this.setDisabled(true);
4917     },
4918     
4919     enable : function()
4920     {
4921         this.setDisabled(false);
4922     },
4923     
4924     setDisabled : function(state)
4925     {
4926         if(this.disabled == state){
4927             return;
4928         }
4929         
4930         this.disabled = state;
4931         
4932         if (state) {
4933             this.el.addClass('disabled');
4934             return;
4935         }
4936         
4937         this.el.removeClass('disabled');
4938         
4939         return;
4940     },
4941     
4942     setActive : function(state)
4943     {
4944         if(this.active == state){
4945             return;
4946         }
4947         
4948         this.active = state;
4949         
4950         if (state) {
4951             this.el.addClass('active');
4952             return;
4953         }
4954         
4955         this.el.removeClass('active');
4956         
4957         return;
4958     },
4959     
4960     isActive: function () 
4961     {
4962         return this.active;
4963     },
4964     
4965     setBadge : function(str)
4966     {
4967         if(!this.badgeEl){
4968             return;
4969         }
4970         
4971         this.badgeEl.dom.innerHTML = str;
4972     }
4973     
4974    
4975      
4976  
4977 });
4978  
4979
4980  /*
4981  * - LGPL
4982  *
4983  * row
4984  * 
4985  */
4986
4987 /**
4988  * @class Roo.bootstrap.Row
4989  * @extends Roo.bootstrap.Component
4990  * Bootstrap Row class (contains columns...)
4991  * 
4992  * @constructor
4993  * Create a new Row
4994  * @param {Object} config The config object
4995  */
4996
4997 Roo.bootstrap.Row = function(config){
4998     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4999 };
5000
5001 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5002     
5003     getAutoCreate : function(){
5004        return {
5005             cls: 'row clearfix'
5006        };
5007     }
5008     
5009     
5010 });
5011
5012  
5013
5014  /*
5015  * - LGPL
5016  *
5017  * element
5018  * 
5019  */
5020
5021 /**
5022  * @class Roo.bootstrap.Element
5023  * @extends Roo.bootstrap.Component
5024  * Bootstrap Element class
5025  * @cfg {String} html contents of the element
5026  * @cfg {String} tag tag of the element
5027  * @cfg {String} cls class of the element
5028  * @cfg {Boolean} preventDefault (true|false) default false
5029  * @cfg {Boolean} clickable (true|false) default false
5030  * 
5031  * @constructor
5032  * Create a new Element
5033  * @param {Object} config The config object
5034  */
5035
5036 Roo.bootstrap.Element = function(config){
5037     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5038     
5039     this.addEvents({
5040         // raw events
5041         /**
5042          * @event click
5043          * When a element is chick
5044          * @param {Roo.bootstrap.Element} this
5045          * @param {Roo.EventObject} e
5046          */
5047         "click" : true
5048     });
5049 };
5050
5051 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5052     
5053     tag: 'div',
5054     cls: '',
5055     html: '',
5056     preventDefault: false, 
5057     clickable: false,
5058     
5059     getAutoCreate : function(){
5060         
5061         var cfg = {
5062             tag: this.tag,
5063             // cls: this.cls, double assign in parent class Component.js :: onRender
5064             html: this.html
5065         };
5066         
5067         return cfg;
5068     },
5069     
5070     initEvents: function() 
5071     {
5072         Roo.bootstrap.Element.superclass.initEvents.call(this);
5073         
5074         if(this.clickable){
5075             this.el.on('click', this.onClick, this);
5076         }
5077         
5078     },
5079     
5080     onClick : function(e)
5081     {
5082         if(this.preventDefault){
5083             e.preventDefault();
5084         }
5085         
5086         this.fireEvent('click', this, e);
5087     },
5088     
5089     getValue : function()
5090     {
5091         return this.el.dom.innerHTML;
5092     },
5093     
5094     setValue : function(value)
5095     {
5096         this.el.dom.innerHTML = value;
5097     }
5098    
5099 });
5100
5101  
5102
5103  /*
5104  * - LGPL
5105  *
5106  * pagination
5107  * 
5108  */
5109
5110 /**
5111  * @class Roo.bootstrap.Pagination
5112  * @extends Roo.bootstrap.Component
5113  * Bootstrap Pagination class
5114  * @cfg {String} size xs | sm | md | lg
5115  * @cfg {Boolean} inverse false | true
5116  * 
5117  * @constructor
5118  * Create a new Pagination
5119  * @param {Object} config The config object
5120  */
5121
5122 Roo.bootstrap.Pagination = function(config){
5123     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5124 };
5125
5126 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5127     
5128     cls: false,
5129     size: false,
5130     inverse: false,
5131     
5132     getAutoCreate : function(){
5133         var cfg = {
5134             tag: 'ul',
5135                 cls: 'pagination'
5136         };
5137         if (this.inverse) {
5138             cfg.cls += ' inverse';
5139         }
5140         if (this.html) {
5141             cfg.html=this.html;
5142         }
5143         if (this.cls) {
5144             cfg.cls += " " + this.cls;
5145         }
5146         return cfg;
5147     }
5148    
5149 });
5150
5151  
5152
5153  /*
5154  * - LGPL
5155  *
5156  * Pagination item
5157  * 
5158  */
5159
5160
5161 /**
5162  * @class Roo.bootstrap.PaginationItem
5163  * @extends Roo.bootstrap.Component
5164  * Bootstrap PaginationItem class
5165  * @cfg {String} html text
5166  * @cfg {String} href the link
5167  * @cfg {Boolean} preventDefault (true | false) default true
5168  * @cfg {Boolean} active (true | false) default false
5169  * @cfg {Boolean} disabled default false
5170  * 
5171  * 
5172  * @constructor
5173  * Create a new PaginationItem
5174  * @param {Object} config The config object
5175  */
5176
5177
5178 Roo.bootstrap.PaginationItem = function(config){
5179     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5180     this.addEvents({
5181         // raw events
5182         /**
5183          * @event click
5184          * The raw click event for the entire grid.
5185          * @param {Roo.EventObject} e
5186          */
5187         "click" : true
5188     });
5189 };
5190
5191 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5192     
5193     href : false,
5194     html : false,
5195     preventDefault: true,
5196     active : false,
5197     cls : false,
5198     disabled: false,
5199     
5200     getAutoCreate : function(){
5201         var cfg= {
5202             tag: 'li',
5203             cn: [
5204                 {
5205                     tag : 'a',
5206                     href : this.href ? this.href : '#',
5207                     html : this.html ? this.html : ''
5208                 }
5209             ]
5210         };
5211         
5212         if(this.cls){
5213             cfg.cls = this.cls;
5214         }
5215         
5216         if(this.disabled){
5217             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5218         }
5219         
5220         if(this.active){
5221             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5222         }
5223         
5224         return cfg;
5225     },
5226     
5227     initEvents: function() {
5228         
5229         this.el.on('click', this.onClick, this);
5230         
5231     },
5232     onClick : function(e)
5233     {
5234         Roo.log('PaginationItem on click ');
5235         if(this.preventDefault){
5236             e.preventDefault();
5237         }
5238         
5239         if(this.disabled){
5240             return;
5241         }
5242         
5243         this.fireEvent('click', this, e);
5244     }
5245    
5246 });
5247
5248  
5249
5250  /*
5251  * - LGPL
5252  *
5253  * slider
5254  * 
5255  */
5256
5257
5258 /**
5259  * @class Roo.bootstrap.Slider
5260  * @extends Roo.bootstrap.Component
5261  * Bootstrap Slider class
5262  *    
5263  * @constructor
5264  * Create a new Slider
5265  * @param {Object} config The config object
5266  */
5267
5268 Roo.bootstrap.Slider = function(config){
5269     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5270 };
5271
5272 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5273     
5274     getAutoCreate : function(){
5275         
5276         var cfg = {
5277             tag: 'div',
5278             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5279             cn: [
5280                 {
5281                     tag: 'a',
5282                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5283                 }
5284             ]
5285         };
5286         
5287         return cfg;
5288     }
5289    
5290 });
5291
5292  /*
5293  * Based on:
5294  * Ext JS Library 1.1.1
5295  * Copyright(c) 2006-2007, Ext JS, LLC.
5296  *
5297  * Originally Released Under LGPL - original licence link has changed is not relivant.
5298  *
5299  * Fork - LGPL
5300  * <script type="text/javascript">
5301  */
5302  
5303
5304 /**
5305  * @class Roo.grid.ColumnModel
5306  * @extends Roo.util.Observable
5307  * This is the default implementation of a ColumnModel used by the Grid. It defines
5308  * the columns in the grid.
5309  * <br>Usage:<br>
5310  <pre><code>
5311  var colModel = new Roo.grid.ColumnModel([
5312         {header: "Ticker", width: 60, sortable: true, locked: true},
5313         {header: "Company Name", width: 150, sortable: true},
5314         {header: "Market Cap.", width: 100, sortable: true},
5315         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5316         {header: "Employees", width: 100, sortable: true, resizable: false}
5317  ]);
5318  </code></pre>
5319  * <p>
5320  
5321  * The config options listed for this class are options which may appear in each
5322  * individual column definition.
5323  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5324  * @constructor
5325  * @param {Object} config An Array of column config objects. See this class's
5326  * config objects for details.
5327 */
5328 Roo.grid.ColumnModel = function(config){
5329         /**
5330      * The config passed into the constructor
5331      */
5332     this.config = config;
5333     this.lookup = {};
5334
5335     // if no id, create one
5336     // if the column does not have a dataIndex mapping,
5337     // map it to the order it is in the config
5338     for(var i = 0, len = config.length; i < len; i++){
5339         var c = config[i];
5340         if(typeof c.dataIndex == "undefined"){
5341             c.dataIndex = i;
5342         }
5343         if(typeof c.renderer == "string"){
5344             c.renderer = Roo.util.Format[c.renderer];
5345         }
5346         if(typeof c.id == "undefined"){
5347             c.id = Roo.id();
5348         }
5349         if(c.editor && c.editor.xtype){
5350             c.editor  = Roo.factory(c.editor, Roo.grid);
5351         }
5352         if(c.editor && c.editor.isFormField){
5353             c.editor = new Roo.grid.GridEditor(c.editor);
5354         }
5355         this.lookup[c.id] = c;
5356     }
5357
5358     /**
5359      * The width of columns which have no width specified (defaults to 100)
5360      * @type Number
5361      */
5362     this.defaultWidth = 100;
5363
5364     /**
5365      * Default sortable of columns which have no sortable specified (defaults to false)
5366      * @type Boolean
5367      */
5368     this.defaultSortable = false;
5369
5370     this.addEvents({
5371         /**
5372              * @event widthchange
5373              * Fires when the width of a column changes.
5374              * @param {ColumnModel} this
5375              * @param {Number} columnIndex The column index
5376              * @param {Number} newWidth The new width
5377              */
5378             "widthchange": true,
5379         /**
5380              * @event headerchange
5381              * Fires when the text of a header changes.
5382              * @param {ColumnModel} this
5383              * @param {Number} columnIndex The column index
5384              * @param {Number} newText The new header text
5385              */
5386             "headerchange": true,
5387         /**
5388              * @event hiddenchange
5389              * Fires when a column is hidden or "unhidden".
5390              * @param {ColumnModel} this
5391              * @param {Number} columnIndex The column index
5392              * @param {Boolean} hidden true if hidden, false otherwise
5393              */
5394             "hiddenchange": true,
5395             /**
5396          * @event columnmoved
5397          * Fires when a column is moved.
5398          * @param {ColumnModel} this
5399          * @param {Number} oldIndex
5400          * @param {Number} newIndex
5401          */
5402         "columnmoved" : true,
5403         /**
5404          * @event columlockchange
5405          * Fires when a column's locked state is changed
5406          * @param {ColumnModel} this
5407          * @param {Number} colIndex
5408          * @param {Boolean} locked true if locked
5409          */
5410         "columnlockchange" : true
5411     });
5412     Roo.grid.ColumnModel.superclass.constructor.call(this);
5413 };
5414 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5415     /**
5416      * @cfg {String} header The header text to display in the Grid view.
5417      */
5418     /**
5419      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5420      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5421      * specified, the column's index is used as an index into the Record's data Array.
5422      */
5423     /**
5424      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5425      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5426      */
5427     /**
5428      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5429      * Defaults to the value of the {@link #defaultSortable} property.
5430      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5431      */
5432     /**
5433      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5434      */
5435     /**
5436      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5437      */
5438     /**
5439      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5440      */
5441     /**
5442      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5443      */
5444     /**
5445      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5446      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5447      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5448      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5449      */
5450        /**
5451      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5452      */
5453     /**
5454      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5455      */
5456     /**
5457      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5458      */
5459     /**
5460      * @cfg {String} cursor (Optional)
5461      */
5462     /**
5463      * @cfg {String} tooltip (Optional)
5464      */
5465     /**
5466      * @cfg {Number} xs (Optional)
5467      */
5468     /**
5469      * @cfg {Number} sm (Optional)
5470      */
5471     /**
5472      * @cfg {Number} md (Optional)
5473      */
5474     /**
5475      * @cfg {Number} lg (Optional)
5476      */
5477     /**
5478      * Returns the id of the column at the specified index.
5479      * @param {Number} index The column index
5480      * @return {String} the id
5481      */
5482     getColumnId : function(index){
5483         return this.config[index].id;
5484     },
5485
5486     /**
5487      * Returns the column for a specified id.
5488      * @param {String} id The column id
5489      * @return {Object} the column
5490      */
5491     getColumnById : function(id){
5492         return this.lookup[id];
5493     },
5494
5495     
5496     /**
5497      * Returns the column for a specified dataIndex.
5498      * @param {String} dataIndex The column dataIndex
5499      * @return {Object|Boolean} the column or false if not found
5500      */
5501     getColumnByDataIndex: function(dataIndex){
5502         var index = this.findColumnIndex(dataIndex);
5503         return index > -1 ? this.config[index] : false;
5504     },
5505     
5506     /**
5507      * Returns the index for a specified column id.
5508      * @param {String} id The column id
5509      * @return {Number} the index, or -1 if not found
5510      */
5511     getIndexById : function(id){
5512         for(var i = 0, len = this.config.length; i < len; i++){
5513             if(this.config[i].id == id){
5514                 return i;
5515             }
5516         }
5517         return -1;
5518     },
5519     
5520     /**
5521      * Returns the index for a specified column dataIndex.
5522      * @param {String} dataIndex The column dataIndex
5523      * @return {Number} the index, or -1 if not found
5524      */
5525     
5526     findColumnIndex : function(dataIndex){
5527         for(var i = 0, len = this.config.length; i < len; i++){
5528             if(this.config[i].dataIndex == dataIndex){
5529                 return i;
5530             }
5531         }
5532         return -1;
5533     },
5534     
5535     
5536     moveColumn : function(oldIndex, newIndex){
5537         var c = this.config[oldIndex];
5538         this.config.splice(oldIndex, 1);
5539         this.config.splice(newIndex, 0, c);
5540         this.dataMap = null;
5541         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5542     },
5543
5544     isLocked : function(colIndex){
5545         return this.config[colIndex].locked === true;
5546     },
5547
5548     setLocked : function(colIndex, value, suppressEvent){
5549         if(this.isLocked(colIndex) == value){
5550             return;
5551         }
5552         this.config[colIndex].locked = value;
5553         if(!suppressEvent){
5554             this.fireEvent("columnlockchange", this, colIndex, value);
5555         }
5556     },
5557
5558     getTotalLockedWidth : function(){
5559         var totalWidth = 0;
5560         for(var i = 0; i < this.config.length; i++){
5561             if(this.isLocked(i) && !this.isHidden(i)){
5562                 this.totalWidth += this.getColumnWidth(i);
5563             }
5564         }
5565         return totalWidth;
5566     },
5567
5568     getLockedCount : function(){
5569         for(var i = 0, len = this.config.length; i < len; i++){
5570             if(!this.isLocked(i)){
5571                 return i;
5572             }
5573         }
5574         
5575         return this.config.length;
5576     },
5577
5578     /**
5579      * Returns the number of columns.
5580      * @return {Number}
5581      */
5582     getColumnCount : function(visibleOnly){
5583         if(visibleOnly === true){
5584             var c = 0;
5585             for(var i = 0, len = this.config.length; i < len; i++){
5586                 if(!this.isHidden(i)){
5587                     c++;
5588                 }
5589             }
5590             return c;
5591         }
5592         return this.config.length;
5593     },
5594
5595     /**
5596      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5597      * @param {Function} fn
5598      * @param {Object} scope (optional)
5599      * @return {Array} result
5600      */
5601     getColumnsBy : function(fn, scope){
5602         var r = [];
5603         for(var i = 0, len = this.config.length; i < len; i++){
5604             var c = this.config[i];
5605             if(fn.call(scope||this, c, i) === true){
5606                 r[r.length] = c;
5607             }
5608         }
5609         return r;
5610     },
5611
5612     /**
5613      * Returns true if the specified column is sortable.
5614      * @param {Number} col The column index
5615      * @return {Boolean}
5616      */
5617     isSortable : function(col){
5618         if(typeof this.config[col].sortable == "undefined"){
5619             return this.defaultSortable;
5620         }
5621         return this.config[col].sortable;
5622     },
5623
5624     /**
5625      * Returns the rendering (formatting) function defined for the column.
5626      * @param {Number} col The column index.
5627      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5628      */
5629     getRenderer : function(col){
5630         if(!this.config[col].renderer){
5631             return Roo.grid.ColumnModel.defaultRenderer;
5632         }
5633         return this.config[col].renderer;
5634     },
5635
5636     /**
5637      * Sets the rendering (formatting) function for a column.
5638      * @param {Number} col The column index
5639      * @param {Function} fn The function to use to process the cell's raw data
5640      * to return HTML markup for the grid view. The render function is called with
5641      * the following parameters:<ul>
5642      * <li>Data value.</li>
5643      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5644      * <li>css A CSS style string to apply to the table cell.</li>
5645      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5646      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5647      * <li>Row index</li>
5648      * <li>Column index</li>
5649      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5650      */
5651     setRenderer : function(col, fn){
5652         this.config[col].renderer = fn;
5653     },
5654
5655     /**
5656      * Returns the width for the specified column.
5657      * @param {Number} col The column index
5658      * @return {Number}
5659      */
5660     getColumnWidth : function(col){
5661         return this.config[col].width * 1 || this.defaultWidth;
5662     },
5663
5664     /**
5665      * Sets the width for a column.
5666      * @param {Number} col The column index
5667      * @param {Number} width The new width
5668      */
5669     setColumnWidth : function(col, width, suppressEvent){
5670         this.config[col].width = width;
5671         this.totalWidth = null;
5672         if(!suppressEvent){
5673              this.fireEvent("widthchange", this, col, width);
5674         }
5675     },
5676
5677     /**
5678      * Returns the total width of all columns.
5679      * @param {Boolean} includeHidden True to include hidden column widths
5680      * @return {Number}
5681      */
5682     getTotalWidth : function(includeHidden){
5683         if(!this.totalWidth){
5684             this.totalWidth = 0;
5685             for(var i = 0, len = this.config.length; i < len; i++){
5686                 if(includeHidden || !this.isHidden(i)){
5687                     this.totalWidth += this.getColumnWidth(i);
5688                 }
5689             }
5690         }
5691         return this.totalWidth;
5692     },
5693
5694     /**
5695      * Returns the header for the specified column.
5696      * @param {Number} col The column index
5697      * @return {String}
5698      */
5699     getColumnHeader : function(col){
5700         return this.config[col].header;
5701     },
5702
5703     /**
5704      * Sets the header for a column.
5705      * @param {Number} col The column index
5706      * @param {String} header The new header
5707      */
5708     setColumnHeader : function(col, header){
5709         this.config[col].header = header;
5710         this.fireEvent("headerchange", this, col, header);
5711     },
5712
5713     /**
5714      * Returns the tooltip for the specified column.
5715      * @param {Number} col The column index
5716      * @return {String}
5717      */
5718     getColumnTooltip : function(col){
5719             return this.config[col].tooltip;
5720     },
5721     /**
5722      * Sets the tooltip for a column.
5723      * @param {Number} col The column index
5724      * @param {String} tooltip The new tooltip
5725      */
5726     setColumnTooltip : function(col, tooltip){
5727             this.config[col].tooltip = tooltip;
5728     },
5729
5730     /**
5731      * Returns the dataIndex for the specified column.
5732      * @param {Number} col The column index
5733      * @return {Number}
5734      */
5735     getDataIndex : function(col){
5736         return this.config[col].dataIndex;
5737     },
5738
5739     /**
5740      * Sets the dataIndex for a column.
5741      * @param {Number} col The column index
5742      * @param {Number} dataIndex The new dataIndex
5743      */
5744     setDataIndex : function(col, dataIndex){
5745         this.config[col].dataIndex = dataIndex;
5746     },
5747
5748     
5749     
5750     /**
5751      * Returns true if the cell is editable.
5752      * @param {Number} colIndex The column index
5753      * @param {Number} rowIndex The row index - this is nto actually used..?
5754      * @return {Boolean}
5755      */
5756     isCellEditable : function(colIndex, rowIndex){
5757         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5758     },
5759
5760     /**
5761      * Returns the editor defined for the cell/column.
5762      * return false or null to disable editing.
5763      * @param {Number} colIndex The column index
5764      * @param {Number} rowIndex The row index
5765      * @return {Object}
5766      */
5767     getCellEditor : function(colIndex, rowIndex){
5768         return this.config[colIndex].editor;
5769     },
5770
5771     /**
5772      * Sets if a column is editable.
5773      * @param {Number} col The column index
5774      * @param {Boolean} editable True if the column is editable
5775      */
5776     setEditable : function(col, editable){
5777         this.config[col].editable = editable;
5778     },
5779
5780
5781     /**
5782      * Returns true if the column is hidden.
5783      * @param {Number} colIndex The column index
5784      * @return {Boolean}
5785      */
5786     isHidden : function(colIndex){
5787         return this.config[colIndex].hidden;
5788     },
5789
5790
5791     /**
5792      * Returns true if the column width cannot be changed
5793      */
5794     isFixed : function(colIndex){
5795         return this.config[colIndex].fixed;
5796     },
5797
5798     /**
5799      * Returns true if the column can be resized
5800      * @return {Boolean}
5801      */
5802     isResizable : function(colIndex){
5803         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5804     },
5805     /**
5806      * Sets if a column is hidden.
5807      * @param {Number} colIndex The column index
5808      * @param {Boolean} hidden True if the column is hidden
5809      */
5810     setHidden : function(colIndex, hidden){
5811         this.config[colIndex].hidden = hidden;
5812         this.totalWidth = null;
5813         this.fireEvent("hiddenchange", this, colIndex, hidden);
5814     },
5815
5816     /**
5817      * Sets the editor for a column.
5818      * @param {Number} col The column index
5819      * @param {Object} editor The editor object
5820      */
5821     setEditor : function(col, editor){
5822         this.config[col].editor = editor;
5823     }
5824 });
5825
5826 Roo.grid.ColumnModel.defaultRenderer = function(value)
5827 {
5828     if(typeof value == "object") {
5829         return value;
5830     }
5831         if(typeof value == "string" && value.length < 1){
5832             return "&#160;";
5833         }
5834     
5835         return String.format("{0}", value);
5836 };
5837
5838 // Alias for backwards compatibility
5839 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5840 /*
5841  * Based on:
5842  * Ext JS Library 1.1.1
5843  * Copyright(c) 2006-2007, Ext JS, LLC.
5844  *
5845  * Originally Released Under LGPL - original licence link has changed is not relivant.
5846  *
5847  * Fork - LGPL
5848  * <script type="text/javascript">
5849  */
5850  
5851 /**
5852  * @class Roo.LoadMask
5853  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5854  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5855  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5856  * element's UpdateManager load indicator and will be destroyed after the initial load.
5857  * @constructor
5858  * Create a new LoadMask
5859  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5860  * @param {Object} config The config object
5861  */
5862 Roo.LoadMask = function(el, config){
5863     this.el = Roo.get(el);
5864     Roo.apply(this, config);
5865     if(this.store){
5866         this.store.on('beforeload', this.onBeforeLoad, this);
5867         this.store.on('load', this.onLoad, this);
5868         this.store.on('loadexception', this.onLoadException, this);
5869         this.removeMask = false;
5870     }else{
5871         var um = this.el.getUpdateManager();
5872         um.showLoadIndicator = false; // disable the default indicator
5873         um.on('beforeupdate', this.onBeforeLoad, this);
5874         um.on('update', this.onLoad, this);
5875         um.on('failure', this.onLoad, this);
5876         this.removeMask = true;
5877     }
5878 };
5879
5880 Roo.LoadMask.prototype = {
5881     /**
5882      * @cfg {Boolean} removeMask
5883      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5884      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5885      */
5886     /**
5887      * @cfg {String} msg
5888      * The text to display in a centered loading message box (defaults to 'Loading...')
5889      */
5890     msg : 'Loading...',
5891     /**
5892      * @cfg {String} msgCls
5893      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5894      */
5895     msgCls : 'x-mask-loading',
5896
5897     /**
5898      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5899      * @type Boolean
5900      */
5901     disabled: false,
5902
5903     /**
5904      * Disables the mask to prevent it from being displayed
5905      */
5906     disable : function(){
5907        this.disabled = true;
5908     },
5909
5910     /**
5911      * Enables the mask so that it can be displayed
5912      */
5913     enable : function(){
5914         this.disabled = false;
5915     },
5916     
5917     onLoadException : function()
5918     {
5919         Roo.log(arguments);
5920         
5921         if (typeof(arguments[3]) != 'undefined') {
5922             Roo.MessageBox.alert("Error loading",arguments[3]);
5923         } 
5924         /*
5925         try {
5926             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5927                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5928             }   
5929         } catch(e) {
5930             
5931         }
5932         */
5933     
5934         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5935     },
5936     // private
5937     onLoad : function()
5938     {
5939         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5940     },
5941
5942     // private
5943     onBeforeLoad : function(){
5944         if(!this.disabled){
5945             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5946         }
5947     },
5948
5949     // private
5950     destroy : function(){
5951         if(this.store){
5952             this.store.un('beforeload', this.onBeforeLoad, this);
5953             this.store.un('load', this.onLoad, this);
5954             this.store.un('loadexception', this.onLoadException, this);
5955         }else{
5956             var um = this.el.getUpdateManager();
5957             um.un('beforeupdate', this.onBeforeLoad, this);
5958             um.un('update', this.onLoad, this);
5959             um.un('failure', this.onLoad, this);
5960         }
5961     }
5962 };/*
5963  * - LGPL
5964  *
5965  * table
5966  * 
5967  */
5968
5969 /**
5970  * @class Roo.bootstrap.Table
5971  * @extends Roo.bootstrap.Component
5972  * Bootstrap Table class
5973  * @cfg {String} cls table class
5974  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5975  * @cfg {String} bgcolor Specifies the background color for a table
5976  * @cfg {Number} border Specifies whether the table cells should have borders or not
5977  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5978  * @cfg {Number} cellspacing Specifies the space between cells
5979  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5980  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5981  * @cfg {String} sortable Specifies that the table should be sortable
5982  * @cfg {String} summary Specifies a summary of the content of a table
5983  * @cfg {Number} width Specifies the width of a table
5984  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5985  * 
5986  * @cfg {boolean} striped Should the rows be alternative striped
5987  * @cfg {boolean} bordered Add borders to the table
5988  * @cfg {boolean} hover Add hover highlighting
5989  * @cfg {boolean} condensed Format condensed
5990  * @cfg {boolean} responsive Format condensed
5991  * @cfg {Boolean} loadMask (true|false) default false
5992  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5993  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5994  * @cfg {Boolean} rowSelection (true|false) default false
5995  * @cfg {Boolean} cellSelection (true|false) default false
5996  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5997  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5998  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5999  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6000  
6001  * 
6002  * @constructor
6003  * Create a new Table
6004  * @param {Object} config The config object
6005  */
6006
6007 Roo.bootstrap.Table = function(config){
6008     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6009     
6010   
6011     
6012     // BC...
6013     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6014     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6015     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6016     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6017     
6018     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6019     if (this.sm) {
6020         this.sm.grid = this;
6021         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6022         this.sm = this.selModel;
6023         this.sm.xmodule = this.xmodule || false;
6024     }
6025     
6026     if (this.cm && typeof(this.cm.config) == 'undefined') {
6027         this.colModel = new Roo.grid.ColumnModel(this.cm);
6028         this.cm = this.colModel;
6029         this.cm.xmodule = this.xmodule || false;
6030     }
6031     if (this.store) {
6032         this.store= Roo.factory(this.store, Roo.data);
6033         this.ds = this.store;
6034         this.ds.xmodule = this.xmodule || false;
6035          
6036     }
6037     if (this.footer && this.store) {
6038         this.footer.dataSource = this.ds;
6039         this.footer = Roo.factory(this.footer);
6040     }
6041     
6042     /** @private */
6043     this.addEvents({
6044         /**
6045          * @event cellclick
6046          * Fires when a cell is clicked
6047          * @param {Roo.bootstrap.Table} this
6048          * @param {Roo.Element} el
6049          * @param {Number} rowIndex
6050          * @param {Number} columnIndex
6051          * @param {Roo.EventObject} e
6052          */
6053         "cellclick" : true,
6054         /**
6055          * @event celldblclick
6056          * Fires when a cell is double clicked
6057          * @param {Roo.bootstrap.Table} this
6058          * @param {Roo.Element} el
6059          * @param {Number} rowIndex
6060          * @param {Number} columnIndex
6061          * @param {Roo.EventObject} e
6062          */
6063         "celldblclick" : true,
6064         /**
6065          * @event rowclick
6066          * Fires when a row is clicked
6067          * @param {Roo.bootstrap.Table} this
6068          * @param {Roo.Element} el
6069          * @param {Number} rowIndex
6070          * @param {Roo.EventObject} e
6071          */
6072         "rowclick" : true,
6073         /**
6074          * @event rowdblclick
6075          * Fires when a row is double clicked
6076          * @param {Roo.bootstrap.Table} this
6077          * @param {Roo.Element} el
6078          * @param {Number} rowIndex
6079          * @param {Roo.EventObject} e
6080          */
6081         "rowdblclick" : true,
6082         /**
6083          * @event mouseover
6084          * Fires when a mouseover occur
6085          * @param {Roo.bootstrap.Table} this
6086          * @param {Roo.Element} el
6087          * @param {Number} rowIndex
6088          * @param {Number} columnIndex
6089          * @param {Roo.EventObject} e
6090          */
6091         "mouseover" : true,
6092         /**
6093          * @event mouseout
6094          * Fires when a mouseout occur
6095          * @param {Roo.bootstrap.Table} this
6096          * @param {Roo.Element} el
6097          * @param {Number} rowIndex
6098          * @param {Number} columnIndex
6099          * @param {Roo.EventObject} e
6100          */
6101         "mouseout" : true,
6102         /**
6103          * @event rowclass
6104          * Fires when a row is rendered, so you can change add a style to it.
6105          * @param {Roo.bootstrap.Table} this
6106          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6107          */
6108         'rowclass' : true,
6109           /**
6110          * @event rowsrendered
6111          * Fires when all the  rows have been rendered
6112          * @param {Roo.bootstrap.Table} this
6113          */
6114         'rowsrendered' : true,
6115         /**
6116          * @event contextmenu
6117          * The raw contextmenu event for the entire grid.
6118          * @param {Roo.EventObject} e
6119          */
6120         "contextmenu" : true,
6121         /**
6122          * @event rowcontextmenu
6123          * Fires when a row is right clicked
6124          * @param {Roo.bootstrap.Table} this
6125          * @param {Number} rowIndex
6126          * @param {Roo.EventObject} e
6127          */
6128         "rowcontextmenu" : true,
6129         /**
6130          * @event cellcontextmenu
6131          * Fires when a cell is right clicked
6132          * @param {Roo.bootstrap.Table} this
6133          * @param {Number} rowIndex
6134          * @param {Number} cellIndex
6135          * @param {Roo.EventObject} e
6136          */
6137          "cellcontextmenu" : true,
6138          /**
6139          * @event headercontextmenu
6140          * Fires when a header is right clicked
6141          * @param {Roo.bootstrap.Table} this
6142          * @param {Number} columnIndex
6143          * @param {Roo.EventObject} e
6144          */
6145         "headercontextmenu" : true
6146     });
6147 };
6148
6149 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6150     
6151     cls: false,
6152     align: false,
6153     bgcolor: false,
6154     border: false,
6155     cellpadding: false,
6156     cellspacing: false,
6157     frame: false,
6158     rules: false,
6159     sortable: false,
6160     summary: false,
6161     width: false,
6162     striped : false,
6163     scrollBody : false,
6164     bordered: false,
6165     hover:  false,
6166     condensed : false,
6167     responsive : false,
6168     sm : false,
6169     cm : false,
6170     store : false,
6171     loadMask : false,
6172     footerShow : true,
6173     headerShow : true,
6174   
6175     rowSelection : false,
6176     cellSelection : false,
6177     layout : false,
6178     
6179     // Roo.Element - the tbody
6180     mainBody: false,
6181     // Roo.Element - thead element
6182     mainHead: false,
6183     
6184     container: false, // used by gridpanel...
6185     
6186     lazyLoad : false,
6187     
6188     CSS : Roo.util.CSS,
6189     
6190     auto_hide_footer : false,
6191     
6192     getAutoCreate : function()
6193     {
6194         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6195         
6196         cfg = {
6197             tag: 'table',
6198             cls : 'table',
6199             cn : []
6200         };
6201         if (this.scrollBody) {
6202             cfg.cls += ' table-body-fixed';
6203         }    
6204         if (this.striped) {
6205             cfg.cls += ' table-striped';
6206         }
6207         
6208         if (this.hover) {
6209             cfg.cls += ' table-hover';
6210         }
6211         if (this.bordered) {
6212             cfg.cls += ' table-bordered';
6213         }
6214         if (this.condensed) {
6215             cfg.cls += ' table-condensed';
6216         }
6217         if (this.responsive) {
6218             cfg.cls += ' table-responsive';
6219         }
6220         
6221         if (this.cls) {
6222             cfg.cls+=  ' ' +this.cls;
6223         }
6224         
6225         // this lot should be simplifed...
6226         var _t = this;
6227         var cp = [
6228             'align',
6229             'bgcolor',
6230             'border',
6231             'cellpadding',
6232             'cellspacing',
6233             'frame',
6234             'rules',
6235             'sortable',
6236             'summary',
6237             'width'
6238         ].forEach(function(k) {
6239             if (_t[k]) {
6240                 cfg[k] = _t[k];
6241             }
6242         });
6243         
6244         
6245         if (this.layout) {
6246             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6247         }
6248         
6249         if(this.store || this.cm){
6250             if(this.headerShow){
6251                 cfg.cn.push(this.renderHeader());
6252             }
6253             
6254             cfg.cn.push(this.renderBody());
6255             
6256             if(this.footerShow){
6257                 cfg.cn.push(this.renderFooter());
6258             }
6259             // where does this come from?
6260             //cfg.cls+=  ' TableGrid';
6261         }
6262         
6263         return { cn : [ cfg ] };
6264     },
6265     
6266     initEvents : function()
6267     {   
6268         if(!this.store || !this.cm){
6269             return;
6270         }
6271         if (this.selModel) {
6272             this.selModel.initEvents();
6273         }
6274         
6275         
6276         //Roo.log('initEvents with ds!!!!');
6277         
6278         this.mainBody = this.el.select('tbody', true).first();
6279         this.mainHead = this.el.select('thead', true).first();
6280         this.mainFoot = this.el.select('tfoot', true).first();
6281         
6282         
6283         
6284         var _this = this;
6285         
6286         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6287             e.on('click', _this.sort, _this);
6288         });
6289         
6290         this.mainBody.on("click", this.onClick, this);
6291         this.mainBody.on("dblclick", this.onDblClick, this);
6292         
6293         // why is this done????? = it breaks dialogs??
6294         //this.parent().el.setStyle('position', 'relative');
6295         
6296         
6297         if (this.footer) {
6298             this.footer.parentId = this.id;
6299             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6300             
6301             if(this.lazyLoad){
6302                 this.el.select('tfoot tr td').first().addClass('hide');
6303             }
6304         } 
6305         
6306         if(this.loadMask) {
6307             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6308         }
6309         
6310         this.store.on('load', this.onLoad, this);
6311         this.store.on('beforeload', this.onBeforeLoad, this);
6312         this.store.on('update', this.onUpdate, this);
6313         this.store.on('add', this.onAdd, this);
6314         this.store.on("clear", this.clear, this);
6315         
6316         this.el.on("contextmenu", this.onContextMenu, this);
6317         
6318         this.mainBody.on('scroll', this.onBodyScroll, this);
6319         
6320         this.cm.on("headerchange", this.onHeaderChange, this);
6321         
6322         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6323         
6324     },
6325     
6326     onContextMenu : function(e, t)
6327     {
6328         this.processEvent("contextmenu", e);
6329     },
6330     
6331     processEvent : function(name, e)
6332     {
6333         if (name != 'touchstart' ) {
6334             this.fireEvent(name, e);    
6335         }
6336         
6337         var t = e.getTarget();
6338         
6339         var cell = Roo.get(t);
6340         
6341         if(!cell){
6342             return;
6343         }
6344         
6345         if(cell.findParent('tfoot', false, true)){
6346             return;
6347         }
6348         
6349         if(cell.findParent('thead', false, true)){
6350             
6351             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6352                 cell = Roo.get(t).findParent('th', false, true);
6353                 if (!cell) {
6354                     Roo.log("failed to find th in thead?");
6355                     Roo.log(e.getTarget());
6356                     return;
6357                 }
6358             }
6359             
6360             var cellIndex = cell.dom.cellIndex;
6361             
6362             var ename = name == 'touchstart' ? 'click' : name;
6363             this.fireEvent("header" + ename, this, cellIndex, e);
6364             
6365             return;
6366         }
6367         
6368         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6369             cell = Roo.get(t).findParent('td', false, true);
6370             if (!cell) {
6371                 Roo.log("failed to find th in tbody?");
6372                 Roo.log(e.getTarget());
6373                 return;
6374             }
6375         }
6376         
6377         var row = cell.findParent('tr', false, true);
6378         var cellIndex = cell.dom.cellIndex;
6379         var rowIndex = row.dom.rowIndex - 1;
6380         
6381         if(row !== false){
6382             
6383             this.fireEvent("row" + name, this, rowIndex, e);
6384             
6385             if(cell !== false){
6386             
6387                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6388             }
6389         }
6390         
6391     },
6392     
6393     onMouseover : function(e, el)
6394     {
6395         var cell = Roo.get(el);
6396         
6397         if(!cell){
6398             return;
6399         }
6400         
6401         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6402             cell = cell.findParent('td', false, true);
6403         }
6404         
6405         var row = cell.findParent('tr', false, true);
6406         var cellIndex = cell.dom.cellIndex;
6407         var rowIndex = row.dom.rowIndex - 1; // start from 0
6408         
6409         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6410         
6411     },
6412     
6413     onMouseout : function(e, el)
6414     {
6415         var cell = Roo.get(el);
6416         
6417         if(!cell){
6418             return;
6419         }
6420         
6421         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6422             cell = cell.findParent('td', false, true);
6423         }
6424         
6425         var row = cell.findParent('tr', false, true);
6426         var cellIndex = cell.dom.cellIndex;
6427         var rowIndex = row.dom.rowIndex - 1; // start from 0
6428         
6429         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6430         
6431     },
6432     
6433     onClick : function(e, el)
6434     {
6435         var cell = Roo.get(el);
6436         
6437         if(!cell || (!this.cellSelection && !this.rowSelection)){
6438             return;
6439         }
6440         
6441         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6442             cell = cell.findParent('td', false, true);
6443         }
6444         
6445         if(!cell || typeof(cell) == 'undefined'){
6446             return;
6447         }
6448         
6449         var row = cell.findParent('tr', false, true);
6450         
6451         if(!row || typeof(row) == 'undefined'){
6452             return;
6453         }
6454         
6455         var cellIndex = cell.dom.cellIndex;
6456         var rowIndex = this.getRowIndex(row);
6457         
6458         // why??? - should these not be based on SelectionModel?
6459         if(this.cellSelection){
6460             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6461         }
6462         
6463         if(this.rowSelection){
6464             this.fireEvent('rowclick', this, row, rowIndex, e);
6465         }
6466         
6467         
6468     },
6469         
6470     onDblClick : function(e,el)
6471     {
6472         var cell = Roo.get(el);
6473         
6474         if(!cell || (!this.cellSelection && !this.rowSelection)){
6475             return;
6476         }
6477         
6478         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6479             cell = cell.findParent('td', false, true);
6480         }
6481         
6482         if(!cell || typeof(cell) == 'undefined'){
6483             return;
6484         }
6485         
6486         var row = cell.findParent('tr', false, true);
6487         
6488         if(!row || typeof(row) == 'undefined'){
6489             return;
6490         }
6491         
6492         var cellIndex = cell.dom.cellIndex;
6493         var rowIndex = this.getRowIndex(row);
6494         
6495         if(this.cellSelection){
6496             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6497         }
6498         
6499         if(this.rowSelection){
6500             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6501         }
6502     },
6503     
6504     sort : function(e,el)
6505     {
6506         var col = Roo.get(el);
6507         
6508         if(!col.hasClass('sortable')){
6509             return;
6510         }
6511         
6512         var sort = col.attr('sort');
6513         var dir = 'ASC';
6514         
6515         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6516             dir = 'DESC';
6517         }
6518         
6519         this.store.sortInfo = {field : sort, direction : dir};
6520         
6521         if (this.footer) {
6522             Roo.log("calling footer first");
6523             this.footer.onClick('first');
6524         } else {
6525         
6526             this.store.load({ params : { start : 0 } });
6527         }
6528     },
6529     
6530     renderHeader : function()
6531     {
6532         var header = {
6533             tag: 'thead',
6534             cn : []
6535         };
6536         
6537         var cm = this.cm;
6538         this.totalWidth = 0;
6539         
6540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6541             
6542             var config = cm.config[i];
6543             
6544             var c = {
6545                 tag: 'th',
6546                 cls : 'x-hcol-' + i,
6547                 style : '',
6548                 html: cm.getColumnHeader(i)
6549             };
6550             
6551             var hh = '';
6552             
6553             if(typeof(config.sortable) != 'undefined' && config.sortable){
6554                 c.cls = 'sortable';
6555                 c.html = '<i class="glyphicon"></i>' + c.html;
6556             }
6557             
6558             if(typeof(config.lgHeader) != 'undefined'){
6559                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6560             }
6561             
6562             if(typeof(config.mdHeader) != 'undefined'){
6563                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6564             }
6565             
6566             if(typeof(config.smHeader) != 'undefined'){
6567                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6568             }
6569             
6570             if(typeof(config.xsHeader) != 'undefined'){
6571                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6572             }
6573             
6574             if(hh.length){
6575                 c.html = hh;
6576             }
6577             
6578             if(typeof(config.tooltip) != 'undefined'){
6579                 c.tooltip = config.tooltip;
6580             }
6581             
6582             if(typeof(config.colspan) != 'undefined'){
6583                 c.colspan = config.colspan;
6584             }
6585             
6586             if(typeof(config.hidden) != 'undefined' && config.hidden){
6587                 c.style += ' display:none;';
6588             }
6589             
6590             if(typeof(config.dataIndex) != 'undefined'){
6591                 c.sort = config.dataIndex;
6592             }
6593             
6594            
6595             
6596             if(typeof(config.align) != 'undefined' && config.align.length){
6597                 c.style += ' text-align:' + config.align + ';';
6598             }
6599             
6600             if(typeof(config.width) != 'undefined'){
6601                 c.style += ' width:' + config.width + 'px;';
6602                 this.totalWidth += config.width;
6603             } else {
6604                 this.totalWidth += 100; // assume minimum of 100 per column?
6605             }
6606             
6607             if(typeof(config.cls) != 'undefined'){
6608                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6609             }
6610             
6611             ['xs','sm','md','lg'].map(function(size){
6612                 
6613                 if(typeof(config[size]) == 'undefined'){
6614                     return;
6615                 }
6616                 
6617                 if (!config[size]) { // 0 = hidden
6618                     c.cls += ' hidden-' + size;
6619                     return;
6620                 }
6621                 
6622                 c.cls += ' col-' + size + '-' + config[size];
6623
6624             });
6625             
6626             header.cn.push(c)
6627         }
6628         
6629         return header;
6630     },
6631     
6632     renderBody : function()
6633     {
6634         var body = {
6635             tag: 'tbody',
6636             cn : [
6637                 {
6638                     tag: 'tr',
6639                     cn : [
6640                         {
6641                             tag : 'td',
6642                             colspan :  this.cm.getColumnCount()
6643                         }
6644                     ]
6645                 }
6646             ]
6647         };
6648         
6649         return body;
6650     },
6651     
6652     renderFooter : function()
6653     {
6654         var footer = {
6655             tag: 'tfoot',
6656             cn : [
6657                 {
6658                     tag: 'tr',
6659                     cn : [
6660                         {
6661                             tag : 'td',
6662                             colspan :  this.cm.getColumnCount()
6663                         }
6664                     ]
6665                 }
6666             ]
6667         };
6668         
6669         return footer;
6670     },
6671     
6672     
6673     
6674     onLoad : function()
6675     {
6676 //        Roo.log('ds onload');
6677         this.clear();
6678         
6679         var _this = this;
6680         var cm = this.cm;
6681         var ds = this.store;
6682         
6683         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6684             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6685             if (_this.store.sortInfo) {
6686                     
6687                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6688                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6689                 }
6690                 
6691                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6692                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6693                 }
6694             }
6695         });
6696         
6697         var tbody =  this.mainBody;
6698               
6699         if(ds.getCount() > 0){
6700             ds.data.each(function(d,rowIndex){
6701                 var row =  this.renderRow(cm, ds, rowIndex);
6702                 
6703                 tbody.createChild(row);
6704                 
6705                 var _this = this;
6706                 
6707                 if(row.cellObjects.length){
6708                     Roo.each(row.cellObjects, function(r){
6709                         _this.renderCellObject(r);
6710                     })
6711                 }
6712                 
6713             }, this);
6714         }
6715         
6716         var tfoot = this.el.select('tfoot', true).first();
6717         
6718         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6719             
6720             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6721             
6722             var total = this.ds.getTotalCount();
6723             
6724             if(this.footer.pageSize < total){
6725                 this.mainFoot.show();
6726             }
6727         }
6728         
6729         Roo.each(this.el.select('tbody td', true).elements, function(e){
6730             e.on('mouseover', _this.onMouseover, _this);
6731         });
6732         
6733         Roo.each(this.el.select('tbody td', true).elements, function(e){
6734             e.on('mouseout', _this.onMouseout, _this);
6735         });
6736         this.fireEvent('rowsrendered', this);
6737         
6738         this.autoSize();
6739     },
6740     
6741     
6742     onUpdate : function(ds,record)
6743     {
6744         this.refreshRow(record);
6745         this.autoSize();
6746     },
6747     
6748     onRemove : function(ds, record, index, isUpdate){
6749         if(isUpdate !== true){
6750             this.fireEvent("beforerowremoved", this, index, record);
6751         }
6752         var bt = this.mainBody.dom;
6753         
6754         var rows = this.el.select('tbody > tr', true).elements;
6755         
6756         if(typeof(rows[index]) != 'undefined'){
6757             bt.removeChild(rows[index].dom);
6758         }
6759         
6760 //        if(bt.rows[index]){
6761 //            bt.removeChild(bt.rows[index]);
6762 //        }
6763         
6764         if(isUpdate !== true){
6765             //this.stripeRows(index);
6766             //this.syncRowHeights(index, index);
6767             //this.layout();
6768             this.fireEvent("rowremoved", this, index, record);
6769         }
6770     },
6771     
6772     onAdd : function(ds, records, rowIndex)
6773     {
6774         //Roo.log('on Add called');
6775         // - note this does not handle multiple adding very well..
6776         var bt = this.mainBody.dom;
6777         for (var i =0 ; i < records.length;i++) {
6778             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6779             //Roo.log(records[i]);
6780             //Roo.log(this.store.getAt(rowIndex+i));
6781             this.insertRow(this.store, rowIndex + i, false);
6782             return;
6783         }
6784         
6785     },
6786     
6787     
6788     refreshRow : function(record){
6789         var ds = this.store, index;
6790         if(typeof record == 'number'){
6791             index = record;
6792             record = ds.getAt(index);
6793         }else{
6794             index = ds.indexOf(record);
6795         }
6796         this.insertRow(ds, index, true);
6797         this.autoSize();
6798         this.onRemove(ds, record, index+1, true);
6799         this.autoSize();
6800         //this.syncRowHeights(index, index);
6801         //this.layout();
6802         this.fireEvent("rowupdated", this, index, record);
6803     },
6804     
6805     insertRow : function(dm, rowIndex, isUpdate){
6806         
6807         if(!isUpdate){
6808             this.fireEvent("beforerowsinserted", this, rowIndex);
6809         }
6810             //var s = this.getScrollState();
6811         var row = this.renderRow(this.cm, this.store, rowIndex);
6812         // insert before rowIndex..
6813         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6814         
6815         var _this = this;
6816                 
6817         if(row.cellObjects.length){
6818             Roo.each(row.cellObjects, function(r){
6819                 _this.renderCellObject(r);
6820             })
6821         }
6822             
6823         if(!isUpdate){
6824             this.fireEvent("rowsinserted", this, rowIndex);
6825             //this.syncRowHeights(firstRow, lastRow);
6826             //this.stripeRows(firstRow);
6827             //this.layout();
6828         }
6829         
6830     },
6831     
6832     
6833     getRowDom : function(rowIndex)
6834     {
6835         var rows = this.el.select('tbody > tr', true).elements;
6836         
6837         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6838         
6839     },
6840     // returns the object tree for a tr..
6841   
6842     
6843     renderRow : function(cm, ds, rowIndex) 
6844     {
6845         var d = ds.getAt(rowIndex);
6846         
6847         var row = {
6848             tag : 'tr',
6849             cls : 'x-row-' + rowIndex,
6850             cn : []
6851         };
6852             
6853         var cellObjects = [];
6854         
6855         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6856             var config = cm.config[i];
6857             
6858             var renderer = cm.getRenderer(i);
6859             var value = '';
6860             var id = false;
6861             
6862             if(typeof(renderer) !== 'undefined'){
6863                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6864             }
6865             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6866             // and are rendered into the cells after the row is rendered - using the id for the element.
6867             
6868             if(typeof(value) === 'object'){
6869                 id = Roo.id();
6870                 cellObjects.push({
6871                     container : id,
6872                     cfg : value 
6873                 })
6874             }
6875             
6876             var rowcfg = {
6877                 record: d,
6878                 rowIndex : rowIndex,
6879                 colIndex : i,
6880                 rowClass : ''
6881             };
6882
6883             this.fireEvent('rowclass', this, rowcfg);
6884             
6885             var td = {
6886                 tag: 'td',
6887                 cls : rowcfg.rowClass + ' x-col-' + i,
6888                 style: '',
6889                 html: (typeof(value) === 'object') ? '' : value
6890             };
6891             
6892             if (id) {
6893                 td.id = id;
6894             }
6895             
6896             if(typeof(config.colspan) != 'undefined'){
6897                 td.colspan = config.colspan;
6898             }
6899             
6900             if(typeof(config.hidden) != 'undefined' && config.hidden){
6901                 td.style += ' display:none;';
6902             }
6903             
6904             if(typeof(config.align) != 'undefined' && config.align.length){
6905                 td.style += ' text-align:' + config.align + ';';
6906             }
6907             if(typeof(config.valign) != 'undefined' && config.valign.length){
6908                 td.style += ' vertical-align:' + config.valign + ';';
6909             }
6910             
6911             if(typeof(config.width) != 'undefined'){
6912                 td.style += ' width:' +  config.width + 'px;';
6913             }
6914             
6915             if(typeof(config.cursor) != 'undefined'){
6916                 td.style += ' cursor:' +  config.cursor + ';';
6917             }
6918             
6919             if(typeof(config.cls) != 'undefined'){
6920                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6921             }
6922             
6923             ['xs','sm','md','lg'].map(function(size){
6924                 
6925                 if(typeof(config[size]) == 'undefined'){
6926                     return;
6927                 }
6928                 
6929                 if (!config[size]) { // 0 = hidden
6930                     td.cls += ' hidden-' + size;
6931                     return;
6932                 }
6933                 
6934                 td.cls += ' col-' + size + '-' + config[size];
6935
6936             });
6937             
6938             row.cn.push(td);
6939            
6940         }
6941         
6942         row.cellObjects = cellObjects;
6943         
6944         return row;
6945           
6946     },
6947     
6948     
6949     
6950     onBeforeLoad : function()
6951     {
6952         
6953     },
6954      /**
6955      * Remove all rows
6956      */
6957     clear : function()
6958     {
6959         this.el.select('tbody', true).first().dom.innerHTML = '';
6960     },
6961     /**
6962      * Show or hide a row.
6963      * @param {Number} rowIndex to show or hide
6964      * @param {Boolean} state hide
6965      */
6966     setRowVisibility : function(rowIndex, state)
6967     {
6968         var bt = this.mainBody.dom;
6969         
6970         var rows = this.el.select('tbody > tr', true).elements;
6971         
6972         if(typeof(rows[rowIndex]) == 'undefined'){
6973             return;
6974         }
6975         rows[rowIndex].dom.style.display = state ? '' : 'none';
6976     },
6977     
6978     
6979     getSelectionModel : function(){
6980         if(!this.selModel){
6981             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6982         }
6983         return this.selModel;
6984     },
6985     /*
6986      * Render the Roo.bootstrap object from renderder
6987      */
6988     renderCellObject : function(r)
6989     {
6990         var _this = this;
6991         
6992         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6993         
6994         var t = r.cfg.render(r.container);
6995         
6996         if(r.cfg.cn){
6997             Roo.each(r.cfg.cn, function(c){
6998                 var child = {
6999                     container: t.getChildContainer(),
7000                     cfg: c
7001                 };
7002                 _this.renderCellObject(child);
7003             })
7004         }
7005     },
7006     
7007     getRowIndex : function(row)
7008     {
7009         var rowIndex = -1;
7010         
7011         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7012             if(el != row){
7013                 return;
7014             }
7015             
7016             rowIndex = index;
7017         });
7018         
7019         return rowIndex;
7020     },
7021      /**
7022      * Returns the grid's underlying element = used by panel.Grid
7023      * @return {Element} The element
7024      */
7025     getGridEl : function(){
7026         return this.el;
7027     },
7028      /**
7029      * Forces a resize - used by panel.Grid
7030      * @return {Element} The element
7031      */
7032     autoSize : function()
7033     {
7034         //var ctr = Roo.get(this.container.dom.parentElement);
7035         var ctr = Roo.get(this.el.dom);
7036         
7037         var thd = this.getGridEl().select('thead',true).first();
7038         var tbd = this.getGridEl().select('tbody', true).first();
7039         var tfd = this.getGridEl().select('tfoot', true).first();
7040         
7041         var cw = ctr.getWidth();
7042         
7043         if (tbd) {
7044             
7045             tbd.setSize(ctr.getWidth(),
7046                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7047             );
7048             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7049             cw -= barsize;
7050         }
7051         cw = Math.max(cw, this.totalWidth);
7052         this.getGridEl().select('tr',true).setWidth(cw);
7053         // resize 'expandable coloumn?
7054         
7055         return; // we doe not have a view in this design..
7056         
7057     },
7058     onBodyScroll: function()
7059     {
7060         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7061         if(this.mainHead){
7062             this.mainHead.setStyle({
7063                 'position' : 'relative',
7064                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7065             });
7066         }
7067         
7068         if(this.lazyLoad){
7069             
7070             var scrollHeight = this.mainBody.dom.scrollHeight;
7071             
7072             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7073             
7074             var height = this.mainBody.getHeight();
7075             
7076             if(scrollHeight - height == scrollTop) {
7077                 
7078                 var total = this.ds.getTotalCount();
7079                 
7080                 if(this.footer.cursor + this.footer.pageSize < total){
7081                     
7082                     this.footer.ds.load({
7083                         params : {
7084                             start : this.footer.cursor + this.footer.pageSize,
7085                             limit : this.footer.pageSize
7086                         },
7087                         add : true
7088                     });
7089                 }
7090             }
7091             
7092         }
7093     },
7094     
7095     onHeaderChange : function()
7096     {
7097         var header = this.renderHeader();
7098         var table = this.el.select('table', true).first();
7099         
7100         this.mainHead.remove();
7101         this.mainHead = table.createChild(header, this.mainBody, false);
7102     },
7103     
7104     onHiddenChange : function(colModel, colIndex, hidden)
7105     {
7106         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7107         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7108         
7109         this.CSS.updateRule(thSelector, "display", "");
7110         this.CSS.updateRule(tdSelector, "display", "");
7111         
7112         if(hidden){
7113             this.CSS.updateRule(thSelector, "display", "none");
7114             this.CSS.updateRule(tdSelector, "display", "none");
7115         }
7116         
7117         this.onHeaderChange();
7118         this.onLoad();
7119     },
7120     
7121     setColumnWidth: function(col_index, width)
7122     {
7123         // width = "md-2 xs-2..."
7124         if(!this.colModel.config[col_index]) {
7125             return;
7126         }
7127         
7128         var w = width.split(" ");
7129         
7130         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7131         
7132         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7133         
7134         
7135         for(var j = 0; j < w.length; j++) {
7136             
7137             if(!w[j]) {
7138                 continue;
7139             }
7140             
7141             var size_cls = w[j].split("-");
7142             
7143             if(!Number.isInteger(size_cls[1] * 1)) {
7144                 continue;
7145             }
7146             
7147             if(!this.colModel.config[col_index][size_cls[0]]) {
7148                 continue;
7149             }
7150             
7151             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7152                 continue;
7153             }
7154             
7155             h_row[0].classList.replace(
7156                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7157                 "col-"+size_cls[0]+"-"+size_cls[1]
7158             );
7159             
7160             for(var i = 0; i < rows.length; i++) {
7161                 
7162                 var size_cls = w[j].split("-");
7163                 
7164                 if(!Number.isInteger(size_cls[1] * 1)) {
7165                     continue;
7166                 }
7167                 
7168                 if(!this.colModel.config[col_index][size_cls[0]]) {
7169                     continue;
7170                 }
7171                 
7172                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7173                     continue;
7174                 }
7175                 
7176                 rows[i].classList.replace(
7177                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7178                     "col-"+size_cls[0]+"-"+size_cls[1]
7179                 );
7180             }
7181             
7182             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7183         }
7184     }
7185 });
7186
7187  
7188
7189  /*
7190  * - LGPL
7191  *
7192  * table cell
7193  * 
7194  */
7195
7196 /**
7197  * @class Roo.bootstrap.TableCell
7198  * @extends Roo.bootstrap.Component
7199  * Bootstrap TableCell class
7200  * @cfg {String} html cell contain text
7201  * @cfg {String} cls cell class
7202  * @cfg {String} tag cell tag (td|th) default td
7203  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7204  * @cfg {String} align Aligns the content in a cell
7205  * @cfg {String} axis Categorizes cells
7206  * @cfg {String} bgcolor Specifies the background color of a cell
7207  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7208  * @cfg {Number} colspan Specifies the number of columns a cell should span
7209  * @cfg {String} headers Specifies one or more header cells a cell is related to
7210  * @cfg {Number} height Sets the height of a cell
7211  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7212  * @cfg {Number} rowspan Sets the number of rows a cell should span
7213  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7214  * @cfg {String} valign Vertical aligns the content in a cell
7215  * @cfg {Number} width Specifies the width of a cell
7216  * 
7217  * @constructor
7218  * Create a new TableCell
7219  * @param {Object} config The config object
7220  */
7221
7222 Roo.bootstrap.TableCell = function(config){
7223     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7224 };
7225
7226 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7227     
7228     html: false,
7229     cls: false,
7230     tag: false,
7231     abbr: false,
7232     align: false,
7233     axis: false,
7234     bgcolor: false,
7235     charoff: false,
7236     colspan: false,
7237     headers: false,
7238     height: false,
7239     nowrap: false,
7240     rowspan: false,
7241     scope: false,
7242     valign: false,
7243     width: false,
7244     
7245     
7246     getAutoCreate : function(){
7247         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7248         
7249         cfg = {
7250             tag: 'td'
7251         };
7252         
7253         if(this.tag){
7254             cfg.tag = this.tag;
7255         }
7256         
7257         if (this.html) {
7258             cfg.html=this.html
7259         }
7260         if (this.cls) {
7261             cfg.cls=this.cls
7262         }
7263         if (this.abbr) {
7264             cfg.abbr=this.abbr
7265         }
7266         if (this.align) {
7267             cfg.align=this.align
7268         }
7269         if (this.axis) {
7270             cfg.axis=this.axis
7271         }
7272         if (this.bgcolor) {
7273             cfg.bgcolor=this.bgcolor
7274         }
7275         if (this.charoff) {
7276             cfg.charoff=this.charoff
7277         }
7278         if (this.colspan) {
7279             cfg.colspan=this.colspan
7280         }
7281         if (this.headers) {
7282             cfg.headers=this.headers
7283         }
7284         if (this.height) {
7285             cfg.height=this.height
7286         }
7287         if (this.nowrap) {
7288             cfg.nowrap=this.nowrap
7289         }
7290         if (this.rowspan) {
7291             cfg.rowspan=this.rowspan
7292         }
7293         if (this.scope) {
7294             cfg.scope=this.scope
7295         }
7296         if (this.valign) {
7297             cfg.valign=this.valign
7298         }
7299         if (this.width) {
7300             cfg.width=this.width
7301         }
7302         
7303         
7304         return cfg;
7305     }
7306    
7307 });
7308
7309  
7310
7311  /*
7312  * - LGPL
7313  *
7314  * table row
7315  * 
7316  */
7317
7318 /**
7319  * @class Roo.bootstrap.TableRow
7320  * @extends Roo.bootstrap.Component
7321  * Bootstrap TableRow class
7322  * @cfg {String} cls row class
7323  * @cfg {String} align Aligns the content in a table row
7324  * @cfg {String} bgcolor Specifies a background color for a table row
7325  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7326  * @cfg {String} valign Vertical aligns the content in a table row
7327  * 
7328  * @constructor
7329  * Create a new TableRow
7330  * @param {Object} config The config object
7331  */
7332
7333 Roo.bootstrap.TableRow = function(config){
7334     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7335 };
7336
7337 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7338     
7339     cls: false,
7340     align: false,
7341     bgcolor: false,
7342     charoff: false,
7343     valign: false,
7344     
7345     getAutoCreate : function(){
7346         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7347         
7348         cfg = {
7349             tag: 'tr'
7350         };
7351             
7352         if(this.cls){
7353             cfg.cls = this.cls;
7354         }
7355         if(this.align){
7356             cfg.align = this.align;
7357         }
7358         if(this.bgcolor){
7359             cfg.bgcolor = this.bgcolor;
7360         }
7361         if(this.charoff){
7362             cfg.charoff = this.charoff;
7363         }
7364         if(this.valign){
7365             cfg.valign = this.valign;
7366         }
7367         
7368         return cfg;
7369     }
7370    
7371 });
7372
7373  
7374
7375  /*
7376  * - LGPL
7377  *
7378  * table body
7379  * 
7380  */
7381
7382 /**
7383  * @class Roo.bootstrap.TableBody
7384  * @extends Roo.bootstrap.Component
7385  * Bootstrap TableBody class
7386  * @cfg {String} cls element class
7387  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7388  * @cfg {String} align Aligns the content inside the element
7389  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7390  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7391  * 
7392  * @constructor
7393  * Create a new TableBody
7394  * @param {Object} config The config object
7395  */
7396
7397 Roo.bootstrap.TableBody = function(config){
7398     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7399 };
7400
7401 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7402     
7403     cls: false,
7404     tag: false,
7405     align: false,
7406     charoff: false,
7407     valign: false,
7408     
7409     getAutoCreate : function(){
7410         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7411         
7412         cfg = {
7413             tag: 'tbody'
7414         };
7415             
7416         if (this.cls) {
7417             cfg.cls=this.cls
7418         }
7419         if(this.tag){
7420             cfg.tag = this.tag;
7421         }
7422         
7423         if(this.align){
7424             cfg.align = this.align;
7425         }
7426         if(this.charoff){
7427             cfg.charoff = this.charoff;
7428         }
7429         if(this.valign){
7430             cfg.valign = this.valign;
7431         }
7432         
7433         return cfg;
7434     }
7435     
7436     
7437 //    initEvents : function()
7438 //    {
7439 //        
7440 //        if(!this.store){
7441 //            return;
7442 //        }
7443 //        
7444 //        this.store = Roo.factory(this.store, Roo.data);
7445 //        this.store.on('load', this.onLoad, this);
7446 //        
7447 //        this.store.load();
7448 //        
7449 //    },
7450 //    
7451 //    onLoad: function () 
7452 //    {   
7453 //        this.fireEvent('load', this);
7454 //    }
7455 //    
7456 //   
7457 });
7458
7459  
7460
7461  /*
7462  * Based on:
7463  * Ext JS Library 1.1.1
7464  * Copyright(c) 2006-2007, Ext JS, LLC.
7465  *
7466  * Originally Released Under LGPL - original licence link has changed is not relivant.
7467  *
7468  * Fork - LGPL
7469  * <script type="text/javascript">
7470  */
7471
7472 // as we use this in bootstrap.
7473 Roo.namespace('Roo.form');
7474  /**
7475  * @class Roo.form.Action
7476  * Internal Class used to handle form actions
7477  * @constructor
7478  * @param {Roo.form.BasicForm} el The form element or its id
7479  * @param {Object} config Configuration options
7480  */
7481
7482  
7483  
7484 // define the action interface
7485 Roo.form.Action = function(form, options){
7486     this.form = form;
7487     this.options = options || {};
7488 };
7489 /**
7490  * Client Validation Failed
7491  * @const 
7492  */
7493 Roo.form.Action.CLIENT_INVALID = 'client';
7494 /**
7495  * Server Validation Failed
7496  * @const 
7497  */
7498 Roo.form.Action.SERVER_INVALID = 'server';
7499  /**
7500  * Connect to Server Failed
7501  * @const 
7502  */
7503 Roo.form.Action.CONNECT_FAILURE = 'connect';
7504 /**
7505  * Reading Data from Server Failed
7506  * @const 
7507  */
7508 Roo.form.Action.LOAD_FAILURE = 'load';
7509
7510 Roo.form.Action.prototype = {
7511     type : 'default',
7512     failureType : undefined,
7513     response : undefined,
7514     result : undefined,
7515
7516     // interface method
7517     run : function(options){
7518
7519     },
7520
7521     // interface method
7522     success : function(response){
7523
7524     },
7525
7526     // interface method
7527     handleResponse : function(response){
7528
7529     },
7530
7531     // default connection failure
7532     failure : function(response){
7533         
7534         this.response = response;
7535         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7536         this.form.afterAction(this, false);
7537     },
7538
7539     processResponse : function(response){
7540         this.response = response;
7541         if(!response.responseText){
7542             return true;
7543         }
7544         this.result = this.handleResponse(response);
7545         return this.result;
7546     },
7547
7548     // utility functions used internally
7549     getUrl : function(appendParams){
7550         var url = this.options.url || this.form.url || this.form.el.dom.action;
7551         if(appendParams){
7552             var p = this.getParams();
7553             if(p){
7554                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7555             }
7556         }
7557         return url;
7558     },
7559
7560     getMethod : function(){
7561         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7562     },
7563
7564     getParams : function(){
7565         var bp = this.form.baseParams;
7566         var p = this.options.params;
7567         if(p){
7568             if(typeof p == "object"){
7569                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7570             }else if(typeof p == 'string' && bp){
7571                 p += '&' + Roo.urlEncode(bp);
7572             }
7573         }else if(bp){
7574             p = Roo.urlEncode(bp);
7575         }
7576         return p;
7577     },
7578
7579     createCallback : function(){
7580         return {
7581             success: this.success,
7582             failure: this.failure,
7583             scope: this,
7584             timeout: (this.form.timeout*1000),
7585             upload: this.form.fileUpload ? this.success : undefined
7586         };
7587     }
7588 };
7589
7590 Roo.form.Action.Submit = function(form, options){
7591     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7592 };
7593
7594 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7595     type : 'submit',
7596
7597     haveProgress : false,
7598     uploadComplete : false,
7599     
7600     // uploadProgress indicator.
7601     uploadProgress : function()
7602     {
7603         if (!this.form.progressUrl) {
7604             return;
7605         }
7606         
7607         if (!this.haveProgress) {
7608             Roo.MessageBox.progress("Uploading", "Uploading");
7609         }
7610         if (this.uploadComplete) {
7611            Roo.MessageBox.hide();
7612            return;
7613         }
7614         
7615         this.haveProgress = true;
7616    
7617         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7618         
7619         var c = new Roo.data.Connection();
7620         c.request({
7621             url : this.form.progressUrl,
7622             params: {
7623                 id : uid
7624             },
7625             method: 'GET',
7626             success : function(req){
7627                //console.log(data);
7628                 var rdata = false;
7629                 var edata;
7630                 try  {
7631                    rdata = Roo.decode(req.responseText)
7632                 } catch (e) {
7633                     Roo.log("Invalid data from server..");
7634                     Roo.log(edata);
7635                     return;
7636                 }
7637                 if (!rdata || !rdata.success) {
7638                     Roo.log(rdata);
7639                     Roo.MessageBox.alert(Roo.encode(rdata));
7640                     return;
7641                 }
7642                 var data = rdata.data;
7643                 
7644                 if (this.uploadComplete) {
7645                    Roo.MessageBox.hide();
7646                    return;
7647                 }
7648                    
7649                 if (data){
7650                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7651                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7652                     );
7653                 }
7654                 this.uploadProgress.defer(2000,this);
7655             },
7656        
7657             failure: function(data) {
7658                 Roo.log('progress url failed ');
7659                 Roo.log(data);
7660             },
7661             scope : this
7662         });
7663            
7664     },
7665     
7666     
7667     run : function()
7668     {
7669         // run get Values on the form, so it syncs any secondary forms.
7670         this.form.getValues();
7671         
7672         var o = this.options;
7673         var method = this.getMethod();
7674         var isPost = method == 'POST';
7675         if(o.clientValidation === false || this.form.isValid()){
7676             
7677             if (this.form.progressUrl) {
7678                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7679                     (new Date() * 1) + '' + Math.random());
7680                     
7681             } 
7682             
7683             
7684             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7685                 form:this.form.el.dom,
7686                 url:this.getUrl(!isPost),
7687                 method: method,
7688                 params:isPost ? this.getParams() : null,
7689                 isUpload: this.form.fileUpload
7690             }));
7691             
7692             this.uploadProgress();
7693
7694         }else if (o.clientValidation !== false){ // client validation failed
7695             this.failureType = Roo.form.Action.CLIENT_INVALID;
7696             this.form.afterAction(this, false);
7697         }
7698     },
7699
7700     success : function(response)
7701     {
7702         this.uploadComplete= true;
7703         if (this.haveProgress) {
7704             Roo.MessageBox.hide();
7705         }
7706         
7707         
7708         var result = this.processResponse(response);
7709         if(result === true || result.success){
7710             this.form.afterAction(this, true);
7711             return;
7712         }
7713         if(result.errors){
7714             this.form.markInvalid(result.errors);
7715             this.failureType = Roo.form.Action.SERVER_INVALID;
7716         }
7717         this.form.afterAction(this, false);
7718     },
7719     failure : function(response)
7720     {
7721         this.uploadComplete= true;
7722         if (this.haveProgress) {
7723             Roo.MessageBox.hide();
7724         }
7725         
7726         this.response = response;
7727         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7728         this.form.afterAction(this, false);
7729     },
7730     
7731     handleResponse : function(response){
7732         if(this.form.errorReader){
7733             var rs = this.form.errorReader.read(response);
7734             var errors = [];
7735             if(rs.records){
7736                 for(var i = 0, len = rs.records.length; i < len; i++) {
7737                     var r = rs.records[i];
7738                     errors[i] = r.data;
7739                 }
7740             }
7741             if(errors.length < 1){
7742                 errors = null;
7743             }
7744             return {
7745                 success : rs.success,
7746                 errors : errors
7747             };
7748         }
7749         var ret = false;
7750         try {
7751             ret = Roo.decode(response.responseText);
7752         } catch (e) {
7753             ret = {
7754                 success: false,
7755                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7756                 errors : []
7757             };
7758         }
7759         return ret;
7760         
7761     }
7762 });
7763
7764
7765 Roo.form.Action.Load = function(form, options){
7766     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7767     this.reader = this.form.reader;
7768 };
7769
7770 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7771     type : 'load',
7772
7773     run : function(){
7774         
7775         Roo.Ajax.request(Roo.apply(
7776                 this.createCallback(), {
7777                     method:this.getMethod(),
7778                     url:this.getUrl(false),
7779                     params:this.getParams()
7780         }));
7781     },
7782
7783     success : function(response){
7784         
7785         var result = this.processResponse(response);
7786         if(result === true || !result.success || !result.data){
7787             this.failureType = Roo.form.Action.LOAD_FAILURE;
7788             this.form.afterAction(this, false);
7789             return;
7790         }
7791         this.form.clearInvalid();
7792         this.form.setValues(result.data);
7793         this.form.afterAction(this, true);
7794     },
7795
7796     handleResponse : function(response){
7797         if(this.form.reader){
7798             var rs = this.form.reader.read(response);
7799             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7800             return {
7801                 success : rs.success,
7802                 data : data
7803             };
7804         }
7805         return Roo.decode(response.responseText);
7806     }
7807 });
7808
7809 Roo.form.Action.ACTION_TYPES = {
7810     'load' : Roo.form.Action.Load,
7811     'submit' : Roo.form.Action.Submit
7812 };/*
7813  * - LGPL
7814  *
7815  * form
7816  *
7817  */
7818
7819 /**
7820  * @class Roo.bootstrap.Form
7821  * @extends Roo.bootstrap.Component
7822  * Bootstrap Form class
7823  * @cfg {String} method  GET | POST (default POST)
7824  * @cfg {String} labelAlign top | left (default top)
7825  * @cfg {String} align left  | right - for navbars
7826  * @cfg {Boolean} loadMask load mask when submit (default true)
7827
7828  *
7829  * @constructor
7830  * Create a new Form
7831  * @param {Object} config The config object
7832  */
7833
7834
7835 Roo.bootstrap.Form = function(config){
7836     
7837     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7838     
7839     Roo.bootstrap.Form.popover.apply();
7840     
7841     this.addEvents({
7842         /**
7843          * @event clientvalidation
7844          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7845          * @param {Form} this
7846          * @param {Boolean} valid true if the form has passed client-side validation
7847          */
7848         clientvalidation: true,
7849         /**
7850          * @event beforeaction
7851          * Fires before any action is performed. Return false to cancel the action.
7852          * @param {Form} this
7853          * @param {Action} action The action to be performed
7854          */
7855         beforeaction: true,
7856         /**
7857          * @event actionfailed
7858          * Fires when an action fails.
7859          * @param {Form} this
7860          * @param {Action} action The action that failed
7861          */
7862         actionfailed : true,
7863         /**
7864          * @event actioncomplete
7865          * Fires when an action is completed.
7866          * @param {Form} this
7867          * @param {Action} action The action that completed
7868          */
7869         actioncomplete : true
7870     });
7871 };
7872
7873 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7874
7875      /**
7876      * @cfg {String} method
7877      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7878      */
7879     method : 'POST',
7880     /**
7881      * @cfg {String} url
7882      * The URL to use for form actions if one isn't supplied in the action options.
7883      */
7884     /**
7885      * @cfg {Boolean} fileUpload
7886      * Set to true if this form is a file upload.
7887      */
7888
7889     /**
7890      * @cfg {Object} baseParams
7891      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7892      */
7893
7894     /**
7895      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7896      */
7897     timeout: 30,
7898     /**
7899      * @cfg {Sting} align (left|right) for navbar forms
7900      */
7901     align : 'left',
7902
7903     // private
7904     activeAction : null,
7905
7906     /**
7907      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7908      * element by passing it or its id or mask the form itself by passing in true.
7909      * @type Mixed
7910      */
7911     waitMsgTarget : false,
7912
7913     loadMask : true,
7914     
7915     /**
7916      * @cfg {Boolean} errorMask (true|false) default false
7917      */
7918     errorMask : false,
7919     
7920     /**
7921      * @cfg {Number} maskOffset Default 100
7922      */
7923     maskOffset : 100,
7924     
7925     /**
7926      * @cfg {Boolean} maskBody
7927      */
7928     maskBody : false,
7929
7930     getAutoCreate : function(){
7931
7932         var cfg = {
7933             tag: 'form',
7934             method : this.method || 'POST',
7935             id : this.id || Roo.id(),
7936             cls : ''
7937         };
7938         if (this.parent().xtype.match(/^Nav/)) {
7939             cfg.cls = 'navbar-form navbar-' + this.align;
7940
7941         }
7942
7943         if (this.labelAlign == 'left' ) {
7944             cfg.cls += ' form-horizontal';
7945         }
7946
7947
7948         return cfg;
7949     },
7950     initEvents : function()
7951     {
7952         this.el.on('submit', this.onSubmit, this);
7953         // this was added as random key presses on the form where triggering form submit.
7954         this.el.on('keypress', function(e) {
7955             if (e.getCharCode() != 13) {
7956                 return true;
7957             }
7958             // we might need to allow it for textareas.. and some other items.
7959             // check e.getTarget().
7960
7961             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7962                 return true;
7963             }
7964
7965             Roo.log("keypress blocked");
7966
7967             e.preventDefault();
7968             return false;
7969         });
7970         
7971     },
7972     // private
7973     onSubmit : function(e){
7974         e.stopEvent();
7975     },
7976
7977      /**
7978      * Returns true if client-side validation on the form is successful.
7979      * @return Boolean
7980      */
7981     isValid : function(){
7982         var items = this.getItems();
7983         var valid = true;
7984         var target = false;
7985         
7986         items.each(function(f){
7987             
7988             if(f.validate()){
7989                 return;
7990             }
7991             
7992             Roo.log('invalid field: ' + f.name);
7993             
7994             valid = false;
7995
7996             if(!target && f.el.isVisible(true)){
7997                 target = f;
7998             }
7999            
8000         });
8001         
8002         if(this.errorMask && !valid){
8003             Roo.bootstrap.Form.popover.mask(this, target);
8004         }
8005         
8006         return valid;
8007     },
8008     
8009     /**
8010      * Returns true if any fields in this form have changed since their original load.
8011      * @return Boolean
8012      */
8013     isDirty : function(){
8014         var dirty = false;
8015         var items = this.getItems();
8016         items.each(function(f){
8017            if(f.isDirty()){
8018                dirty = true;
8019                return false;
8020            }
8021            return true;
8022         });
8023         return dirty;
8024     },
8025      /**
8026      * Performs a predefined action (submit or load) or custom actions you define on this form.
8027      * @param {String} actionName The name of the action type
8028      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8029      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8030      * accept other config options):
8031      * <pre>
8032 Property          Type             Description
8033 ----------------  ---------------  ----------------------------------------------------------------------------------
8034 url               String           The url for the action (defaults to the form's url)
8035 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8036 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8037 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8038                                    validate the form on the client (defaults to false)
8039      * </pre>
8040      * @return {BasicForm} this
8041      */
8042     doAction : function(action, options){
8043         if(typeof action == 'string'){
8044             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8045         }
8046         if(this.fireEvent('beforeaction', this, action) !== false){
8047             this.beforeAction(action);
8048             action.run.defer(100, action);
8049         }
8050         return this;
8051     },
8052
8053     // private
8054     beforeAction : function(action){
8055         var o = action.options;
8056         
8057         if(this.loadMask){
8058             
8059             if(this.maskBody){
8060                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8061             } else {
8062                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8063             }
8064         }
8065         // not really supported yet.. ??
8066
8067         //if(this.waitMsgTarget === true){
8068         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8069         //}else if(this.waitMsgTarget){
8070         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8071         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8072         //}else {
8073         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8074        // }
8075
8076     },
8077
8078     // private
8079     afterAction : function(action, success){
8080         this.activeAction = null;
8081         var o = action.options;
8082
8083         if(this.loadMask){
8084             
8085             if(this.maskBody){
8086                 Roo.get(document.body).unmask();
8087             } else {
8088                 this.el.unmask();
8089             }
8090         }
8091         
8092         //if(this.waitMsgTarget === true){
8093 //            this.el.unmask();
8094         //}else if(this.waitMsgTarget){
8095         //    this.waitMsgTarget.unmask();
8096         //}else{
8097         //    Roo.MessageBox.updateProgress(1);
8098         //    Roo.MessageBox.hide();
8099        // }
8100         //
8101         if(success){
8102             if(o.reset){
8103                 this.reset();
8104             }
8105             Roo.callback(o.success, o.scope, [this, action]);
8106             this.fireEvent('actioncomplete', this, action);
8107
8108         }else{
8109
8110             // failure condition..
8111             // we have a scenario where updates need confirming.
8112             // eg. if a locking scenario exists..
8113             // we look for { errors : { needs_confirm : true }} in the response.
8114             if (
8115                 (typeof(action.result) != 'undefined')  &&
8116                 (typeof(action.result.errors) != 'undefined')  &&
8117                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8118            ){
8119                 var _t = this;
8120                 Roo.log("not supported yet");
8121                  /*
8122
8123                 Roo.MessageBox.confirm(
8124                     "Change requires confirmation",
8125                     action.result.errorMsg,
8126                     function(r) {
8127                         if (r != 'yes') {
8128                             return;
8129                         }
8130                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8131                     }
8132
8133                 );
8134                 */
8135
8136
8137                 return;
8138             }
8139
8140             Roo.callback(o.failure, o.scope, [this, action]);
8141             // show an error message if no failed handler is set..
8142             if (!this.hasListener('actionfailed')) {
8143                 Roo.log("need to add dialog support");
8144                 /*
8145                 Roo.MessageBox.alert("Error",
8146                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8147                         action.result.errorMsg :
8148                         "Saving Failed, please check your entries or try again"
8149                 );
8150                 */
8151             }
8152
8153             this.fireEvent('actionfailed', this, action);
8154         }
8155
8156     },
8157     /**
8158      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8159      * @param {String} id The value to search for
8160      * @return Field
8161      */
8162     findField : function(id){
8163         var items = this.getItems();
8164         var field = items.get(id);
8165         if(!field){
8166              items.each(function(f){
8167                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8168                     field = f;
8169                     return false;
8170                 }
8171                 return true;
8172             });
8173         }
8174         return field || null;
8175     },
8176      /**
8177      * Mark fields in this form invalid in bulk.
8178      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8179      * @return {BasicForm} this
8180      */
8181     markInvalid : function(errors){
8182         if(errors instanceof Array){
8183             for(var i = 0, len = errors.length; i < len; i++){
8184                 var fieldError = errors[i];
8185                 var f = this.findField(fieldError.id);
8186                 if(f){
8187                     f.markInvalid(fieldError.msg);
8188                 }
8189             }
8190         }else{
8191             var field, id;
8192             for(id in errors){
8193                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8194                     field.markInvalid(errors[id]);
8195                 }
8196             }
8197         }
8198         //Roo.each(this.childForms || [], function (f) {
8199         //    f.markInvalid(errors);
8200         //});
8201
8202         return this;
8203     },
8204
8205     /**
8206      * Set values for fields in this form in bulk.
8207      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8208      * @return {BasicForm} this
8209      */
8210     setValues : function(values){
8211         if(values instanceof Array){ // array of objects
8212             for(var i = 0, len = values.length; i < len; i++){
8213                 var v = values[i];
8214                 var f = this.findField(v.id);
8215                 if(f){
8216                     f.setValue(v.value);
8217                     if(this.trackResetOnLoad){
8218                         f.originalValue = f.getValue();
8219                     }
8220                 }
8221             }
8222         }else{ // object hash
8223             var field, id;
8224             for(id in values){
8225                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8226
8227                     if (field.setFromData &&
8228                         field.valueField &&
8229                         field.displayField &&
8230                         // combos' with local stores can
8231                         // be queried via setValue()
8232                         // to set their value..
8233                         (field.store && !field.store.isLocal)
8234                         ) {
8235                         // it's a combo
8236                         var sd = { };
8237                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8238                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8239                         field.setFromData(sd);
8240
8241                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8242                         
8243                         field.setFromData(values);
8244                         
8245                     } else {
8246                         field.setValue(values[id]);
8247                     }
8248
8249
8250                     if(this.trackResetOnLoad){
8251                         field.originalValue = field.getValue();
8252                     }
8253                 }
8254             }
8255         }
8256
8257         //Roo.each(this.childForms || [], function (f) {
8258         //    f.setValues(values);
8259         //});
8260
8261         return this;
8262     },
8263
8264     /**
8265      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8266      * they are returned as an array.
8267      * @param {Boolean} asString
8268      * @return {Object}
8269      */
8270     getValues : function(asString){
8271         //if (this.childForms) {
8272             // copy values from the child forms
8273         //    Roo.each(this.childForms, function (f) {
8274         //        this.setValues(f.getValues());
8275         //    }, this);
8276         //}
8277
8278
8279
8280         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8281         if(asString === true){
8282             return fs;
8283         }
8284         return Roo.urlDecode(fs);
8285     },
8286
8287     /**
8288      * Returns the fields in this form as an object with key/value pairs.
8289      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8290      * @return {Object}
8291      */
8292     getFieldValues : function(with_hidden)
8293     {
8294         var items = this.getItems();
8295         var ret = {};
8296         items.each(function(f){
8297             
8298             if (!f.getName()) {
8299                 return;
8300             }
8301             
8302             var v = f.getValue();
8303             
8304             if (f.inputType =='radio') {
8305                 if (typeof(ret[f.getName()]) == 'undefined') {
8306                     ret[f.getName()] = ''; // empty..
8307                 }
8308
8309                 if (!f.el.dom.checked) {
8310                     return;
8311
8312                 }
8313                 v = f.el.dom.value;
8314
8315             }
8316             
8317             if(f.xtype == 'MoneyField'){
8318                 ret[f.currencyName] = f.getCurrency();
8319             }
8320
8321             // not sure if this supported any more..
8322             if ((typeof(v) == 'object') && f.getRawValue) {
8323                 v = f.getRawValue() ; // dates..
8324             }
8325             // combo boxes where name != hiddenName...
8326             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8327                 ret[f.name] = f.getRawValue();
8328             }
8329             ret[f.getName()] = v;
8330         });
8331
8332         return ret;
8333     },
8334
8335     /**
8336      * Clears all invalid messages in this form.
8337      * @return {BasicForm} this
8338      */
8339     clearInvalid : function(){
8340         var items = this.getItems();
8341
8342         items.each(function(f){
8343            f.clearInvalid();
8344         });
8345
8346         return this;
8347     },
8348
8349     /**
8350      * Resets this form.
8351      * @return {BasicForm} this
8352      */
8353     reset : function(){
8354         var items = this.getItems();
8355         items.each(function(f){
8356             f.reset();
8357         });
8358
8359         Roo.each(this.childForms || [], function (f) {
8360             f.reset();
8361         });
8362
8363
8364         return this;
8365     },
8366     
8367     getItems : function()
8368     {
8369         var r=new Roo.util.MixedCollection(false, function(o){
8370             return o.id || (o.id = Roo.id());
8371         });
8372         var iter = function(el) {
8373             if (el.inputEl) {
8374                 r.add(el);
8375             }
8376             if (!el.items) {
8377                 return;
8378             }
8379             Roo.each(el.items,function(e) {
8380                 iter(e);
8381             });
8382         };
8383
8384         iter(this);
8385         return r;
8386     },
8387     
8388     hideFields : function(items)
8389     {
8390         Roo.each(items, function(i){
8391             
8392             var f = this.findField(i);
8393             
8394             if(!f){
8395                 return;
8396             }
8397             
8398             f.hide();
8399             
8400         }, this);
8401     },
8402     
8403     showFields : function(items)
8404     {
8405         Roo.each(items, function(i){
8406             
8407             var f = this.findField(i);
8408             
8409             if(!f){
8410                 return;
8411             }
8412             
8413             f.show();
8414             
8415         }, this);
8416     }
8417
8418 });
8419
8420 Roo.apply(Roo.bootstrap.Form, {
8421     
8422     popover : {
8423         
8424         padding : 5,
8425         
8426         isApplied : false,
8427         
8428         isMasked : false,
8429         
8430         form : false,
8431         
8432         target : false,
8433         
8434         toolTip : false,
8435         
8436         intervalID : false,
8437         
8438         maskEl : false,
8439         
8440         apply : function()
8441         {
8442             if(this.isApplied){
8443                 return;
8444             }
8445             
8446             this.maskEl = {
8447                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8448                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8449                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8450                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8451             };
8452             
8453             this.maskEl.top.enableDisplayMode("block");
8454             this.maskEl.left.enableDisplayMode("block");
8455             this.maskEl.bottom.enableDisplayMode("block");
8456             this.maskEl.right.enableDisplayMode("block");
8457             
8458             this.toolTip = new Roo.bootstrap.Tooltip({
8459                 cls : 'roo-form-error-popover',
8460                 alignment : {
8461                     'left' : ['r-l', [-2,0], 'right'],
8462                     'right' : ['l-r', [2,0], 'left'],
8463                     'bottom' : ['tl-bl', [0,2], 'top'],
8464                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8465                 }
8466             });
8467             
8468             this.toolTip.render(Roo.get(document.body));
8469
8470             this.toolTip.el.enableDisplayMode("block");
8471             
8472             Roo.get(document.body).on('click', function(){
8473                 this.unmask();
8474             }, this);
8475             
8476             Roo.get(document.body).on('touchstart', function(){
8477                 this.unmask();
8478             }, this);
8479             
8480             this.isApplied = true
8481         },
8482         
8483         mask : function(form, target)
8484         {
8485             this.form = form;
8486             
8487             this.target = target;
8488             
8489             if(!this.form.errorMask || !target.el){
8490                 return;
8491             }
8492             
8493             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8494             
8495             Roo.log(scrollable);
8496             
8497             var ot = this.target.el.calcOffsetsTo(scrollable);
8498             
8499             var scrollTo = ot[1] - this.form.maskOffset;
8500             
8501             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8502             
8503             scrollable.scrollTo('top', scrollTo);
8504             
8505             var box = this.target.el.getBox();
8506             Roo.log(box);
8507             var zIndex = Roo.bootstrap.Modal.zIndex++;
8508
8509             
8510             this.maskEl.top.setStyle('position', 'absolute');
8511             this.maskEl.top.setStyle('z-index', zIndex);
8512             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8513             this.maskEl.top.setLeft(0);
8514             this.maskEl.top.setTop(0);
8515             this.maskEl.top.show();
8516             
8517             this.maskEl.left.setStyle('position', 'absolute');
8518             this.maskEl.left.setStyle('z-index', zIndex);
8519             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8520             this.maskEl.left.setLeft(0);
8521             this.maskEl.left.setTop(box.y - this.padding);
8522             this.maskEl.left.show();
8523
8524             this.maskEl.bottom.setStyle('position', 'absolute');
8525             this.maskEl.bottom.setStyle('z-index', zIndex);
8526             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8527             this.maskEl.bottom.setLeft(0);
8528             this.maskEl.bottom.setTop(box.bottom + this.padding);
8529             this.maskEl.bottom.show();
8530
8531             this.maskEl.right.setStyle('position', 'absolute');
8532             this.maskEl.right.setStyle('z-index', zIndex);
8533             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8534             this.maskEl.right.setLeft(box.right + this.padding);
8535             this.maskEl.right.setTop(box.y - this.padding);
8536             this.maskEl.right.show();
8537
8538             this.toolTip.bindEl = this.target.el;
8539
8540             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8541
8542             var tip = this.target.blankText;
8543
8544             if(this.target.getValue() !== '' ) {
8545                 
8546                 if (this.target.invalidText.length) {
8547                     tip = this.target.invalidText;
8548                 } else if (this.target.regexText.length){
8549                     tip = this.target.regexText;
8550                 }
8551             }
8552
8553             this.toolTip.show(tip);
8554
8555             this.intervalID = window.setInterval(function() {
8556                 Roo.bootstrap.Form.popover.unmask();
8557             }, 10000);
8558
8559             window.onwheel = function(){ return false;};
8560             
8561             (function(){ this.isMasked = true; }).defer(500, this);
8562             
8563         },
8564         
8565         unmask : function()
8566         {
8567             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8568                 return;
8569             }
8570             
8571             this.maskEl.top.setStyle('position', 'absolute');
8572             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8573             this.maskEl.top.hide();
8574
8575             this.maskEl.left.setStyle('position', 'absolute');
8576             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8577             this.maskEl.left.hide();
8578
8579             this.maskEl.bottom.setStyle('position', 'absolute');
8580             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8581             this.maskEl.bottom.hide();
8582
8583             this.maskEl.right.setStyle('position', 'absolute');
8584             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8585             this.maskEl.right.hide();
8586             
8587             this.toolTip.hide();
8588             
8589             this.toolTip.el.hide();
8590             
8591             window.onwheel = function(){ return true;};
8592             
8593             if(this.intervalID){
8594                 window.clearInterval(this.intervalID);
8595                 this.intervalID = false;
8596             }
8597             
8598             this.isMasked = false;
8599             
8600         }
8601         
8602     }
8603     
8604 });
8605
8606 /*
8607  * Based on:
8608  * Ext JS Library 1.1.1
8609  * Copyright(c) 2006-2007, Ext JS, LLC.
8610  *
8611  * Originally Released Under LGPL - original licence link has changed is not relivant.
8612  *
8613  * Fork - LGPL
8614  * <script type="text/javascript">
8615  */
8616 /**
8617  * @class Roo.form.VTypes
8618  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8619  * @singleton
8620  */
8621 Roo.form.VTypes = function(){
8622     // closure these in so they are only created once.
8623     var alpha = /^[a-zA-Z_]+$/;
8624     var alphanum = /^[a-zA-Z0-9_]+$/;
8625     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8626     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8627
8628     // All these messages and functions are configurable
8629     return {
8630         /**
8631          * The function used to validate email addresses
8632          * @param {String} value The email address
8633          */
8634         'email' : function(v){
8635             return email.test(v);
8636         },
8637         /**
8638          * The error text to display when the email validation function returns false
8639          * @type String
8640          */
8641         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8642         /**
8643          * The keystroke filter mask to be applied on email input
8644          * @type RegExp
8645          */
8646         'emailMask' : /[a-z0-9_\.\-@]/i,
8647
8648         /**
8649          * The function used to validate URLs
8650          * @param {String} value The URL
8651          */
8652         'url' : function(v){
8653             return url.test(v);
8654         },
8655         /**
8656          * The error text to display when the url validation function returns false
8657          * @type String
8658          */
8659         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8660         
8661         /**
8662          * The function used to validate alpha values
8663          * @param {String} value The value
8664          */
8665         'alpha' : function(v){
8666             return alpha.test(v);
8667         },
8668         /**
8669          * The error text to display when the alpha validation function returns false
8670          * @type String
8671          */
8672         'alphaText' : 'This field should only contain letters and _',
8673         /**
8674          * The keystroke filter mask to be applied on alpha input
8675          * @type RegExp
8676          */
8677         'alphaMask' : /[a-z_]/i,
8678
8679         /**
8680          * The function used to validate alphanumeric values
8681          * @param {String} value The value
8682          */
8683         'alphanum' : function(v){
8684             return alphanum.test(v);
8685         },
8686         /**
8687          * The error text to display when the alphanumeric validation function returns false
8688          * @type String
8689          */
8690         'alphanumText' : 'This field should only contain letters, numbers and _',
8691         /**
8692          * The keystroke filter mask to be applied on alphanumeric input
8693          * @type RegExp
8694          */
8695         'alphanumMask' : /[a-z0-9_]/i
8696     };
8697 }();/*
8698  * - LGPL
8699  *
8700  * Input
8701  * 
8702  */
8703
8704 /**
8705  * @class Roo.bootstrap.Input
8706  * @extends Roo.bootstrap.Component
8707  * Bootstrap Input class
8708  * @cfg {Boolean} disabled is it disabled
8709  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8710  * @cfg {String} name name of the input
8711  * @cfg {string} fieldLabel - the label associated
8712  * @cfg {string} placeholder - placeholder to put in text.
8713  * @cfg {string}  before - input group add on before
8714  * @cfg {string} after - input group add on after
8715  * @cfg {string} size - (lg|sm) or leave empty..
8716  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8717  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8718  * @cfg {Number} md colspan out of 12 for computer-sized screens
8719  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8720  * @cfg {string} value default value of the input
8721  * @cfg {Number} labelWidth set the width of label 
8722  * @cfg {Number} labellg set the width of label (1-12)
8723  * @cfg {Number} labelmd set the width of label (1-12)
8724  * @cfg {Number} labelsm set the width of label (1-12)
8725  * @cfg {Number} labelxs set the width of label (1-12)
8726  * @cfg {String} labelAlign (top|left)
8727  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8728  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8729  * @cfg {String} indicatorpos (left|right) default left
8730  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8731  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8732
8733  * @cfg {String} align (left|center|right) Default left
8734  * @cfg {Boolean} forceFeedback (true|false) Default false
8735  * 
8736  * @constructor
8737  * Create a new Input
8738  * @param {Object} config The config object
8739  */
8740
8741 Roo.bootstrap.Input = function(config){
8742     
8743     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8744     
8745     this.addEvents({
8746         /**
8747          * @event focus
8748          * Fires when this field receives input focus.
8749          * @param {Roo.form.Field} this
8750          */
8751         focus : true,
8752         /**
8753          * @event blur
8754          * Fires when this field loses input focus.
8755          * @param {Roo.form.Field} this
8756          */
8757         blur : true,
8758         /**
8759          * @event specialkey
8760          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8761          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8762          * @param {Roo.form.Field} this
8763          * @param {Roo.EventObject} e The event object
8764          */
8765         specialkey : true,
8766         /**
8767          * @event change
8768          * Fires just before the field blurs if the field value has changed.
8769          * @param {Roo.form.Field} this
8770          * @param {Mixed} newValue The new value
8771          * @param {Mixed} oldValue The original value
8772          */
8773         change : true,
8774         /**
8775          * @event invalid
8776          * Fires after the field has been marked as invalid.
8777          * @param {Roo.form.Field} this
8778          * @param {String} msg The validation message
8779          */
8780         invalid : true,
8781         /**
8782          * @event valid
8783          * Fires after the field has been validated with no errors.
8784          * @param {Roo.form.Field} this
8785          */
8786         valid : true,
8787          /**
8788          * @event keyup
8789          * Fires after the key up
8790          * @param {Roo.form.Field} this
8791          * @param {Roo.EventObject}  e The event Object
8792          */
8793         keyup : true
8794     });
8795 };
8796
8797 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8798      /**
8799      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8800       automatic validation (defaults to "keyup").
8801      */
8802     validationEvent : "keyup",
8803      /**
8804      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8805      */
8806     validateOnBlur : true,
8807     /**
8808      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8809      */
8810     validationDelay : 250,
8811      /**
8812      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8813      */
8814     focusClass : "x-form-focus",  // not needed???
8815     
8816        
8817     /**
8818      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8819      */
8820     invalidClass : "has-warning",
8821     
8822     /**
8823      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8824      */
8825     validClass : "has-success",
8826     
8827     /**
8828      * @cfg {Boolean} hasFeedback (true|false) default true
8829      */
8830     hasFeedback : true,
8831     
8832     /**
8833      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8834      */
8835     invalidFeedbackClass : "glyphicon-warning-sign",
8836     
8837     /**
8838      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8839      */
8840     validFeedbackClass : "glyphicon-ok",
8841     
8842     /**
8843      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8844      */
8845     selectOnFocus : false,
8846     
8847      /**
8848      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8849      */
8850     maskRe : null,
8851        /**
8852      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8853      */
8854     vtype : null,
8855     
8856       /**
8857      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8858      */
8859     disableKeyFilter : false,
8860     
8861        /**
8862      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8863      */
8864     disabled : false,
8865      /**
8866      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8867      */
8868     allowBlank : true,
8869     /**
8870      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8871      */
8872     blankText : "Please complete this mandatory field",
8873     
8874      /**
8875      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8876      */
8877     minLength : 0,
8878     /**
8879      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8880      */
8881     maxLength : Number.MAX_VALUE,
8882     /**
8883      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8884      */
8885     minLengthText : "The minimum length for this field is {0}",
8886     /**
8887      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8888      */
8889     maxLengthText : "The maximum length for this field is {0}",
8890   
8891     
8892     /**
8893      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8894      * If available, this function will be called only after the basic validators all return true, and will be passed the
8895      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8896      */
8897     validator : null,
8898     /**
8899      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8900      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8901      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8902      */
8903     regex : null,
8904     /**
8905      * @cfg {String} regexText -- Depricated - use Invalid Text
8906      */
8907     regexText : "",
8908     
8909     /**
8910      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8911      */
8912     invalidText : "",
8913     
8914     
8915     
8916     autocomplete: false,
8917     
8918     
8919     fieldLabel : '',
8920     inputType : 'text',
8921     
8922     name : false,
8923     placeholder: false,
8924     before : false,
8925     after : false,
8926     size : false,
8927     hasFocus : false,
8928     preventMark: false,
8929     isFormField : true,
8930     value : '',
8931     labelWidth : 2,
8932     labelAlign : false,
8933     readOnly : false,
8934     align : false,
8935     formatedValue : false,
8936     forceFeedback : false,
8937     
8938     indicatorpos : 'left',
8939     
8940     labellg : 0,
8941     labelmd : 0,
8942     labelsm : 0,
8943     labelxs : 0,
8944     
8945     capture : '',
8946     accept : '',
8947     
8948     parentLabelAlign : function()
8949     {
8950         var parent = this;
8951         while (parent.parent()) {
8952             parent = parent.parent();
8953             if (typeof(parent.labelAlign) !='undefined') {
8954                 return parent.labelAlign;
8955             }
8956         }
8957         return 'left';
8958         
8959     },
8960     
8961     getAutoCreate : function()
8962     {
8963         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8964         
8965         var id = Roo.id();
8966         
8967         var cfg = {};
8968         
8969         if(this.inputType != 'hidden'){
8970             cfg.cls = 'form-group' //input-group
8971         }
8972         
8973         var input =  {
8974             tag: 'input',
8975             id : id,
8976             type : this.inputType,
8977             value : this.value,
8978             cls : 'form-control',
8979             placeholder : this.placeholder || '',
8980             autocomplete : this.autocomplete || 'new-password'
8981         };
8982         
8983         if(this.capture.length){
8984             input.capture = this.capture;
8985         }
8986         
8987         if(this.accept.length){
8988             input.accept = this.accept + "/*";
8989         }
8990         
8991         if(this.align){
8992             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8993         }
8994         
8995         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8996             input.maxLength = this.maxLength;
8997         }
8998         
8999         if (this.disabled) {
9000             input.disabled=true;
9001         }
9002         
9003         if (this.readOnly) {
9004             input.readonly=true;
9005         }
9006         
9007         if (this.name) {
9008             input.name = this.name;
9009         }
9010         
9011         if (this.size) {
9012             input.cls += ' input-' + this.size;
9013         }
9014         
9015         var settings=this;
9016         ['xs','sm','md','lg'].map(function(size){
9017             if (settings[size]) {
9018                 cfg.cls += ' col-' + size + '-' + settings[size];
9019             }
9020         });
9021         
9022         var inputblock = input;
9023         
9024         var feedback = {
9025             tag: 'span',
9026             cls: 'glyphicon form-control-feedback'
9027         };
9028             
9029         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9030             
9031             inputblock = {
9032                 cls : 'has-feedback',
9033                 cn :  [
9034                     input,
9035                     feedback
9036                 ] 
9037             };  
9038         }
9039         
9040         if (this.before || this.after) {
9041             
9042             inputblock = {
9043                 cls : 'input-group',
9044                 cn :  [] 
9045             };
9046             
9047             if (this.before && typeof(this.before) == 'string') {
9048                 
9049                 inputblock.cn.push({
9050                     tag :'span',
9051                     cls : 'roo-input-before input-group-addon',
9052                     html : this.before
9053                 });
9054             }
9055             if (this.before && typeof(this.before) == 'object') {
9056                 this.before = Roo.factory(this.before);
9057                 
9058                 inputblock.cn.push({
9059                     tag :'span',
9060                     cls : 'roo-input-before input-group-' +
9061                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9062                 });
9063             }
9064             
9065             inputblock.cn.push(input);
9066             
9067             if (this.after && typeof(this.after) == 'string') {
9068                 inputblock.cn.push({
9069                     tag :'span',
9070                     cls : 'roo-input-after input-group-addon',
9071                     html : this.after
9072                 });
9073             }
9074             if (this.after && typeof(this.after) == 'object') {
9075                 this.after = Roo.factory(this.after);
9076                 
9077                 inputblock.cn.push({
9078                     tag :'span',
9079                     cls : 'roo-input-after input-group-' +
9080                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9081                 });
9082             }
9083             
9084             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9085                 inputblock.cls += ' has-feedback';
9086                 inputblock.cn.push(feedback);
9087             }
9088         };
9089         
9090         if (align ==='left' && this.fieldLabel.length) {
9091             
9092             cfg.cls += ' roo-form-group-label-left';
9093             
9094             cfg.cn = [
9095                 {
9096                     tag : 'i',
9097                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9098                     tooltip : 'This field is required'
9099                 },
9100                 {
9101                     tag: 'label',
9102                     'for' :  id,
9103                     cls : 'control-label',
9104                     html : this.fieldLabel
9105
9106                 },
9107                 {
9108                     cls : "", 
9109                     cn: [
9110                         inputblock
9111                     ]
9112                 }
9113             ];
9114             
9115             var labelCfg = cfg.cn[1];
9116             var contentCfg = cfg.cn[2];
9117             
9118             if(this.indicatorpos == 'right'){
9119                 cfg.cn = [
9120                     {
9121                         tag: 'label',
9122                         'for' :  id,
9123                         cls : 'control-label',
9124                         cn : [
9125                             {
9126                                 tag : 'span',
9127                                 html : this.fieldLabel
9128                             },
9129                             {
9130                                 tag : 'i',
9131                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9132                                 tooltip : 'This field is required'
9133                             }
9134                         ]
9135                     },
9136                     {
9137                         cls : "",
9138                         cn: [
9139                             inputblock
9140                         ]
9141                     }
9142
9143                 ];
9144                 
9145                 labelCfg = cfg.cn[0];
9146                 contentCfg = cfg.cn[1];
9147             
9148             }
9149             
9150             if(this.labelWidth > 12){
9151                 labelCfg.style = "width: " + this.labelWidth + 'px';
9152             }
9153             
9154             if(this.labelWidth < 13 && this.labelmd == 0){
9155                 this.labelmd = this.labelWidth;
9156             }
9157             
9158             if(this.labellg > 0){
9159                 labelCfg.cls += ' col-lg-' + this.labellg;
9160                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9161             }
9162             
9163             if(this.labelmd > 0){
9164                 labelCfg.cls += ' col-md-' + this.labelmd;
9165                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9166             }
9167             
9168             if(this.labelsm > 0){
9169                 labelCfg.cls += ' col-sm-' + this.labelsm;
9170                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9171             }
9172             
9173             if(this.labelxs > 0){
9174                 labelCfg.cls += ' col-xs-' + this.labelxs;
9175                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9176             }
9177             
9178             
9179         } else if ( this.fieldLabel.length) {
9180                 
9181             cfg.cn = [
9182                 {
9183                     tag : 'i',
9184                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9185                     tooltip : 'This field is required'
9186                 },
9187                 {
9188                     tag: 'label',
9189                    //cls : 'input-group-addon',
9190                     html : this.fieldLabel
9191
9192                 },
9193
9194                inputblock
9195
9196            ];
9197            
9198            if(this.indicatorpos == 'right'){
9199                 
9200                 cfg.cn = [
9201                     {
9202                         tag: 'label',
9203                        //cls : 'input-group-addon',
9204                         html : this.fieldLabel
9205
9206                     },
9207                     {
9208                         tag : 'i',
9209                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9210                         tooltip : 'This field is required'
9211                     },
9212
9213                    inputblock
9214
9215                ];
9216
9217             }
9218
9219         } else {
9220             
9221             cfg.cn = [
9222
9223                     inputblock
9224
9225             ];
9226                 
9227                 
9228         };
9229         
9230         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9231            cfg.cls += ' navbar-form';
9232         }
9233         
9234         if (this.parentType === 'NavGroup') {
9235            cfg.cls += ' navbar-form';
9236            cfg.tag = 'li';
9237         }
9238         
9239         return cfg;
9240         
9241     },
9242     /**
9243      * return the real input element.
9244      */
9245     inputEl: function ()
9246     {
9247         return this.el.select('input.form-control',true).first();
9248     },
9249     
9250     tooltipEl : function()
9251     {
9252         return this.inputEl();
9253     },
9254     
9255     indicatorEl : function()
9256     {
9257         var indicator = this.el.select('i.roo-required-indicator',true).first();
9258         
9259         if(!indicator){
9260             return false;
9261         }
9262         
9263         return indicator;
9264         
9265     },
9266     
9267     setDisabled : function(v)
9268     {
9269         var i  = this.inputEl().dom;
9270         if (!v) {
9271             i.removeAttribute('disabled');
9272             return;
9273             
9274         }
9275         i.setAttribute('disabled','true');
9276     },
9277     initEvents : function()
9278     {
9279           
9280         this.inputEl().on("keydown" , this.fireKey,  this);
9281         this.inputEl().on("focus", this.onFocus,  this);
9282         this.inputEl().on("blur", this.onBlur,  this);
9283         
9284         this.inputEl().relayEvent('keyup', this);
9285         
9286         this.indicator = this.indicatorEl();
9287         
9288         if(this.indicator){
9289             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9290         }
9291  
9292         // reference to original value for reset
9293         this.originalValue = this.getValue();
9294         //Roo.form.TextField.superclass.initEvents.call(this);
9295         if(this.validationEvent == 'keyup'){
9296             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9297             this.inputEl().on('keyup', this.filterValidation, this);
9298         }
9299         else if(this.validationEvent !== false){
9300             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9301         }
9302         
9303         if(this.selectOnFocus){
9304             this.on("focus", this.preFocus, this);
9305             
9306         }
9307         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9308             this.inputEl().on("keypress", this.filterKeys, this);
9309         } else {
9310             this.inputEl().relayEvent('keypress', this);
9311         }
9312        /* if(this.grow){
9313             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9314             this.el.on("click", this.autoSize,  this);
9315         }
9316         */
9317         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9318             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9319         }
9320         
9321         if (typeof(this.before) == 'object') {
9322             this.before.render(this.el.select('.roo-input-before',true).first());
9323         }
9324         if (typeof(this.after) == 'object') {
9325             this.after.render(this.el.select('.roo-input-after',true).first());
9326         }
9327         
9328         this.inputEl().on('change', this.onChange, this);
9329         
9330     },
9331     filterValidation : function(e){
9332         if(!e.isNavKeyPress()){
9333             this.validationTask.delay(this.validationDelay);
9334         }
9335     },
9336      /**
9337      * Validates the field value
9338      * @return {Boolean} True if the value is valid, else false
9339      */
9340     validate : function(){
9341         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9342         if(this.disabled || this.validateValue(this.getRawValue())){
9343             this.markValid();
9344             return true;
9345         }
9346         
9347         this.markInvalid();
9348         return false;
9349     },
9350     
9351     
9352     /**
9353      * Validates a value according to the field's validation rules and marks the field as invalid
9354      * if the validation fails
9355      * @param {Mixed} value The value to validate
9356      * @return {Boolean} True if the value is valid, else false
9357      */
9358     validateValue : function(value)
9359     {
9360         if(this.getVisibilityEl().hasClass('hidden')){
9361             return true;
9362         }
9363         
9364         if(value.length < 1)  { // if it's blank
9365             if(this.allowBlank){
9366                 return true;
9367             }
9368             return false;
9369         }
9370         
9371         if(value.length < this.minLength){
9372             return false;
9373         }
9374         if(value.length > this.maxLength){
9375             return false;
9376         }
9377         if(this.vtype){
9378             var vt = Roo.form.VTypes;
9379             if(!vt[this.vtype](value, this)){
9380                 return false;
9381             }
9382         }
9383         if(typeof this.validator == "function"){
9384             var msg = this.validator(value);
9385             if(msg !== true){
9386                 return false;
9387             }
9388             if (typeof(msg) == 'string') {
9389                 this.invalidText = msg;
9390             }
9391         }
9392         
9393         if(this.regex && !this.regex.test(value)){
9394             return false;
9395         }
9396         
9397         return true;
9398     },
9399     
9400      // private
9401     fireKey : function(e){
9402         //Roo.log('field ' + e.getKey());
9403         if(e.isNavKeyPress()){
9404             this.fireEvent("specialkey", this, e);
9405         }
9406     },
9407     focus : function (selectText){
9408         if(this.rendered){
9409             this.inputEl().focus();
9410             if(selectText === true){
9411                 this.inputEl().dom.select();
9412             }
9413         }
9414         return this;
9415     } ,
9416     
9417     onFocus : function(){
9418         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9419            // this.el.addClass(this.focusClass);
9420         }
9421         if(!this.hasFocus){
9422             this.hasFocus = true;
9423             this.startValue = this.getValue();
9424             this.fireEvent("focus", this);
9425         }
9426     },
9427     
9428     beforeBlur : Roo.emptyFn,
9429
9430     
9431     // private
9432     onBlur : function(){
9433         this.beforeBlur();
9434         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9435             //this.el.removeClass(this.focusClass);
9436         }
9437         this.hasFocus = false;
9438         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9439             this.validate();
9440         }
9441         var v = this.getValue();
9442         if(String(v) !== String(this.startValue)){
9443             this.fireEvent('change', this, v, this.startValue);
9444         }
9445         this.fireEvent("blur", this);
9446     },
9447     
9448     onChange : function(e)
9449     {
9450         var v = this.getValue();
9451         if(String(v) !== String(this.startValue)){
9452             this.fireEvent('change', this, v, this.startValue);
9453         }
9454         
9455     },
9456     
9457     /**
9458      * Resets the current field value to the originally loaded value and clears any validation messages
9459      */
9460     reset : function(){
9461         this.setValue(this.originalValue);
9462         this.validate();
9463     },
9464      /**
9465      * Returns the name of the field
9466      * @return {Mixed} name The name field
9467      */
9468     getName: function(){
9469         return this.name;
9470     },
9471      /**
9472      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9473      * @return {Mixed} value The field value
9474      */
9475     getValue : function(){
9476         
9477         var v = this.inputEl().getValue();
9478         
9479         return v;
9480     },
9481     /**
9482      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9483      * @return {Mixed} value The field value
9484      */
9485     getRawValue : function(){
9486         var v = this.inputEl().getValue();
9487         
9488         return v;
9489     },
9490     
9491     /**
9492      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9493      * @param {Mixed} value The value to set
9494      */
9495     setRawValue : function(v){
9496         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9497     },
9498     
9499     selectText : function(start, end){
9500         var v = this.getRawValue();
9501         if(v.length > 0){
9502             start = start === undefined ? 0 : start;
9503             end = end === undefined ? v.length : end;
9504             var d = this.inputEl().dom;
9505             if(d.setSelectionRange){
9506                 d.setSelectionRange(start, end);
9507             }else if(d.createTextRange){
9508                 var range = d.createTextRange();
9509                 range.moveStart("character", start);
9510                 range.moveEnd("character", v.length-end);
9511                 range.select();
9512             }
9513         }
9514     },
9515     
9516     /**
9517      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9518      * @param {Mixed} value The value to set
9519      */
9520     setValue : function(v){
9521         this.value = v;
9522         if(this.rendered){
9523             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9524             this.validate();
9525         }
9526     },
9527     
9528     /*
9529     processValue : function(value){
9530         if(this.stripCharsRe){
9531             var newValue = value.replace(this.stripCharsRe, '');
9532             if(newValue !== value){
9533                 this.setRawValue(newValue);
9534                 return newValue;
9535             }
9536         }
9537         return value;
9538     },
9539   */
9540     preFocus : function(){
9541         
9542         if(this.selectOnFocus){
9543             this.inputEl().dom.select();
9544         }
9545     },
9546     filterKeys : function(e){
9547         var k = e.getKey();
9548         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9549             return;
9550         }
9551         var c = e.getCharCode(), cc = String.fromCharCode(c);
9552         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9553             return;
9554         }
9555         if(!this.maskRe.test(cc)){
9556             e.stopEvent();
9557         }
9558     },
9559      /**
9560      * Clear any invalid styles/messages for this field
9561      */
9562     clearInvalid : function(){
9563         
9564         if(!this.el || this.preventMark){ // not rendered
9565             return;
9566         }
9567         
9568      
9569         this.el.removeClass(this.invalidClass);
9570         
9571         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9572             
9573             var feedback = this.el.select('.form-control-feedback', true).first();
9574             
9575             if(feedback){
9576                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9577             }
9578             
9579         }
9580         
9581         if(this.indicator){
9582             this.indicator.removeClass('visible');
9583             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9584         }
9585         
9586         this.fireEvent('valid', this);
9587     },
9588     
9589      /**
9590      * Mark this field as valid
9591      */
9592     markValid : function()
9593     {
9594         if(!this.el  || this.preventMark){ // not rendered...
9595             return;
9596         }
9597         
9598         this.el.removeClass([this.invalidClass, this.validClass]);
9599         
9600         var feedback = this.el.select('.form-control-feedback', true).first();
9601             
9602         if(feedback){
9603             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9604         }
9605         
9606         if(this.indicator){
9607             this.indicator.removeClass('visible');
9608             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9609         }
9610         
9611         if(this.disabled){
9612             return;
9613         }
9614         
9615         if(this.allowBlank && !this.getRawValue().length){
9616             return;
9617         }
9618         
9619         this.el.addClass(this.validClass);
9620         
9621         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9622             
9623             var feedback = this.el.select('.form-control-feedback', true).first();
9624             
9625             if(feedback){
9626                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9627                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9628             }
9629             
9630         }
9631         
9632         this.fireEvent('valid', this);
9633     },
9634     
9635      /**
9636      * Mark this field as invalid
9637      * @param {String} msg The validation message
9638      */
9639     markInvalid : function(msg)
9640     {
9641         if(!this.el  || this.preventMark){ // not rendered
9642             return;
9643         }
9644         
9645         this.el.removeClass([this.invalidClass, this.validClass]);
9646         
9647         var feedback = this.el.select('.form-control-feedback', true).first();
9648             
9649         if(feedback){
9650             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9651         }
9652
9653         if(this.disabled){
9654             return;
9655         }
9656         
9657         if(this.allowBlank && !this.getRawValue().length){
9658             return;
9659         }
9660         
9661         if(this.indicator){
9662             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9663             this.indicator.addClass('visible');
9664         }
9665         
9666         this.el.addClass(this.invalidClass);
9667         
9668         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9669             
9670             var feedback = this.el.select('.form-control-feedback', true).first();
9671             
9672             if(feedback){
9673                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9674                 
9675                 if(this.getValue().length || this.forceFeedback){
9676                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9677                 }
9678                 
9679             }
9680             
9681         }
9682         
9683         this.fireEvent('invalid', this, msg);
9684     },
9685     // private
9686     SafariOnKeyDown : function(event)
9687     {
9688         // this is a workaround for a password hang bug on chrome/ webkit.
9689         if (this.inputEl().dom.type != 'password') {
9690             return;
9691         }
9692         
9693         var isSelectAll = false;
9694         
9695         if(this.inputEl().dom.selectionEnd > 0){
9696             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9697         }
9698         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9699             event.preventDefault();
9700             this.setValue('');
9701             return;
9702         }
9703         
9704         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9705             
9706             event.preventDefault();
9707             // this is very hacky as keydown always get's upper case.
9708             //
9709             var cc = String.fromCharCode(event.getCharCode());
9710             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9711             
9712         }
9713     },
9714     adjustWidth : function(tag, w){
9715         tag = tag.toLowerCase();
9716         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9717             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9718                 if(tag == 'input'){
9719                     return w + 2;
9720                 }
9721                 if(tag == 'textarea'){
9722                     return w-2;
9723                 }
9724             }else if(Roo.isOpera){
9725                 if(tag == 'input'){
9726                     return w + 2;
9727                 }
9728                 if(tag == 'textarea'){
9729                     return w-2;
9730                 }
9731             }
9732         }
9733         return w;
9734     },
9735     
9736     setFieldLabel : function(v)
9737     {
9738         if(!this.rendered){
9739             return;
9740         }
9741         
9742         if(this.indicator){
9743             var ar = this.el.select('label > span',true);
9744             
9745             if (ar.elements.length) {
9746                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9747                 this.fieldLabel = v;
9748                 return;
9749             }
9750             
9751             var br = this.el.select('label',true);
9752             
9753             if(br.elements.length) {
9754                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9755                 this.fieldLabel = v;
9756                 return;
9757             }
9758             
9759             Roo.log('Cannot Found any of label > span || label in input');
9760             return;
9761         }
9762         
9763         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9764         this.fieldLabel = v;
9765         
9766         
9767     }
9768 });
9769
9770  
9771 /*
9772  * - LGPL
9773  *
9774  * Input
9775  * 
9776  */
9777
9778 /**
9779  * @class Roo.bootstrap.TextArea
9780  * @extends Roo.bootstrap.Input
9781  * Bootstrap TextArea class
9782  * @cfg {Number} cols Specifies the visible width of a text area
9783  * @cfg {Number} rows Specifies the visible number of lines in a text area
9784  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9785  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9786  * @cfg {string} html text
9787  * 
9788  * @constructor
9789  * Create a new TextArea
9790  * @param {Object} config The config object
9791  */
9792
9793 Roo.bootstrap.TextArea = function(config){
9794     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9795    
9796 };
9797
9798 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9799      
9800     cols : false,
9801     rows : 5,
9802     readOnly : false,
9803     warp : 'soft',
9804     resize : false,
9805     value: false,
9806     html: false,
9807     
9808     getAutoCreate : function(){
9809         
9810         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9811         
9812         var id = Roo.id();
9813         
9814         var cfg = {};
9815         
9816         if(this.inputType != 'hidden'){
9817             cfg.cls = 'form-group' //input-group
9818         }
9819         
9820         var input =  {
9821             tag: 'textarea',
9822             id : id,
9823             warp : this.warp,
9824             rows : this.rows,
9825             value : this.value || '',
9826             html: this.html || '',
9827             cls : 'form-control',
9828             placeholder : this.placeholder || '' 
9829             
9830         };
9831         
9832         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9833             input.maxLength = this.maxLength;
9834         }
9835         
9836         if(this.resize){
9837             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9838         }
9839         
9840         if(this.cols){
9841             input.cols = this.cols;
9842         }
9843         
9844         if (this.readOnly) {
9845             input.readonly = true;
9846         }
9847         
9848         if (this.name) {
9849             input.name = this.name;
9850         }
9851         
9852         if (this.size) {
9853             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9854         }
9855         
9856         var settings=this;
9857         ['xs','sm','md','lg'].map(function(size){
9858             if (settings[size]) {
9859                 cfg.cls += ' col-' + size + '-' + settings[size];
9860             }
9861         });
9862         
9863         var inputblock = input;
9864         
9865         if(this.hasFeedback && !this.allowBlank){
9866             
9867             var feedback = {
9868                 tag: 'span',
9869                 cls: 'glyphicon form-control-feedback'
9870             };
9871
9872             inputblock = {
9873                 cls : 'has-feedback',
9874                 cn :  [
9875                     input,
9876                     feedback
9877                 ] 
9878             };  
9879         }
9880         
9881         
9882         if (this.before || this.after) {
9883             
9884             inputblock = {
9885                 cls : 'input-group',
9886                 cn :  [] 
9887             };
9888             if (this.before) {
9889                 inputblock.cn.push({
9890                     tag :'span',
9891                     cls : 'input-group-addon',
9892                     html : this.before
9893                 });
9894             }
9895             
9896             inputblock.cn.push(input);
9897             
9898             if(this.hasFeedback && !this.allowBlank){
9899                 inputblock.cls += ' has-feedback';
9900                 inputblock.cn.push(feedback);
9901             }
9902             
9903             if (this.after) {
9904                 inputblock.cn.push({
9905                     tag :'span',
9906                     cls : 'input-group-addon',
9907                     html : this.after
9908                 });
9909             }
9910             
9911         }
9912         
9913         if (align ==='left' && this.fieldLabel.length) {
9914             cfg.cn = [
9915                 {
9916                     tag: 'label',
9917                     'for' :  id,
9918                     cls : 'control-label',
9919                     html : this.fieldLabel
9920                 },
9921                 {
9922                     cls : "",
9923                     cn: [
9924                         inputblock
9925                     ]
9926                 }
9927
9928             ];
9929             
9930             if(this.labelWidth > 12){
9931                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9932             }
9933
9934             if(this.labelWidth < 13 && this.labelmd == 0){
9935                 this.labelmd = this.labelWidth;
9936             }
9937
9938             if(this.labellg > 0){
9939                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9940                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9941             }
9942
9943             if(this.labelmd > 0){
9944                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9945                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9946             }
9947
9948             if(this.labelsm > 0){
9949                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9950                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9951             }
9952
9953             if(this.labelxs > 0){
9954                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9955                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9956             }
9957             
9958         } else if ( this.fieldLabel.length) {
9959             cfg.cn = [
9960
9961                {
9962                    tag: 'label',
9963                    //cls : 'input-group-addon',
9964                    html : this.fieldLabel
9965
9966                },
9967
9968                inputblock
9969
9970            ];
9971
9972         } else {
9973
9974             cfg.cn = [
9975
9976                 inputblock
9977
9978             ];
9979                 
9980         }
9981         
9982         if (this.disabled) {
9983             input.disabled=true;
9984         }
9985         
9986         return cfg;
9987         
9988     },
9989     /**
9990      * return the real textarea element.
9991      */
9992     inputEl: function ()
9993     {
9994         return this.el.select('textarea.form-control',true).first();
9995     },
9996     
9997     /**
9998      * Clear any invalid styles/messages for this field
9999      */
10000     clearInvalid : function()
10001     {
10002         
10003         if(!this.el || this.preventMark){ // not rendered
10004             return;
10005         }
10006         
10007         var label = this.el.select('label', true).first();
10008         var icon = this.el.select('i.fa-star', true).first();
10009         
10010         if(label && icon){
10011             icon.remove();
10012         }
10013         
10014         this.el.removeClass(this.invalidClass);
10015         
10016         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10017             
10018             var feedback = this.el.select('.form-control-feedback', true).first();
10019             
10020             if(feedback){
10021                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10022             }
10023             
10024         }
10025         
10026         this.fireEvent('valid', this);
10027     },
10028     
10029      /**
10030      * Mark this field as valid
10031      */
10032     markValid : function()
10033     {
10034         if(!this.el  || this.preventMark){ // not rendered
10035             return;
10036         }
10037         
10038         this.el.removeClass([this.invalidClass, this.validClass]);
10039         
10040         var feedback = this.el.select('.form-control-feedback', true).first();
10041             
10042         if(feedback){
10043             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10044         }
10045
10046         if(this.disabled || this.allowBlank){
10047             return;
10048         }
10049         
10050         var label = this.el.select('label', true).first();
10051         var icon = this.el.select('i.fa-star', true).first();
10052         
10053         if(label && icon){
10054             icon.remove();
10055         }
10056         
10057         this.el.addClass(this.validClass);
10058         
10059         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10060             
10061             var feedback = this.el.select('.form-control-feedback', true).first();
10062             
10063             if(feedback){
10064                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10065                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10066             }
10067             
10068         }
10069         
10070         this.fireEvent('valid', this);
10071     },
10072     
10073      /**
10074      * Mark this field as invalid
10075      * @param {String} msg The validation message
10076      */
10077     markInvalid : function(msg)
10078     {
10079         if(!this.el  || this.preventMark){ // not rendered
10080             return;
10081         }
10082         
10083         this.el.removeClass([this.invalidClass, this.validClass]);
10084         
10085         var feedback = this.el.select('.form-control-feedback', true).first();
10086             
10087         if(feedback){
10088             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10089         }
10090
10091         if(this.disabled || this.allowBlank){
10092             return;
10093         }
10094         
10095         var label = this.el.select('label', true).first();
10096         var icon = this.el.select('i.fa-star', true).first();
10097         
10098         if(!this.getValue().length && label && !icon){
10099             this.el.createChild({
10100                 tag : 'i',
10101                 cls : 'text-danger fa fa-lg fa-star',
10102                 tooltip : 'This field is required',
10103                 style : 'margin-right:5px;'
10104             }, label, true);
10105         }
10106
10107         this.el.addClass(this.invalidClass);
10108         
10109         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10110             
10111             var feedback = this.el.select('.form-control-feedback', true).first();
10112             
10113             if(feedback){
10114                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10115                 
10116                 if(this.getValue().length || this.forceFeedback){
10117                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10118                 }
10119                 
10120             }
10121             
10122         }
10123         
10124         this.fireEvent('invalid', this, msg);
10125     }
10126 });
10127
10128  
10129 /*
10130  * - LGPL
10131  *
10132  * trigger field - base class for combo..
10133  * 
10134  */
10135  
10136 /**
10137  * @class Roo.bootstrap.TriggerField
10138  * @extends Roo.bootstrap.Input
10139  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10140  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10141  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10142  * for which you can provide a custom implementation.  For example:
10143  * <pre><code>
10144 var trigger = new Roo.bootstrap.TriggerField();
10145 trigger.onTriggerClick = myTriggerFn;
10146 trigger.applyTo('my-field');
10147 </code></pre>
10148  *
10149  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10150  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10151  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10152  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10153  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10154
10155  * @constructor
10156  * Create a new TriggerField.
10157  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10158  * to the base TextField)
10159  */
10160 Roo.bootstrap.TriggerField = function(config){
10161     this.mimicing = false;
10162     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10163 };
10164
10165 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10166     /**
10167      * @cfg {String} triggerClass A CSS class to apply to the trigger
10168      */
10169      /**
10170      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10171      */
10172     hideTrigger:false,
10173
10174     /**
10175      * @cfg {Boolean} removable (true|false) special filter default false
10176      */
10177     removable : false,
10178     
10179     /** @cfg {Boolean} grow @hide */
10180     /** @cfg {Number} growMin @hide */
10181     /** @cfg {Number} growMax @hide */
10182
10183     /**
10184      * @hide 
10185      * @method
10186      */
10187     autoSize: Roo.emptyFn,
10188     // private
10189     monitorTab : true,
10190     // private
10191     deferHeight : true,
10192
10193     
10194     actionMode : 'wrap',
10195     
10196     caret : false,
10197     
10198     
10199     getAutoCreate : function(){
10200        
10201         var align = this.labelAlign || this.parentLabelAlign();
10202         
10203         var id = Roo.id();
10204         
10205         var cfg = {
10206             cls: 'form-group' //input-group
10207         };
10208         
10209         
10210         var input =  {
10211             tag: 'input',
10212             id : id,
10213             type : this.inputType,
10214             cls : 'form-control',
10215             autocomplete: 'new-password',
10216             placeholder : this.placeholder || '' 
10217             
10218         };
10219         if (this.name) {
10220             input.name = this.name;
10221         }
10222         if (this.size) {
10223             input.cls += ' input-' + this.size;
10224         }
10225         
10226         if (this.disabled) {
10227             input.disabled=true;
10228         }
10229         
10230         var inputblock = input;
10231         
10232         if(this.hasFeedback && !this.allowBlank){
10233             
10234             var feedback = {
10235                 tag: 'span',
10236                 cls: 'glyphicon form-control-feedback'
10237             };
10238             
10239             if(this.removable && !this.editable && !this.tickable){
10240                 inputblock = {
10241                     cls : 'has-feedback',
10242                     cn :  [
10243                         inputblock,
10244                         {
10245                             tag: 'button',
10246                             html : 'x',
10247                             cls : 'roo-combo-removable-btn close'
10248                         },
10249                         feedback
10250                     ] 
10251                 };
10252             } else {
10253                 inputblock = {
10254                     cls : 'has-feedback',
10255                     cn :  [
10256                         inputblock,
10257                         feedback
10258                     ] 
10259                 };
10260             }
10261
10262         } else {
10263             if(this.removable && !this.editable && !this.tickable){
10264                 inputblock = {
10265                     cls : 'roo-removable',
10266                     cn :  [
10267                         inputblock,
10268                         {
10269                             tag: 'button',
10270                             html : 'x',
10271                             cls : 'roo-combo-removable-btn close'
10272                         }
10273                     ] 
10274                 };
10275             }
10276         }
10277         
10278         if (this.before || this.after) {
10279             
10280             inputblock = {
10281                 cls : 'input-group',
10282                 cn :  [] 
10283             };
10284             if (this.before) {
10285                 inputblock.cn.push({
10286                     tag :'span',
10287                     cls : 'input-group-addon',
10288                     html : this.before
10289                 });
10290             }
10291             
10292             inputblock.cn.push(input);
10293             
10294             if(this.hasFeedback && !this.allowBlank){
10295                 inputblock.cls += ' has-feedback';
10296                 inputblock.cn.push(feedback);
10297             }
10298             
10299             if (this.after) {
10300                 inputblock.cn.push({
10301                     tag :'span',
10302                     cls : 'input-group-addon',
10303                     html : this.after
10304                 });
10305             }
10306             
10307         };
10308         
10309         var box = {
10310             tag: 'div',
10311             cn: [
10312                 {
10313                     tag: 'input',
10314                     type : 'hidden',
10315                     cls: 'form-hidden-field'
10316                 },
10317                 inputblock
10318             ]
10319             
10320         };
10321         
10322         if(this.multiple){
10323             box = {
10324                 tag: 'div',
10325                 cn: [
10326                     {
10327                         tag: 'input',
10328                         type : 'hidden',
10329                         cls: 'form-hidden-field'
10330                     },
10331                     {
10332                         tag: 'ul',
10333                         cls: 'roo-select2-choices',
10334                         cn:[
10335                             {
10336                                 tag: 'li',
10337                                 cls: 'roo-select2-search-field',
10338                                 cn: [
10339
10340                                     inputblock
10341                                 ]
10342                             }
10343                         ]
10344                     }
10345                 ]
10346             }
10347         };
10348         
10349         var combobox = {
10350             cls: 'roo-select2-container input-group',
10351             cn: [
10352                 box
10353 //                {
10354 //                    tag: 'ul',
10355 //                    cls: 'typeahead typeahead-long dropdown-menu',
10356 //                    style: 'display:none'
10357 //                }
10358             ]
10359         };
10360         
10361         if(!this.multiple && this.showToggleBtn){
10362             
10363             var caret = {
10364                         tag: 'span',
10365                         cls: 'caret'
10366              };
10367             if (this.caret != false) {
10368                 caret = {
10369                      tag: 'i',
10370                      cls: 'fa fa-' + this.caret
10371                 };
10372                 
10373             }
10374             
10375             combobox.cn.push({
10376                 tag :'span',
10377                 cls : 'input-group-addon btn dropdown-toggle',
10378                 cn : [
10379                     caret,
10380                     {
10381                         tag: 'span',
10382                         cls: 'combobox-clear',
10383                         cn  : [
10384                             {
10385                                 tag : 'i',
10386                                 cls: 'icon-remove'
10387                             }
10388                         ]
10389                     }
10390                 ]
10391
10392             })
10393         }
10394         
10395         if(this.multiple){
10396             combobox.cls += ' roo-select2-container-multi';
10397         }
10398         
10399         if (align ==='left' && this.fieldLabel.length) {
10400             
10401             cfg.cls += ' roo-form-group-label-left';
10402
10403             cfg.cn = [
10404                 {
10405                     tag : 'i',
10406                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10407                     tooltip : 'This field is required'
10408                 },
10409                 {
10410                     tag: 'label',
10411                     'for' :  id,
10412                     cls : 'control-label',
10413                     html : this.fieldLabel
10414
10415                 },
10416                 {
10417                     cls : "", 
10418                     cn: [
10419                         combobox
10420                     ]
10421                 }
10422
10423             ];
10424             
10425             var labelCfg = cfg.cn[1];
10426             var contentCfg = cfg.cn[2];
10427             
10428             if(this.indicatorpos == 'right'){
10429                 cfg.cn = [
10430                     {
10431                         tag: 'label',
10432                         'for' :  id,
10433                         cls : 'control-label',
10434                         cn : [
10435                             {
10436                                 tag : 'span',
10437                                 html : this.fieldLabel
10438                             },
10439                             {
10440                                 tag : 'i',
10441                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10442                                 tooltip : 'This field is required'
10443                             }
10444                         ]
10445                     },
10446                     {
10447                         cls : "", 
10448                         cn: [
10449                             combobox
10450                         ]
10451                     }
10452
10453                 ];
10454                 
10455                 labelCfg = cfg.cn[0];
10456                 contentCfg = cfg.cn[1];
10457             }
10458             
10459             if(this.labelWidth > 12){
10460                 labelCfg.style = "width: " + this.labelWidth + 'px';
10461             }
10462             
10463             if(this.labelWidth < 13 && this.labelmd == 0){
10464                 this.labelmd = this.labelWidth;
10465             }
10466             
10467             if(this.labellg > 0){
10468                 labelCfg.cls += ' col-lg-' + this.labellg;
10469                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10470             }
10471             
10472             if(this.labelmd > 0){
10473                 labelCfg.cls += ' col-md-' + this.labelmd;
10474                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10475             }
10476             
10477             if(this.labelsm > 0){
10478                 labelCfg.cls += ' col-sm-' + this.labelsm;
10479                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10480             }
10481             
10482             if(this.labelxs > 0){
10483                 labelCfg.cls += ' col-xs-' + this.labelxs;
10484                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10485             }
10486             
10487         } else if ( this.fieldLabel.length) {
10488 //                Roo.log(" label");
10489             cfg.cn = [
10490                 {
10491                    tag : 'i',
10492                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10493                    tooltip : 'This field is required'
10494                },
10495                {
10496                    tag: 'label',
10497                    //cls : 'input-group-addon',
10498                    html : this.fieldLabel
10499
10500                },
10501
10502                combobox
10503
10504             ];
10505             
10506             if(this.indicatorpos == 'right'){
10507                 
10508                 cfg.cn = [
10509                     {
10510                        tag: 'label',
10511                        cn : [
10512                            {
10513                                tag : 'span',
10514                                html : this.fieldLabel
10515                            },
10516                            {
10517                               tag : 'i',
10518                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10519                               tooltip : 'This field is required'
10520                            }
10521                        ]
10522
10523                     },
10524                     combobox
10525
10526                 ];
10527
10528             }
10529
10530         } else {
10531             
10532 //                Roo.log(" no label && no align");
10533                 cfg = combobox
10534                      
10535                 
10536         }
10537         
10538         var settings=this;
10539         ['xs','sm','md','lg'].map(function(size){
10540             if (settings[size]) {
10541                 cfg.cls += ' col-' + size + '-' + settings[size];
10542             }
10543         });
10544         
10545         return cfg;
10546         
10547     },
10548     
10549     
10550     
10551     // private
10552     onResize : function(w, h){
10553 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10554 //        if(typeof w == 'number'){
10555 //            var x = w - this.trigger.getWidth();
10556 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10557 //            this.trigger.setStyle('left', x+'px');
10558 //        }
10559     },
10560
10561     // private
10562     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10563
10564     // private
10565     getResizeEl : function(){
10566         return this.inputEl();
10567     },
10568
10569     // private
10570     getPositionEl : function(){
10571         return this.inputEl();
10572     },
10573
10574     // private
10575     alignErrorIcon : function(){
10576         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10577     },
10578
10579     // private
10580     initEvents : function(){
10581         
10582         this.createList();
10583         
10584         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10585         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10586         if(!this.multiple && this.showToggleBtn){
10587             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10588             if(this.hideTrigger){
10589                 this.trigger.setDisplayed(false);
10590             }
10591             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10592         }
10593         
10594         if(this.multiple){
10595             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10596         }
10597         
10598         if(this.removable && !this.editable && !this.tickable){
10599             var close = this.closeTriggerEl();
10600             
10601             if(close){
10602                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10603                 close.on('click', this.removeBtnClick, this, close);
10604             }
10605         }
10606         
10607         //this.trigger.addClassOnOver('x-form-trigger-over');
10608         //this.trigger.addClassOnClick('x-form-trigger-click');
10609         
10610         //if(!this.width){
10611         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10612         //}
10613     },
10614     
10615     closeTriggerEl : function()
10616     {
10617         var close = this.el.select('.roo-combo-removable-btn', true).first();
10618         return close ? close : false;
10619     },
10620     
10621     removeBtnClick : function(e, h, el)
10622     {
10623         e.preventDefault();
10624         
10625         if(this.fireEvent("remove", this) !== false){
10626             this.reset();
10627             this.fireEvent("afterremove", this)
10628         }
10629     },
10630     
10631     createList : function()
10632     {
10633         this.list = Roo.get(document.body).createChild({
10634             tag: 'ul',
10635             cls: 'typeahead typeahead-long dropdown-menu',
10636             style: 'display:none'
10637         });
10638         
10639         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10640         
10641     },
10642
10643     // private
10644     initTrigger : function(){
10645        
10646     },
10647
10648     // private
10649     onDestroy : function(){
10650         if(this.trigger){
10651             this.trigger.removeAllListeners();
10652           //  this.trigger.remove();
10653         }
10654         //if(this.wrap){
10655         //    this.wrap.remove();
10656         //}
10657         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10658     },
10659
10660     // private
10661     onFocus : function(){
10662         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10663         /*
10664         if(!this.mimicing){
10665             this.wrap.addClass('x-trigger-wrap-focus');
10666             this.mimicing = true;
10667             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10668             if(this.monitorTab){
10669                 this.el.on("keydown", this.checkTab, this);
10670             }
10671         }
10672         */
10673     },
10674
10675     // private
10676     checkTab : function(e){
10677         if(e.getKey() == e.TAB){
10678             this.triggerBlur();
10679         }
10680     },
10681
10682     // private
10683     onBlur : function(){
10684         // do nothing
10685     },
10686
10687     // private
10688     mimicBlur : function(e, t){
10689         /*
10690         if(!this.wrap.contains(t) && this.validateBlur()){
10691             this.triggerBlur();
10692         }
10693         */
10694     },
10695
10696     // private
10697     triggerBlur : function(){
10698         this.mimicing = false;
10699         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10700         if(this.monitorTab){
10701             this.el.un("keydown", this.checkTab, this);
10702         }
10703         //this.wrap.removeClass('x-trigger-wrap-focus');
10704         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10705     },
10706
10707     // private
10708     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10709     validateBlur : function(e, t){
10710         return true;
10711     },
10712
10713     // private
10714     onDisable : function(){
10715         this.inputEl().dom.disabled = true;
10716         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10717         //if(this.wrap){
10718         //    this.wrap.addClass('x-item-disabled');
10719         //}
10720     },
10721
10722     // private
10723     onEnable : function(){
10724         this.inputEl().dom.disabled = false;
10725         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10726         //if(this.wrap){
10727         //    this.el.removeClass('x-item-disabled');
10728         //}
10729     },
10730
10731     // private
10732     onShow : function(){
10733         var ae = this.getActionEl();
10734         
10735         if(ae){
10736             ae.dom.style.display = '';
10737             ae.dom.style.visibility = 'visible';
10738         }
10739     },
10740
10741     // private
10742     
10743     onHide : function(){
10744         var ae = this.getActionEl();
10745         ae.dom.style.display = 'none';
10746     },
10747
10748     /**
10749      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10750      * by an implementing function.
10751      * @method
10752      * @param {EventObject} e
10753      */
10754     onTriggerClick : Roo.emptyFn
10755 });
10756  /*
10757  * Based on:
10758  * Ext JS Library 1.1.1
10759  * Copyright(c) 2006-2007, Ext JS, LLC.
10760  *
10761  * Originally Released Under LGPL - original licence link has changed is not relivant.
10762  *
10763  * Fork - LGPL
10764  * <script type="text/javascript">
10765  */
10766
10767
10768 /**
10769  * @class Roo.data.SortTypes
10770  * @singleton
10771  * Defines the default sorting (casting?) comparison functions used when sorting data.
10772  */
10773 Roo.data.SortTypes = {
10774     /**
10775      * Default sort that does nothing
10776      * @param {Mixed} s The value being converted
10777      * @return {Mixed} The comparison value
10778      */
10779     none : function(s){
10780         return s;
10781     },
10782     
10783     /**
10784      * The regular expression used to strip tags
10785      * @type {RegExp}
10786      * @property
10787      */
10788     stripTagsRE : /<\/?[^>]+>/gi,
10789     
10790     /**
10791      * Strips all HTML tags to sort on text only
10792      * @param {Mixed} s The value being converted
10793      * @return {String} The comparison value
10794      */
10795     asText : function(s){
10796         return String(s).replace(this.stripTagsRE, "");
10797     },
10798     
10799     /**
10800      * Strips all HTML tags to sort on text only - Case insensitive
10801      * @param {Mixed} s The value being converted
10802      * @return {String} The comparison value
10803      */
10804     asUCText : function(s){
10805         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10806     },
10807     
10808     /**
10809      * Case insensitive string
10810      * @param {Mixed} s The value being converted
10811      * @return {String} The comparison value
10812      */
10813     asUCString : function(s) {
10814         return String(s).toUpperCase();
10815     },
10816     
10817     /**
10818      * Date sorting
10819      * @param {Mixed} s The value being converted
10820      * @return {Number} The comparison value
10821      */
10822     asDate : function(s) {
10823         if(!s){
10824             return 0;
10825         }
10826         if(s instanceof Date){
10827             return s.getTime();
10828         }
10829         return Date.parse(String(s));
10830     },
10831     
10832     /**
10833      * Float sorting
10834      * @param {Mixed} s The value being converted
10835      * @return {Float} The comparison value
10836      */
10837     asFloat : function(s) {
10838         var val = parseFloat(String(s).replace(/,/g, ""));
10839         if(isNaN(val)) {
10840             val = 0;
10841         }
10842         return val;
10843     },
10844     
10845     /**
10846      * Integer sorting
10847      * @param {Mixed} s The value being converted
10848      * @return {Number} The comparison value
10849      */
10850     asInt : function(s) {
10851         var val = parseInt(String(s).replace(/,/g, ""));
10852         if(isNaN(val)) {
10853             val = 0;
10854         }
10855         return val;
10856     }
10857 };/*
10858  * Based on:
10859  * Ext JS Library 1.1.1
10860  * Copyright(c) 2006-2007, Ext JS, LLC.
10861  *
10862  * Originally Released Under LGPL - original licence link has changed is not relivant.
10863  *
10864  * Fork - LGPL
10865  * <script type="text/javascript">
10866  */
10867
10868 /**
10869 * @class Roo.data.Record
10870  * Instances of this class encapsulate both record <em>definition</em> information, and record
10871  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10872  * to access Records cached in an {@link Roo.data.Store} object.<br>
10873  * <p>
10874  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10875  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10876  * objects.<br>
10877  * <p>
10878  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10879  * @constructor
10880  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10881  * {@link #create}. The parameters are the same.
10882  * @param {Array} data An associative Array of data values keyed by the field name.
10883  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10884  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10885  * not specified an integer id is generated.
10886  */
10887 Roo.data.Record = function(data, id){
10888     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10889     this.data = data;
10890 };
10891
10892 /**
10893  * Generate a constructor for a specific record layout.
10894  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10895  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10896  * Each field definition object may contain the following properties: <ul>
10897  * <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,
10898  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10899  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10900  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10901  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10902  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10903  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10904  * this may be omitted.</p></li>
10905  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10906  * <ul><li>auto (Default, implies no conversion)</li>
10907  * <li>string</li>
10908  * <li>int</li>
10909  * <li>float</li>
10910  * <li>boolean</li>
10911  * <li>date</li></ul></p></li>
10912  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10913  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10914  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10915  * by the Reader into an object that will be stored in the Record. It is passed the
10916  * following parameters:<ul>
10917  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10918  * </ul></p></li>
10919  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10920  * </ul>
10921  * <br>usage:<br><pre><code>
10922 var TopicRecord = Roo.data.Record.create(
10923     {name: 'title', mapping: 'topic_title'},
10924     {name: 'author', mapping: 'username'},
10925     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10926     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10927     {name: 'lastPoster', mapping: 'user2'},
10928     {name: 'excerpt', mapping: 'post_text'}
10929 );
10930
10931 var myNewRecord = new TopicRecord({
10932     title: 'Do my job please',
10933     author: 'noobie',
10934     totalPosts: 1,
10935     lastPost: new Date(),
10936     lastPoster: 'Animal',
10937     excerpt: 'No way dude!'
10938 });
10939 myStore.add(myNewRecord);
10940 </code></pre>
10941  * @method create
10942  * @static
10943  */
10944 Roo.data.Record.create = function(o){
10945     var f = function(){
10946         f.superclass.constructor.apply(this, arguments);
10947     };
10948     Roo.extend(f, Roo.data.Record);
10949     var p = f.prototype;
10950     p.fields = new Roo.util.MixedCollection(false, function(field){
10951         return field.name;
10952     });
10953     for(var i = 0, len = o.length; i < len; i++){
10954         p.fields.add(new Roo.data.Field(o[i]));
10955     }
10956     f.getField = function(name){
10957         return p.fields.get(name);  
10958     };
10959     return f;
10960 };
10961
10962 Roo.data.Record.AUTO_ID = 1000;
10963 Roo.data.Record.EDIT = 'edit';
10964 Roo.data.Record.REJECT = 'reject';
10965 Roo.data.Record.COMMIT = 'commit';
10966
10967 Roo.data.Record.prototype = {
10968     /**
10969      * Readonly flag - true if this record has been modified.
10970      * @type Boolean
10971      */
10972     dirty : false,
10973     editing : false,
10974     error: null,
10975     modified: null,
10976
10977     // private
10978     join : function(store){
10979         this.store = store;
10980     },
10981
10982     /**
10983      * Set the named field to the specified value.
10984      * @param {String} name The name of the field to set.
10985      * @param {Object} value The value to set the field to.
10986      */
10987     set : function(name, value){
10988         if(this.data[name] == value){
10989             return;
10990         }
10991         this.dirty = true;
10992         if(!this.modified){
10993             this.modified = {};
10994         }
10995         if(typeof this.modified[name] == 'undefined'){
10996             this.modified[name] = this.data[name];
10997         }
10998         this.data[name] = value;
10999         if(!this.editing && this.store){
11000             this.store.afterEdit(this);
11001         }       
11002     },
11003
11004     /**
11005      * Get the value of the named field.
11006      * @param {String} name The name of the field to get the value of.
11007      * @return {Object} The value of the field.
11008      */
11009     get : function(name){
11010         return this.data[name]; 
11011     },
11012
11013     // private
11014     beginEdit : function(){
11015         this.editing = true;
11016         this.modified = {}; 
11017     },
11018
11019     // private
11020     cancelEdit : function(){
11021         this.editing = false;
11022         delete this.modified;
11023     },
11024
11025     // private
11026     endEdit : function(){
11027         this.editing = false;
11028         if(this.dirty && this.store){
11029             this.store.afterEdit(this);
11030         }
11031     },
11032
11033     /**
11034      * Usually called by the {@link Roo.data.Store} which owns the Record.
11035      * Rejects all changes made to the Record since either creation, or the last commit operation.
11036      * Modified fields are reverted to their original values.
11037      * <p>
11038      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11039      * of reject operations.
11040      */
11041     reject : function(){
11042         var m = this.modified;
11043         for(var n in m){
11044             if(typeof m[n] != "function"){
11045                 this.data[n] = m[n];
11046             }
11047         }
11048         this.dirty = false;
11049         delete this.modified;
11050         this.editing = false;
11051         if(this.store){
11052             this.store.afterReject(this);
11053         }
11054     },
11055
11056     /**
11057      * Usually called by the {@link Roo.data.Store} which owns the Record.
11058      * Commits all changes made to the Record since either creation, or the last commit operation.
11059      * <p>
11060      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11061      * of commit operations.
11062      */
11063     commit : function(){
11064         this.dirty = false;
11065         delete this.modified;
11066         this.editing = false;
11067         if(this.store){
11068             this.store.afterCommit(this);
11069         }
11070     },
11071
11072     // private
11073     hasError : function(){
11074         return this.error != null;
11075     },
11076
11077     // private
11078     clearError : function(){
11079         this.error = null;
11080     },
11081
11082     /**
11083      * Creates a copy of this record.
11084      * @param {String} id (optional) A new record id if you don't want to use this record's id
11085      * @return {Record}
11086      */
11087     copy : function(newId) {
11088         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11089     }
11090 };/*
11091  * Based on:
11092  * Ext JS Library 1.1.1
11093  * Copyright(c) 2006-2007, Ext JS, LLC.
11094  *
11095  * Originally Released Under LGPL - original licence link has changed is not relivant.
11096  *
11097  * Fork - LGPL
11098  * <script type="text/javascript">
11099  */
11100
11101
11102
11103 /**
11104  * @class Roo.data.Store
11105  * @extends Roo.util.Observable
11106  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11107  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11108  * <p>
11109  * 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
11110  * has no knowledge of the format of the data returned by the Proxy.<br>
11111  * <p>
11112  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11113  * instances from the data object. These records are cached and made available through accessor functions.
11114  * @constructor
11115  * Creates a new Store.
11116  * @param {Object} config A config object containing the objects needed for the Store to access data,
11117  * and read the data into Records.
11118  */
11119 Roo.data.Store = function(config){
11120     this.data = new Roo.util.MixedCollection(false);
11121     this.data.getKey = function(o){
11122         return o.id;
11123     };
11124     this.baseParams = {};
11125     // private
11126     this.paramNames = {
11127         "start" : "start",
11128         "limit" : "limit",
11129         "sort" : "sort",
11130         "dir" : "dir",
11131         "multisort" : "_multisort"
11132     };
11133
11134     if(config && config.data){
11135         this.inlineData = config.data;
11136         delete config.data;
11137     }
11138
11139     Roo.apply(this, config);
11140     
11141     if(this.reader){ // reader passed
11142         this.reader = Roo.factory(this.reader, Roo.data);
11143         this.reader.xmodule = this.xmodule || false;
11144         if(!this.recordType){
11145             this.recordType = this.reader.recordType;
11146         }
11147         if(this.reader.onMetaChange){
11148             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11149         }
11150     }
11151
11152     if(this.recordType){
11153         this.fields = this.recordType.prototype.fields;
11154     }
11155     this.modified = [];
11156
11157     this.addEvents({
11158         /**
11159          * @event datachanged
11160          * Fires when the data cache has changed, and a widget which is using this Store
11161          * as a Record cache should refresh its view.
11162          * @param {Store} this
11163          */
11164         datachanged : true,
11165         /**
11166          * @event metachange
11167          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11168          * @param {Store} this
11169          * @param {Object} meta The JSON metadata
11170          */
11171         metachange : true,
11172         /**
11173          * @event add
11174          * Fires when Records have been added to the Store
11175          * @param {Store} this
11176          * @param {Roo.data.Record[]} records The array of Records added
11177          * @param {Number} index The index at which the record(s) were added
11178          */
11179         add : true,
11180         /**
11181          * @event remove
11182          * Fires when a Record has been removed from the Store
11183          * @param {Store} this
11184          * @param {Roo.data.Record} record The Record that was removed
11185          * @param {Number} index The index at which the record was removed
11186          */
11187         remove : true,
11188         /**
11189          * @event update
11190          * Fires when a Record has been updated
11191          * @param {Store} this
11192          * @param {Roo.data.Record} record The Record that was updated
11193          * @param {String} operation The update operation being performed.  Value may be one of:
11194          * <pre><code>
11195  Roo.data.Record.EDIT
11196  Roo.data.Record.REJECT
11197  Roo.data.Record.COMMIT
11198          * </code></pre>
11199          */
11200         update : true,
11201         /**
11202          * @event clear
11203          * Fires when the data cache has been cleared.
11204          * @param {Store} this
11205          */
11206         clear : true,
11207         /**
11208          * @event beforeload
11209          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11210          * the load action will be canceled.
11211          * @param {Store} this
11212          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11213          */
11214         beforeload : true,
11215         /**
11216          * @event beforeloadadd
11217          * Fires after a new set of Records has been loaded.
11218          * @param {Store} this
11219          * @param {Roo.data.Record[]} records The Records that were loaded
11220          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11221          */
11222         beforeloadadd : true,
11223         /**
11224          * @event load
11225          * Fires after a new set of Records has been loaded, before they are added to the store.
11226          * @param {Store} this
11227          * @param {Roo.data.Record[]} records The Records that were loaded
11228          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11229          * @params {Object} return from reader
11230          */
11231         load : true,
11232         /**
11233          * @event loadexception
11234          * Fires if an exception occurs in the Proxy during loading.
11235          * Called with the signature of the Proxy's "loadexception" event.
11236          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11237          * 
11238          * @param {Proxy} 
11239          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11240          * @param {Object} load options 
11241          * @param {Object} jsonData from your request (normally this contains the Exception)
11242          */
11243         loadexception : true
11244     });
11245     
11246     if(this.proxy){
11247         this.proxy = Roo.factory(this.proxy, Roo.data);
11248         this.proxy.xmodule = this.xmodule || false;
11249         this.relayEvents(this.proxy,  ["loadexception"]);
11250     }
11251     this.sortToggle = {};
11252     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11253
11254     Roo.data.Store.superclass.constructor.call(this);
11255
11256     if(this.inlineData){
11257         this.loadData(this.inlineData);
11258         delete this.inlineData;
11259     }
11260 };
11261
11262 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11263      /**
11264     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11265     * without a remote query - used by combo/forms at present.
11266     */
11267     
11268     /**
11269     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11270     */
11271     /**
11272     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11273     */
11274     /**
11275     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11276     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11277     */
11278     /**
11279     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11280     * on any HTTP request
11281     */
11282     /**
11283     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11284     */
11285     /**
11286     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11287     */
11288     multiSort: false,
11289     /**
11290     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11291     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11292     */
11293     remoteSort : false,
11294
11295     /**
11296     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11297      * loaded or when a record is removed. (defaults to false).
11298     */
11299     pruneModifiedRecords : false,
11300
11301     // private
11302     lastOptions : null,
11303
11304     /**
11305      * Add Records to the Store and fires the add event.
11306      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11307      */
11308     add : function(records){
11309         records = [].concat(records);
11310         for(var i = 0, len = records.length; i < len; i++){
11311             records[i].join(this);
11312         }
11313         var index = this.data.length;
11314         this.data.addAll(records);
11315         this.fireEvent("add", this, records, index);
11316     },
11317
11318     /**
11319      * Remove a Record from the Store and fires the remove event.
11320      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11321      */
11322     remove : function(record){
11323         var index = this.data.indexOf(record);
11324         this.data.removeAt(index);
11325  
11326         if(this.pruneModifiedRecords){
11327             this.modified.remove(record);
11328         }
11329         this.fireEvent("remove", this, record, index);
11330     },
11331
11332     /**
11333      * Remove all Records from the Store and fires the clear event.
11334      */
11335     removeAll : function(){
11336         this.data.clear();
11337         if(this.pruneModifiedRecords){
11338             this.modified = [];
11339         }
11340         this.fireEvent("clear", this);
11341     },
11342
11343     /**
11344      * Inserts Records to the Store at the given index and fires the add event.
11345      * @param {Number} index The start index at which to insert the passed Records.
11346      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11347      */
11348     insert : function(index, records){
11349         records = [].concat(records);
11350         for(var i = 0, len = records.length; i < len; i++){
11351             this.data.insert(index, records[i]);
11352             records[i].join(this);
11353         }
11354         this.fireEvent("add", this, records, index);
11355     },
11356
11357     /**
11358      * Get the index within the cache of the passed Record.
11359      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11360      * @return {Number} The index of the passed Record. Returns -1 if not found.
11361      */
11362     indexOf : function(record){
11363         return this.data.indexOf(record);
11364     },
11365
11366     /**
11367      * Get the index within the cache of the Record with the passed id.
11368      * @param {String} id The id of the Record to find.
11369      * @return {Number} The index of the Record. Returns -1 if not found.
11370      */
11371     indexOfId : function(id){
11372         return this.data.indexOfKey(id);
11373     },
11374
11375     /**
11376      * Get the Record with the specified id.
11377      * @param {String} id The id of the Record to find.
11378      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11379      */
11380     getById : function(id){
11381         return this.data.key(id);
11382     },
11383
11384     /**
11385      * Get the Record at the specified index.
11386      * @param {Number} index The index of the Record to find.
11387      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11388      */
11389     getAt : function(index){
11390         return this.data.itemAt(index);
11391     },
11392
11393     /**
11394      * Returns a range of Records between specified indices.
11395      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11396      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11397      * @return {Roo.data.Record[]} An array of Records
11398      */
11399     getRange : function(start, end){
11400         return this.data.getRange(start, end);
11401     },
11402
11403     // private
11404     storeOptions : function(o){
11405         o = Roo.apply({}, o);
11406         delete o.callback;
11407         delete o.scope;
11408         this.lastOptions = o;
11409     },
11410
11411     /**
11412      * Loads the Record cache from the configured Proxy using the configured Reader.
11413      * <p>
11414      * If using remote paging, then the first load call must specify the <em>start</em>
11415      * and <em>limit</em> properties in the options.params property to establish the initial
11416      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11417      * <p>
11418      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11419      * and this call will return before the new data has been loaded. Perform any post-processing
11420      * in a callback function, or in a "load" event handler.</strong>
11421      * <p>
11422      * @param {Object} options An object containing properties which control loading options:<ul>
11423      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11424      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11425      * passed the following arguments:<ul>
11426      * <li>r : Roo.data.Record[]</li>
11427      * <li>options: Options object from the load call</li>
11428      * <li>success: Boolean success indicator</li></ul></li>
11429      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11430      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11431      * </ul>
11432      */
11433     load : function(options){
11434         options = options || {};
11435         if(this.fireEvent("beforeload", this, options) !== false){
11436             this.storeOptions(options);
11437             var p = Roo.apply(options.params || {}, this.baseParams);
11438             // if meta was not loaded from remote source.. try requesting it.
11439             if (!this.reader.metaFromRemote) {
11440                 p._requestMeta = 1;
11441             }
11442             if(this.sortInfo && this.remoteSort){
11443                 var pn = this.paramNames;
11444                 p[pn["sort"]] = this.sortInfo.field;
11445                 p[pn["dir"]] = this.sortInfo.direction;
11446             }
11447             if (this.multiSort) {
11448                 var pn = this.paramNames;
11449                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11450             }
11451             
11452             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11453         }
11454     },
11455
11456     /**
11457      * Reloads the Record cache from the configured Proxy using the configured Reader and
11458      * the options from the last load operation performed.
11459      * @param {Object} options (optional) An object containing properties which may override the options
11460      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11461      * the most recently used options are reused).
11462      */
11463     reload : function(options){
11464         this.load(Roo.applyIf(options||{}, this.lastOptions));
11465     },
11466
11467     // private
11468     // Called as a callback by the Reader during a load operation.
11469     loadRecords : function(o, options, success){
11470         if(!o || success === false){
11471             if(success !== false){
11472                 this.fireEvent("load", this, [], options, o);
11473             }
11474             if(options.callback){
11475                 options.callback.call(options.scope || this, [], options, false);
11476             }
11477             return;
11478         }
11479         // if data returned failure - throw an exception.
11480         if (o.success === false) {
11481             // show a message if no listener is registered.
11482             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11483                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11484             }
11485             // loadmask wil be hooked into this..
11486             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11487             return;
11488         }
11489         var r = o.records, t = o.totalRecords || r.length;
11490         
11491         this.fireEvent("beforeloadadd", this, r, options, o);
11492         
11493         if(!options || options.add !== true){
11494             if(this.pruneModifiedRecords){
11495                 this.modified = [];
11496             }
11497             for(var i = 0, len = r.length; i < len; i++){
11498                 r[i].join(this);
11499             }
11500             if(this.snapshot){
11501                 this.data = this.snapshot;
11502                 delete this.snapshot;
11503             }
11504             this.data.clear();
11505             this.data.addAll(r);
11506             this.totalLength = t;
11507             this.applySort();
11508             this.fireEvent("datachanged", this);
11509         }else{
11510             this.totalLength = Math.max(t, this.data.length+r.length);
11511             this.add(r);
11512         }
11513         
11514         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11515                 
11516             var e = new Roo.data.Record({});
11517
11518             e.set(this.parent.displayField, this.parent.emptyTitle);
11519             e.set(this.parent.valueField, '');
11520
11521             this.insert(0, e);
11522         }
11523             
11524         this.fireEvent("load", this, r, options, o);
11525         if(options.callback){
11526             options.callback.call(options.scope || this, r, options, true);
11527         }
11528     },
11529
11530
11531     /**
11532      * Loads data from a passed data block. A Reader which understands the format of the data
11533      * must have been configured in the constructor.
11534      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11535      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11536      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11537      */
11538     loadData : function(o, append){
11539         var r = this.reader.readRecords(o);
11540         this.loadRecords(r, {add: append}, true);
11541     },
11542
11543     /**
11544      * Gets the number of cached records.
11545      * <p>
11546      * <em>If using paging, this may not be the total size of the dataset. If the data object
11547      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11548      * the data set size</em>
11549      */
11550     getCount : function(){
11551         return this.data.length || 0;
11552     },
11553
11554     /**
11555      * Gets the total number of records in the dataset as returned by the server.
11556      * <p>
11557      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11558      * the dataset size</em>
11559      */
11560     getTotalCount : function(){
11561         return this.totalLength || 0;
11562     },
11563
11564     /**
11565      * Returns the sort state of the Store as an object with two properties:
11566      * <pre><code>
11567  field {String} The name of the field by which the Records are sorted
11568  direction {String} The sort order, "ASC" or "DESC"
11569      * </code></pre>
11570      */
11571     getSortState : function(){
11572         return this.sortInfo;
11573     },
11574
11575     // private
11576     applySort : function(){
11577         if(this.sortInfo && !this.remoteSort){
11578             var s = this.sortInfo, f = s.field;
11579             var st = this.fields.get(f).sortType;
11580             var fn = function(r1, r2){
11581                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11582                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11583             };
11584             this.data.sort(s.direction, fn);
11585             if(this.snapshot && this.snapshot != this.data){
11586                 this.snapshot.sort(s.direction, fn);
11587             }
11588         }
11589     },
11590
11591     /**
11592      * Sets the default sort column and order to be used by the next load operation.
11593      * @param {String} fieldName The name of the field to sort by.
11594      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11595      */
11596     setDefaultSort : function(field, dir){
11597         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11598     },
11599
11600     /**
11601      * Sort the Records.
11602      * If remote sorting is used, the sort is performed on the server, and the cache is
11603      * reloaded. If local sorting is used, the cache is sorted internally.
11604      * @param {String} fieldName The name of the field to sort by.
11605      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11606      */
11607     sort : function(fieldName, dir){
11608         var f = this.fields.get(fieldName);
11609         if(!dir){
11610             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11611             
11612             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11613                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11614             }else{
11615                 dir = f.sortDir;
11616             }
11617         }
11618         this.sortToggle[f.name] = dir;
11619         this.sortInfo = {field: f.name, direction: dir};
11620         if(!this.remoteSort){
11621             this.applySort();
11622             this.fireEvent("datachanged", this);
11623         }else{
11624             this.load(this.lastOptions);
11625         }
11626     },
11627
11628     /**
11629      * Calls the specified function for each of the Records in the cache.
11630      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11631      * Returning <em>false</em> aborts and exits the iteration.
11632      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11633      */
11634     each : function(fn, scope){
11635         this.data.each(fn, scope);
11636     },
11637
11638     /**
11639      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11640      * (e.g., during paging).
11641      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11642      */
11643     getModifiedRecords : function(){
11644         return this.modified;
11645     },
11646
11647     // private
11648     createFilterFn : function(property, value, anyMatch){
11649         if(!value.exec){ // not a regex
11650             value = String(value);
11651             if(value.length == 0){
11652                 return false;
11653             }
11654             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11655         }
11656         return function(r){
11657             return value.test(r.data[property]);
11658         };
11659     },
11660
11661     /**
11662      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11663      * @param {String} property A field on your records
11664      * @param {Number} start The record index to start at (defaults to 0)
11665      * @param {Number} end The last record index to include (defaults to length - 1)
11666      * @return {Number} The sum
11667      */
11668     sum : function(property, start, end){
11669         var rs = this.data.items, v = 0;
11670         start = start || 0;
11671         end = (end || end === 0) ? end : rs.length-1;
11672
11673         for(var i = start; i <= end; i++){
11674             v += (rs[i].data[property] || 0);
11675         }
11676         return v;
11677     },
11678
11679     /**
11680      * Filter the records by a specified property.
11681      * @param {String} field A field on your records
11682      * @param {String/RegExp} value Either a string that the field
11683      * should start with or a RegExp to test against the field
11684      * @param {Boolean} anyMatch True to match any part not just the beginning
11685      */
11686     filter : function(property, value, anyMatch){
11687         var fn = this.createFilterFn(property, value, anyMatch);
11688         return fn ? this.filterBy(fn) : this.clearFilter();
11689     },
11690
11691     /**
11692      * Filter by a function. The specified function will be called with each
11693      * record in this data source. If the function returns true the record is included,
11694      * otherwise it is filtered.
11695      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11696      * @param {Object} scope (optional) The scope of the function (defaults to this)
11697      */
11698     filterBy : function(fn, scope){
11699         this.snapshot = this.snapshot || this.data;
11700         this.data = this.queryBy(fn, scope||this);
11701         this.fireEvent("datachanged", this);
11702     },
11703
11704     /**
11705      * Query the records by a specified property.
11706      * @param {String} field A field on your records
11707      * @param {String/RegExp} value Either a string that the field
11708      * should start with or a RegExp to test against the field
11709      * @param {Boolean} anyMatch True to match any part not just the beginning
11710      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11711      */
11712     query : function(property, value, anyMatch){
11713         var fn = this.createFilterFn(property, value, anyMatch);
11714         return fn ? this.queryBy(fn) : this.data.clone();
11715     },
11716
11717     /**
11718      * Query by a function. The specified function will be called with each
11719      * record in this data source. If the function returns true the record is included
11720      * in the results.
11721      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11722      * @param {Object} scope (optional) The scope of the function (defaults to this)
11723       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11724      **/
11725     queryBy : function(fn, scope){
11726         var data = this.snapshot || this.data;
11727         return data.filterBy(fn, scope||this);
11728     },
11729
11730     /**
11731      * Collects unique values for a particular dataIndex from this store.
11732      * @param {String} dataIndex The property to collect
11733      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11734      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11735      * @return {Array} An array of the unique values
11736      **/
11737     collect : function(dataIndex, allowNull, bypassFilter){
11738         var d = (bypassFilter === true && this.snapshot) ?
11739                 this.snapshot.items : this.data.items;
11740         var v, sv, r = [], l = {};
11741         for(var i = 0, len = d.length; i < len; i++){
11742             v = d[i].data[dataIndex];
11743             sv = String(v);
11744             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11745                 l[sv] = true;
11746                 r[r.length] = v;
11747             }
11748         }
11749         return r;
11750     },
11751
11752     /**
11753      * Revert to a view of the Record cache with no filtering applied.
11754      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11755      */
11756     clearFilter : function(suppressEvent){
11757         if(this.snapshot && this.snapshot != this.data){
11758             this.data = this.snapshot;
11759             delete this.snapshot;
11760             if(suppressEvent !== true){
11761                 this.fireEvent("datachanged", this);
11762             }
11763         }
11764     },
11765
11766     // private
11767     afterEdit : function(record){
11768         if(this.modified.indexOf(record) == -1){
11769             this.modified.push(record);
11770         }
11771         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11772     },
11773     
11774     // private
11775     afterReject : function(record){
11776         this.modified.remove(record);
11777         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11778     },
11779
11780     // private
11781     afterCommit : function(record){
11782         this.modified.remove(record);
11783         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11784     },
11785
11786     /**
11787      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11788      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11789      */
11790     commitChanges : function(){
11791         var m = this.modified.slice(0);
11792         this.modified = [];
11793         for(var i = 0, len = m.length; i < len; i++){
11794             m[i].commit();
11795         }
11796     },
11797
11798     /**
11799      * Cancel outstanding changes on all changed records.
11800      */
11801     rejectChanges : function(){
11802         var m = this.modified.slice(0);
11803         this.modified = [];
11804         for(var i = 0, len = m.length; i < len; i++){
11805             m[i].reject();
11806         }
11807     },
11808
11809     onMetaChange : function(meta, rtype, o){
11810         this.recordType = rtype;
11811         this.fields = rtype.prototype.fields;
11812         delete this.snapshot;
11813         this.sortInfo = meta.sortInfo || this.sortInfo;
11814         this.modified = [];
11815         this.fireEvent('metachange', this, this.reader.meta);
11816     },
11817     
11818     moveIndex : function(data, type)
11819     {
11820         var index = this.indexOf(data);
11821         
11822         var newIndex = index + type;
11823         
11824         this.remove(data);
11825         
11826         this.insert(newIndex, data);
11827         
11828     }
11829 });/*
11830  * Based on:
11831  * Ext JS Library 1.1.1
11832  * Copyright(c) 2006-2007, Ext JS, LLC.
11833  *
11834  * Originally Released Under LGPL - original licence link has changed is not relivant.
11835  *
11836  * Fork - LGPL
11837  * <script type="text/javascript">
11838  */
11839
11840 /**
11841  * @class Roo.data.SimpleStore
11842  * @extends Roo.data.Store
11843  * Small helper class to make creating Stores from Array data easier.
11844  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11845  * @cfg {Array} fields An array of field definition objects, or field name strings.
11846  * @cfg {Array} data The multi-dimensional array of data
11847  * @constructor
11848  * @param {Object} config
11849  */
11850 Roo.data.SimpleStore = function(config){
11851     Roo.data.SimpleStore.superclass.constructor.call(this, {
11852         isLocal : true,
11853         reader: new Roo.data.ArrayReader({
11854                 id: config.id
11855             },
11856             Roo.data.Record.create(config.fields)
11857         ),
11858         proxy : new Roo.data.MemoryProxy(config.data)
11859     });
11860     this.load();
11861 };
11862 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11863  * Based on:
11864  * Ext JS Library 1.1.1
11865  * Copyright(c) 2006-2007, Ext JS, LLC.
11866  *
11867  * Originally Released Under LGPL - original licence link has changed is not relivant.
11868  *
11869  * Fork - LGPL
11870  * <script type="text/javascript">
11871  */
11872
11873 /**
11874 /**
11875  * @extends Roo.data.Store
11876  * @class Roo.data.JsonStore
11877  * Small helper class to make creating Stores for JSON data easier. <br/>
11878 <pre><code>
11879 var store = new Roo.data.JsonStore({
11880     url: 'get-images.php',
11881     root: 'images',
11882     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11883 });
11884 </code></pre>
11885  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11886  * JsonReader and HttpProxy (unless inline data is provided).</b>
11887  * @cfg {Array} fields An array of field definition objects, or field name strings.
11888  * @constructor
11889  * @param {Object} config
11890  */
11891 Roo.data.JsonStore = function(c){
11892     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11893         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11894         reader: new Roo.data.JsonReader(c, c.fields)
11895     }));
11896 };
11897 Roo.extend(Roo.data.JsonStore, 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 Roo.data.Field = function(config){
11910     if(typeof config == "string"){
11911         config = {name: config};
11912     }
11913     Roo.apply(this, config);
11914     
11915     if(!this.type){
11916         this.type = "auto";
11917     }
11918     
11919     var st = Roo.data.SortTypes;
11920     // named sortTypes are supported, here we look them up
11921     if(typeof this.sortType == "string"){
11922         this.sortType = st[this.sortType];
11923     }
11924     
11925     // set default sortType for strings and dates
11926     if(!this.sortType){
11927         switch(this.type){
11928             case "string":
11929                 this.sortType = st.asUCString;
11930                 break;
11931             case "date":
11932                 this.sortType = st.asDate;
11933                 break;
11934             default:
11935                 this.sortType = st.none;
11936         }
11937     }
11938
11939     // define once
11940     var stripRe = /[\$,%]/g;
11941
11942     // prebuilt conversion function for this field, instead of
11943     // switching every time we're reading a value
11944     if(!this.convert){
11945         var cv, dateFormat = this.dateFormat;
11946         switch(this.type){
11947             case "":
11948             case "auto":
11949             case undefined:
11950                 cv = function(v){ return v; };
11951                 break;
11952             case "string":
11953                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11954                 break;
11955             case "int":
11956                 cv = function(v){
11957                     return v !== undefined && v !== null && v !== '' ?
11958                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11959                     };
11960                 break;
11961             case "float":
11962                 cv = function(v){
11963                     return v !== undefined && v !== null && v !== '' ?
11964                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11965                     };
11966                 break;
11967             case "bool":
11968             case "boolean":
11969                 cv = function(v){ return v === true || v === "true" || v == 1; };
11970                 break;
11971             case "date":
11972                 cv = function(v){
11973                     if(!v){
11974                         return '';
11975                     }
11976                     if(v instanceof Date){
11977                         return v;
11978                     }
11979                     if(dateFormat){
11980                         if(dateFormat == "timestamp"){
11981                             return new Date(v*1000);
11982                         }
11983                         return Date.parseDate(v, dateFormat);
11984                     }
11985                     var parsed = Date.parse(v);
11986                     return parsed ? new Date(parsed) : null;
11987                 };
11988              break;
11989             
11990         }
11991         this.convert = cv;
11992     }
11993 };
11994
11995 Roo.data.Field.prototype = {
11996     dateFormat: null,
11997     defaultValue: "",
11998     mapping: null,
11999     sortType : null,
12000     sortDir : "ASC"
12001 };/*
12002  * Based on:
12003  * Ext JS Library 1.1.1
12004  * Copyright(c) 2006-2007, Ext JS, LLC.
12005  *
12006  * Originally Released Under LGPL - original licence link has changed is not relivant.
12007  *
12008  * Fork - LGPL
12009  * <script type="text/javascript">
12010  */
12011  
12012 // Base class for reading structured data from a data source.  This class is intended to be
12013 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12014
12015 /**
12016  * @class Roo.data.DataReader
12017  * Base class for reading structured data from a data source.  This class is intended to be
12018  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12019  */
12020
12021 Roo.data.DataReader = function(meta, recordType){
12022     
12023     this.meta = meta;
12024     
12025     this.recordType = recordType instanceof Array ? 
12026         Roo.data.Record.create(recordType) : recordType;
12027 };
12028
12029 Roo.data.DataReader.prototype = {
12030      /**
12031      * Create an empty record
12032      * @param {Object} data (optional) - overlay some values
12033      * @return {Roo.data.Record} record created.
12034      */
12035     newRow :  function(d) {
12036         var da =  {};
12037         this.recordType.prototype.fields.each(function(c) {
12038             switch( c.type) {
12039                 case 'int' : da[c.name] = 0; break;
12040                 case 'date' : da[c.name] = new Date(); break;
12041                 case 'float' : da[c.name] = 0.0; break;
12042                 case 'boolean' : da[c.name] = false; break;
12043                 default : da[c.name] = ""; break;
12044             }
12045             
12046         });
12047         return new this.recordType(Roo.apply(da, d));
12048     }
12049     
12050 };/*
12051  * Based on:
12052  * Ext JS Library 1.1.1
12053  * Copyright(c) 2006-2007, Ext JS, LLC.
12054  *
12055  * Originally Released Under LGPL - original licence link has changed is not relivant.
12056  *
12057  * Fork - LGPL
12058  * <script type="text/javascript">
12059  */
12060
12061 /**
12062  * @class Roo.data.DataProxy
12063  * @extends Roo.data.Observable
12064  * This class is an abstract base class for implementations which provide retrieval of
12065  * unformatted data objects.<br>
12066  * <p>
12067  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12068  * (of the appropriate type which knows how to parse the data object) to provide a block of
12069  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12070  * <p>
12071  * Custom implementations must implement the load method as described in
12072  * {@link Roo.data.HttpProxy#load}.
12073  */
12074 Roo.data.DataProxy = function(){
12075     this.addEvents({
12076         /**
12077          * @event beforeload
12078          * Fires before a network request is made to retrieve a data object.
12079          * @param {Object} This DataProxy object.
12080          * @param {Object} params The params parameter to the load function.
12081          */
12082         beforeload : true,
12083         /**
12084          * @event load
12085          * Fires before the load method's callback is called.
12086          * @param {Object} This DataProxy object.
12087          * @param {Object} o The data object.
12088          * @param {Object} arg The callback argument object passed to the load function.
12089          */
12090         load : true,
12091         /**
12092          * @event loadexception
12093          * Fires if an Exception occurs during data retrieval.
12094          * @param {Object} This DataProxy object.
12095          * @param {Object} o The data object.
12096          * @param {Object} arg The callback argument object passed to the load function.
12097          * @param {Object} e The Exception.
12098          */
12099         loadexception : true
12100     });
12101     Roo.data.DataProxy.superclass.constructor.call(this);
12102 };
12103
12104 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12105
12106     /**
12107      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12108      */
12109 /*
12110  * Based on:
12111  * Ext JS Library 1.1.1
12112  * Copyright(c) 2006-2007, Ext JS, LLC.
12113  *
12114  * Originally Released Under LGPL - original licence link has changed is not relivant.
12115  *
12116  * Fork - LGPL
12117  * <script type="text/javascript">
12118  */
12119 /**
12120  * @class Roo.data.MemoryProxy
12121  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12122  * to the Reader when its load method is called.
12123  * @constructor
12124  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12125  */
12126 Roo.data.MemoryProxy = function(data){
12127     if (data.data) {
12128         data = data.data;
12129     }
12130     Roo.data.MemoryProxy.superclass.constructor.call(this);
12131     this.data = data;
12132 };
12133
12134 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12135     
12136     /**
12137      * Load data from the requested source (in this case an in-memory
12138      * data object passed to the constructor), read the data object into
12139      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12140      * process that block using the passed callback.
12141      * @param {Object} params This parameter is not used by the MemoryProxy class.
12142      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12143      * object into a block of Roo.data.Records.
12144      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12145      * The function must be passed <ul>
12146      * <li>The Record block object</li>
12147      * <li>The "arg" argument from the load function</li>
12148      * <li>A boolean success indicator</li>
12149      * </ul>
12150      * @param {Object} scope The scope in which to call the callback
12151      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12152      */
12153     load : function(params, reader, callback, scope, arg){
12154         params = params || {};
12155         var result;
12156         try {
12157             result = reader.readRecords(this.data);
12158         }catch(e){
12159             this.fireEvent("loadexception", this, arg, null, e);
12160             callback.call(scope, null, arg, false);
12161             return;
12162         }
12163         callback.call(scope, result, arg, true);
12164     },
12165     
12166     // private
12167     update : function(params, records){
12168         
12169     }
12170 });/*
12171  * Based on:
12172  * Ext JS Library 1.1.1
12173  * Copyright(c) 2006-2007, Ext JS, LLC.
12174  *
12175  * Originally Released Under LGPL - original licence link has changed is not relivant.
12176  *
12177  * Fork - LGPL
12178  * <script type="text/javascript">
12179  */
12180 /**
12181  * @class Roo.data.HttpProxy
12182  * @extends Roo.data.DataProxy
12183  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12184  * configured to reference a certain URL.<br><br>
12185  * <p>
12186  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12187  * from which the running page was served.<br><br>
12188  * <p>
12189  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12190  * <p>
12191  * Be aware that to enable the browser to parse an XML document, the server must set
12192  * the Content-Type header in the HTTP response to "text/xml".
12193  * @constructor
12194  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12195  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12196  * will be used to make the request.
12197  */
12198 Roo.data.HttpProxy = function(conn){
12199     Roo.data.HttpProxy.superclass.constructor.call(this);
12200     // is conn a conn config or a real conn?
12201     this.conn = conn;
12202     this.useAjax = !conn || !conn.events;
12203   
12204 };
12205
12206 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12207     // thse are take from connection...
12208     
12209     /**
12210      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12211      */
12212     /**
12213      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12214      * extra parameters to each request made by this object. (defaults to undefined)
12215      */
12216     /**
12217      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12218      *  to each request made by this object. (defaults to undefined)
12219      */
12220     /**
12221      * @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)
12222      */
12223     /**
12224      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12225      */
12226      /**
12227      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12228      * @type Boolean
12229      */
12230   
12231
12232     /**
12233      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12234      * @type Boolean
12235      */
12236     /**
12237      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12238      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12239      * a finer-grained basis than the DataProxy events.
12240      */
12241     getConnection : function(){
12242         return this.useAjax ? Roo.Ajax : this.conn;
12243     },
12244
12245     /**
12246      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12247      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12248      * process that block using the passed callback.
12249      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12250      * for the request to the remote server.
12251      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12252      * object into a block of Roo.data.Records.
12253      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12254      * The function must be passed <ul>
12255      * <li>The Record block object</li>
12256      * <li>The "arg" argument from the load function</li>
12257      * <li>A boolean success indicator</li>
12258      * </ul>
12259      * @param {Object} scope The scope in which to call the callback
12260      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12261      */
12262     load : function(params, reader, callback, scope, arg){
12263         if(this.fireEvent("beforeload", this, params) !== false){
12264             var  o = {
12265                 params : params || {},
12266                 request: {
12267                     callback : callback,
12268                     scope : scope,
12269                     arg : arg
12270                 },
12271                 reader: reader,
12272                 callback : this.loadResponse,
12273                 scope: this
12274             };
12275             if(this.useAjax){
12276                 Roo.applyIf(o, this.conn);
12277                 if(this.activeRequest){
12278                     Roo.Ajax.abort(this.activeRequest);
12279                 }
12280                 this.activeRequest = Roo.Ajax.request(o);
12281             }else{
12282                 this.conn.request(o);
12283             }
12284         }else{
12285             callback.call(scope||this, null, arg, false);
12286         }
12287     },
12288
12289     // private
12290     loadResponse : function(o, success, response){
12291         delete this.activeRequest;
12292         if(!success){
12293             this.fireEvent("loadexception", this, o, response);
12294             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12295             return;
12296         }
12297         var result;
12298         try {
12299             result = o.reader.read(response);
12300         }catch(e){
12301             this.fireEvent("loadexception", this, o, response, e);
12302             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12303             return;
12304         }
12305         
12306         this.fireEvent("load", this, o, o.request.arg);
12307         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12308     },
12309
12310     // private
12311     update : function(dataSet){
12312
12313     },
12314
12315     // private
12316     updateResponse : function(dataSet){
12317
12318     }
12319 });/*
12320  * Based on:
12321  * Ext JS Library 1.1.1
12322  * Copyright(c) 2006-2007, Ext JS, LLC.
12323  *
12324  * Originally Released Under LGPL - original licence link has changed is not relivant.
12325  *
12326  * Fork - LGPL
12327  * <script type="text/javascript">
12328  */
12329
12330 /**
12331  * @class Roo.data.ScriptTagProxy
12332  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12333  * other than the originating domain of the running page.<br><br>
12334  * <p>
12335  * <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
12336  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12337  * <p>
12338  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12339  * source code that is used as the source inside a &lt;script> tag.<br><br>
12340  * <p>
12341  * In order for the browser to process the returned data, the server must wrap the data object
12342  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12343  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12344  * depending on whether the callback name was passed:
12345  * <p>
12346  * <pre><code>
12347 boolean scriptTag = false;
12348 String cb = request.getParameter("callback");
12349 if (cb != null) {
12350     scriptTag = true;
12351     response.setContentType("text/javascript");
12352 } else {
12353     response.setContentType("application/x-json");
12354 }
12355 Writer out = response.getWriter();
12356 if (scriptTag) {
12357     out.write(cb + "(");
12358 }
12359 out.print(dataBlock.toJsonString());
12360 if (scriptTag) {
12361     out.write(");");
12362 }
12363 </pre></code>
12364  *
12365  * @constructor
12366  * @param {Object} config A configuration object.
12367  */
12368 Roo.data.ScriptTagProxy = function(config){
12369     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12370     Roo.apply(this, config);
12371     this.head = document.getElementsByTagName("head")[0];
12372 };
12373
12374 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12375
12376 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12377     /**
12378      * @cfg {String} url The URL from which to request the data object.
12379      */
12380     /**
12381      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12382      */
12383     timeout : 30000,
12384     /**
12385      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12386      * the server the name of the callback function set up by the load call to process the returned data object.
12387      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12388      * javascript output which calls this named function passing the data object as its only parameter.
12389      */
12390     callbackParam : "callback",
12391     /**
12392      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12393      * name to the request.
12394      */
12395     nocache : true,
12396
12397     /**
12398      * Load data from the configured URL, read the data object into
12399      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12400      * process that block using the passed callback.
12401      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12402      * for the request to the remote server.
12403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12404      * object into a block of Roo.data.Records.
12405      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12406      * The function must be passed <ul>
12407      * <li>The Record block object</li>
12408      * <li>The "arg" argument from the load function</li>
12409      * <li>A boolean success indicator</li>
12410      * </ul>
12411      * @param {Object} scope The scope in which to call the callback
12412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12413      */
12414     load : function(params, reader, callback, scope, arg){
12415         if(this.fireEvent("beforeload", this, params) !== false){
12416
12417             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12418
12419             var url = this.url;
12420             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12421             if(this.nocache){
12422                 url += "&_dc=" + (new Date().getTime());
12423             }
12424             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12425             var trans = {
12426                 id : transId,
12427                 cb : "stcCallback"+transId,
12428                 scriptId : "stcScript"+transId,
12429                 params : params,
12430                 arg : arg,
12431                 url : url,
12432                 callback : callback,
12433                 scope : scope,
12434                 reader : reader
12435             };
12436             var conn = this;
12437
12438             window[trans.cb] = function(o){
12439                 conn.handleResponse(o, trans);
12440             };
12441
12442             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12443
12444             if(this.autoAbort !== false){
12445                 this.abort();
12446             }
12447
12448             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12449
12450             var script = document.createElement("script");
12451             script.setAttribute("src", url);
12452             script.setAttribute("type", "text/javascript");
12453             script.setAttribute("id", trans.scriptId);
12454             this.head.appendChild(script);
12455
12456             this.trans = trans;
12457         }else{
12458             callback.call(scope||this, null, arg, false);
12459         }
12460     },
12461
12462     // private
12463     isLoading : function(){
12464         return this.trans ? true : false;
12465     },
12466
12467     /**
12468      * Abort the current server request.
12469      */
12470     abort : function(){
12471         if(this.isLoading()){
12472             this.destroyTrans(this.trans);
12473         }
12474     },
12475
12476     // private
12477     destroyTrans : function(trans, isLoaded){
12478         this.head.removeChild(document.getElementById(trans.scriptId));
12479         clearTimeout(trans.timeoutId);
12480         if(isLoaded){
12481             window[trans.cb] = undefined;
12482             try{
12483                 delete window[trans.cb];
12484             }catch(e){}
12485         }else{
12486             // if hasn't been loaded, wait for load to remove it to prevent script error
12487             window[trans.cb] = function(){
12488                 window[trans.cb] = undefined;
12489                 try{
12490                     delete window[trans.cb];
12491                 }catch(e){}
12492             };
12493         }
12494     },
12495
12496     // private
12497     handleResponse : function(o, trans){
12498         this.trans = false;
12499         this.destroyTrans(trans, true);
12500         var result;
12501         try {
12502             result = trans.reader.readRecords(o);
12503         }catch(e){
12504             this.fireEvent("loadexception", this, o, trans.arg, e);
12505             trans.callback.call(trans.scope||window, null, trans.arg, false);
12506             return;
12507         }
12508         this.fireEvent("load", this, o, trans.arg);
12509         trans.callback.call(trans.scope||window, result, trans.arg, true);
12510     },
12511
12512     // private
12513     handleFailure : function(trans){
12514         this.trans = false;
12515         this.destroyTrans(trans, false);
12516         this.fireEvent("loadexception", this, null, trans.arg);
12517         trans.callback.call(trans.scope||window, null, trans.arg, false);
12518     }
12519 });/*
12520  * Based on:
12521  * Ext JS Library 1.1.1
12522  * Copyright(c) 2006-2007, Ext JS, LLC.
12523  *
12524  * Originally Released Under LGPL - original licence link has changed is not relivant.
12525  *
12526  * Fork - LGPL
12527  * <script type="text/javascript">
12528  */
12529
12530 /**
12531  * @class Roo.data.JsonReader
12532  * @extends Roo.data.DataReader
12533  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12534  * based on mappings in a provided Roo.data.Record constructor.
12535  * 
12536  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12537  * in the reply previously. 
12538  * 
12539  * <p>
12540  * Example code:
12541  * <pre><code>
12542 var RecordDef = Roo.data.Record.create([
12543     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12544     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12545 ]);
12546 var myReader = new Roo.data.JsonReader({
12547     totalProperty: "results",    // The property which contains the total dataset size (optional)
12548     root: "rows",                // The property which contains an Array of row objects
12549     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12550 }, RecordDef);
12551 </code></pre>
12552  * <p>
12553  * This would consume a JSON file like this:
12554  * <pre><code>
12555 { 'results': 2, 'rows': [
12556     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12557     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12558 }
12559 </code></pre>
12560  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12561  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12562  * paged from the remote server.
12563  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12564  * @cfg {String} root name of the property which contains the Array of row objects.
12565  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12566  * @cfg {Array} fields Array of field definition objects
12567  * @constructor
12568  * Create a new JsonReader
12569  * @param {Object} meta Metadata configuration options
12570  * @param {Object} recordType Either an Array of field definition objects,
12571  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12572  */
12573 Roo.data.JsonReader = function(meta, recordType){
12574     
12575     meta = meta || {};
12576     // set some defaults:
12577     Roo.applyIf(meta, {
12578         totalProperty: 'total',
12579         successProperty : 'success',
12580         root : 'data',
12581         id : 'id'
12582     });
12583     
12584     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12585 };
12586 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12587     
12588     /**
12589      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12590      * Used by Store query builder to append _requestMeta to params.
12591      * 
12592      */
12593     metaFromRemote : false,
12594     /**
12595      * This method is only used by a DataProxy which has retrieved data from a remote server.
12596      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12597      * @return {Object} data A data block which is used by an Roo.data.Store object as
12598      * a cache of Roo.data.Records.
12599      */
12600     read : function(response){
12601         var json = response.responseText;
12602        
12603         var o = /* eval:var:o */ eval("("+json+")");
12604         if(!o) {
12605             throw {message: "JsonReader.read: Json object not found"};
12606         }
12607         
12608         if(o.metaData){
12609             
12610             delete this.ef;
12611             this.metaFromRemote = true;
12612             this.meta = o.metaData;
12613             this.recordType = Roo.data.Record.create(o.metaData.fields);
12614             this.onMetaChange(this.meta, this.recordType, o);
12615         }
12616         return this.readRecords(o);
12617     },
12618
12619     // private function a store will implement
12620     onMetaChange : function(meta, recordType, o){
12621
12622     },
12623
12624     /**
12625          * @ignore
12626          */
12627     simpleAccess: function(obj, subsc) {
12628         return obj[subsc];
12629     },
12630
12631         /**
12632          * @ignore
12633          */
12634     getJsonAccessor: function(){
12635         var re = /[\[\.]/;
12636         return function(expr) {
12637             try {
12638                 return(re.test(expr))
12639                     ? new Function("obj", "return obj." + expr)
12640                     : function(obj){
12641                         return obj[expr];
12642                     };
12643             } catch(e){}
12644             return Roo.emptyFn;
12645         };
12646     }(),
12647
12648     /**
12649      * Create a data block containing Roo.data.Records from an XML document.
12650      * @param {Object} o An object which contains an Array of row objects in the property specified
12651      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12652      * which contains the total size of the dataset.
12653      * @return {Object} data A data block which is used by an Roo.data.Store object as
12654      * a cache of Roo.data.Records.
12655      */
12656     readRecords : function(o){
12657         /**
12658          * After any data loads, the raw JSON data is available for further custom processing.
12659          * @type Object
12660          */
12661         this.o = o;
12662         var s = this.meta, Record = this.recordType,
12663             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12664
12665 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12666         if (!this.ef) {
12667             if(s.totalProperty) {
12668                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12669                 }
12670                 if(s.successProperty) {
12671                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12672                 }
12673                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12674                 if (s.id) {
12675                         var g = this.getJsonAccessor(s.id);
12676                         this.getId = function(rec) {
12677                                 var r = g(rec);  
12678                                 return (r === undefined || r === "") ? null : r;
12679                         };
12680                 } else {
12681                         this.getId = function(){return null;};
12682                 }
12683             this.ef = [];
12684             for(var jj = 0; jj < fl; jj++){
12685                 f = fi[jj];
12686                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12687                 this.ef[jj] = this.getJsonAccessor(map);
12688             }
12689         }
12690
12691         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12692         if(s.totalProperty){
12693             var vt = parseInt(this.getTotal(o), 10);
12694             if(!isNaN(vt)){
12695                 totalRecords = vt;
12696             }
12697         }
12698         if(s.successProperty){
12699             var vs = this.getSuccess(o);
12700             if(vs === false || vs === 'false'){
12701                 success = false;
12702             }
12703         }
12704         var records = [];
12705         for(var i = 0; i < c; i++){
12706                 var n = root[i];
12707             var values = {};
12708             var id = this.getId(n);
12709             for(var j = 0; j < fl; j++){
12710                 f = fi[j];
12711             var v = this.ef[j](n);
12712             if (!f.convert) {
12713                 Roo.log('missing convert for ' + f.name);
12714                 Roo.log(f);
12715                 continue;
12716             }
12717             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12718             }
12719             var record = new Record(values, id);
12720             record.json = n;
12721             records[i] = record;
12722         }
12723         return {
12724             raw : o,
12725             success : success,
12726             records : records,
12727             totalRecords : totalRecords
12728         };
12729     }
12730 });/*
12731  * Based on:
12732  * Ext JS Library 1.1.1
12733  * Copyright(c) 2006-2007, Ext JS, LLC.
12734  *
12735  * Originally Released Under LGPL - original licence link has changed is not relivant.
12736  *
12737  * Fork - LGPL
12738  * <script type="text/javascript">
12739  */
12740
12741 /**
12742  * @class Roo.data.ArrayReader
12743  * @extends Roo.data.DataReader
12744  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12745  * Each element of that Array represents a row of data fields. The
12746  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12747  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12748  * <p>
12749  * Example code:.
12750  * <pre><code>
12751 var RecordDef = Roo.data.Record.create([
12752     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12753     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12754 ]);
12755 var myReader = new Roo.data.ArrayReader({
12756     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12757 }, RecordDef);
12758 </code></pre>
12759  * <p>
12760  * This would consume an Array like this:
12761  * <pre><code>
12762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12763   </code></pre>
12764  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12765  * @constructor
12766  * Create a new JsonReader
12767  * @param {Object} meta Metadata configuration options.
12768  * @param {Object} recordType Either an Array of field definition objects
12769  * as specified to {@link Roo.data.Record#create},
12770  * or an {@link Roo.data.Record} object
12771  * created using {@link Roo.data.Record#create}.
12772  */
12773 Roo.data.ArrayReader = function(meta, recordType){
12774     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12775 };
12776
12777 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12778     /**
12779      * Create a data block containing Roo.data.Records from an XML document.
12780      * @param {Object} o An Array of row objects which represents the dataset.
12781      * @return {Object} data A data block which is used by an Roo.data.Store object as
12782      * a cache of Roo.data.Records.
12783      */
12784     readRecords : function(o){
12785         var sid = this.meta ? this.meta.id : null;
12786         var recordType = this.recordType, fields = recordType.prototype.fields;
12787         var records = [];
12788         var root = o;
12789             for(var i = 0; i < root.length; i++){
12790                     var n = root[i];
12791                 var values = {};
12792                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12793                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12794                 var f = fields.items[j];
12795                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12796                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12797                 v = f.convert(v);
12798                 values[f.name] = v;
12799             }
12800                 var record = new recordType(values, id);
12801                 record.json = n;
12802                 records[records.length] = record;
12803             }
12804             return {
12805                 records : records,
12806                 totalRecords : records.length
12807             };
12808     }
12809 });/*
12810  * - LGPL
12811  * * 
12812  */
12813
12814 /**
12815  * @class Roo.bootstrap.ComboBox
12816  * @extends Roo.bootstrap.TriggerField
12817  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12818  * @cfg {Boolean} append (true|false) default false
12819  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12820  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12821  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12822  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12823  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12824  * @cfg {Boolean} animate default true
12825  * @cfg {Boolean} emptyResultText only for touch device
12826  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12827  * @cfg {String} emptyTitle default ''
12828  * @constructor
12829  * Create a new ComboBox.
12830  * @param {Object} config Configuration options
12831  */
12832 Roo.bootstrap.ComboBox = function(config){
12833     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12834     this.addEvents({
12835         /**
12836          * @event expand
12837          * Fires when the dropdown list is expanded
12838         * @param {Roo.bootstrap.ComboBox} combo This combo box
12839         */
12840         'expand' : true,
12841         /**
12842          * @event collapse
12843          * Fires when the dropdown list is collapsed
12844         * @param {Roo.bootstrap.ComboBox} combo This combo box
12845         */
12846         'collapse' : true,
12847         /**
12848          * @event beforeselect
12849          * Fires before a list item is selected. Return false to cancel the selection.
12850         * @param {Roo.bootstrap.ComboBox} combo This combo box
12851         * @param {Roo.data.Record} record The data record returned from the underlying store
12852         * @param {Number} index The index of the selected item in the dropdown list
12853         */
12854         'beforeselect' : true,
12855         /**
12856          * @event select
12857          * Fires when a list item is selected
12858         * @param {Roo.bootstrap.ComboBox} combo This combo box
12859         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12860         * @param {Number} index The index of the selected item in the dropdown list
12861         */
12862         'select' : true,
12863         /**
12864          * @event beforequery
12865          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12866          * The event object passed has these properties:
12867         * @param {Roo.bootstrap.ComboBox} combo This combo box
12868         * @param {String} query The query
12869         * @param {Boolean} forceAll true to force "all" query
12870         * @param {Boolean} cancel true to cancel the query
12871         * @param {Object} e The query event object
12872         */
12873         'beforequery': true,
12874          /**
12875          * @event add
12876          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12877         * @param {Roo.bootstrap.ComboBox} combo This combo box
12878         */
12879         'add' : true,
12880         /**
12881          * @event edit
12882          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12883         * @param {Roo.bootstrap.ComboBox} combo This combo box
12884         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12885         */
12886         'edit' : true,
12887         /**
12888          * @event remove
12889          * Fires when the remove value from the combobox array
12890         * @param {Roo.bootstrap.ComboBox} combo This combo box
12891         */
12892         'remove' : true,
12893         /**
12894          * @event afterremove
12895          * Fires when the remove value from the combobox array
12896         * @param {Roo.bootstrap.ComboBox} combo This combo box
12897         */
12898         'afterremove' : true,
12899         /**
12900          * @event specialfilter
12901          * Fires when specialfilter
12902             * @param {Roo.bootstrap.ComboBox} combo This combo box
12903             */
12904         'specialfilter' : true,
12905         /**
12906          * @event tick
12907          * Fires when tick the element
12908             * @param {Roo.bootstrap.ComboBox} combo This combo box
12909             */
12910         'tick' : true,
12911         /**
12912          * @event touchviewdisplay
12913          * Fires when touch view require special display (default is using displayField)
12914             * @param {Roo.bootstrap.ComboBox} combo This combo box
12915             * @param {Object} cfg set html .
12916             */
12917         'touchviewdisplay' : true
12918         
12919     });
12920     
12921     this.item = [];
12922     this.tickItems = [];
12923     
12924     this.selectedIndex = -1;
12925     if(this.mode == 'local'){
12926         if(config.queryDelay === undefined){
12927             this.queryDelay = 10;
12928         }
12929         if(config.minChars === undefined){
12930             this.minChars = 0;
12931         }
12932     }
12933 };
12934
12935 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12936      
12937     /**
12938      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12939      * rendering into an Roo.Editor, defaults to false)
12940      */
12941     /**
12942      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12943      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12944      */
12945     /**
12946      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12947      */
12948     /**
12949      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12950      * the dropdown list (defaults to undefined, with no header element)
12951      */
12952
12953      /**
12954      * @cfg {String/Roo.Template} tpl The template to use to render the output
12955      */
12956      
12957      /**
12958      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12959      */
12960     listWidth: undefined,
12961     /**
12962      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12963      * mode = 'remote' or 'text' if mode = 'local')
12964      */
12965     displayField: undefined,
12966     
12967     /**
12968      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12969      * mode = 'remote' or 'value' if mode = 'local'). 
12970      * Note: use of a valueField requires the user make a selection
12971      * in order for a value to be mapped.
12972      */
12973     valueField: undefined,
12974     /**
12975      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12976      */
12977     modalTitle : '',
12978     
12979     /**
12980      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12981      * field's data value (defaults to the underlying DOM element's name)
12982      */
12983     hiddenName: undefined,
12984     /**
12985      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12986      */
12987     listClass: '',
12988     /**
12989      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12990      */
12991     selectedClass: 'active',
12992     
12993     /**
12994      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12995      */
12996     shadow:'sides',
12997     /**
12998      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12999      * anchor positions (defaults to 'tl-bl')
13000      */
13001     listAlign: 'tl-bl?',
13002     /**
13003      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13004      */
13005     maxHeight: 300,
13006     /**
13007      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13008      * query specified by the allQuery config option (defaults to 'query')
13009      */
13010     triggerAction: 'query',
13011     /**
13012      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13013      * (defaults to 4, does not apply if editable = false)
13014      */
13015     minChars : 4,
13016     /**
13017      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13018      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13019      */
13020     typeAhead: false,
13021     /**
13022      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13023      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13024      */
13025     queryDelay: 500,
13026     /**
13027      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13028      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13029      */
13030     pageSize: 0,
13031     /**
13032      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13033      * when editable = true (defaults to false)
13034      */
13035     selectOnFocus:false,
13036     /**
13037      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13038      */
13039     queryParam: 'query',
13040     /**
13041      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13042      * when mode = 'remote' (defaults to 'Loading...')
13043      */
13044     loadingText: 'Loading...',
13045     /**
13046      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13047      */
13048     resizable: false,
13049     /**
13050      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13051      */
13052     handleHeight : 8,
13053     /**
13054      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13055      * traditional select (defaults to true)
13056      */
13057     editable: true,
13058     /**
13059      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13060      */
13061     allQuery: '',
13062     /**
13063      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13064      */
13065     mode: 'remote',
13066     /**
13067      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13068      * listWidth has a higher value)
13069      */
13070     minListWidth : 70,
13071     /**
13072      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13073      * allow the user to set arbitrary text into the field (defaults to false)
13074      */
13075     forceSelection:false,
13076     /**
13077      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13078      * if typeAhead = true (defaults to 250)
13079      */
13080     typeAheadDelay : 250,
13081     /**
13082      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13083      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13084      */
13085     valueNotFoundText : undefined,
13086     /**
13087      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13088      */
13089     blockFocus : false,
13090     
13091     /**
13092      * @cfg {Boolean} disableClear Disable showing of clear button.
13093      */
13094     disableClear : false,
13095     /**
13096      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13097      */
13098     alwaysQuery : false,
13099     
13100     /**
13101      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13102      */
13103     multiple : false,
13104     
13105     /**
13106      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13107      */
13108     invalidClass : "has-warning",
13109     
13110     /**
13111      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13112      */
13113     validClass : "has-success",
13114     
13115     /**
13116      * @cfg {Boolean} specialFilter (true|false) special filter default false
13117      */
13118     specialFilter : false,
13119     
13120     /**
13121      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13122      */
13123     mobileTouchView : true,
13124     
13125     /**
13126      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13127      */
13128     useNativeIOS : false,
13129     
13130     /**
13131      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13132      */
13133     mobile_restrict_height : false,
13134     
13135     ios_options : false,
13136     
13137     //private
13138     addicon : false,
13139     editicon: false,
13140     
13141     page: 0,
13142     hasQuery: false,
13143     append: false,
13144     loadNext: false,
13145     autoFocus : true,
13146     tickable : false,
13147     btnPosition : 'right',
13148     triggerList : true,
13149     showToggleBtn : true,
13150     animate : true,
13151     emptyResultText: 'Empty',
13152     triggerText : 'Select',
13153     emptyTitle : '',
13154     
13155     // element that contains real text value.. (when hidden is used..)
13156     
13157     getAutoCreate : function()
13158     {   
13159         var cfg = false;
13160         //render
13161         /*
13162          * Render classic select for iso
13163          */
13164         
13165         if(Roo.isIOS && this.useNativeIOS){
13166             cfg = this.getAutoCreateNativeIOS();
13167             return cfg;
13168         }
13169         
13170         /*
13171          * Touch Devices
13172          */
13173         
13174         if(Roo.isTouch && this.mobileTouchView){
13175             cfg = this.getAutoCreateTouchView();
13176             return cfg;;
13177         }
13178         
13179         /*
13180          *  Normal ComboBox
13181          */
13182         if(!this.tickable){
13183             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13184             return cfg;
13185         }
13186         
13187         /*
13188          *  ComboBox with tickable selections
13189          */
13190              
13191         var align = this.labelAlign || this.parentLabelAlign();
13192         
13193         cfg = {
13194             cls : 'form-group roo-combobox-tickable' //input-group
13195         };
13196         
13197         var btn_text_select = '';
13198         var btn_text_done = '';
13199         var btn_text_cancel = '';
13200         
13201         if (this.btn_text_show) {
13202             btn_text_select = 'Select';
13203             btn_text_done = 'Done';
13204             btn_text_cancel = 'Cancel'; 
13205         }
13206         
13207         var buttons = {
13208             tag : 'div',
13209             cls : 'tickable-buttons',
13210             cn : [
13211                 {
13212                     tag : 'button',
13213                     type : 'button',
13214                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13215                     //html : this.triggerText
13216                     html: btn_text_select
13217                 },
13218                 {
13219                     tag : 'button',
13220                     type : 'button',
13221                     name : 'ok',
13222                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13223                     //html : 'Done'
13224                     html: btn_text_done
13225                 },
13226                 {
13227                     tag : 'button',
13228                     type : 'button',
13229                     name : 'cancel',
13230                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13231                     //html : 'Cancel'
13232                     html: btn_text_cancel
13233                 }
13234             ]
13235         };
13236         
13237         if(this.editable){
13238             buttons.cn.unshift({
13239                 tag: 'input',
13240                 cls: 'roo-select2-search-field-input'
13241             });
13242         }
13243         
13244         var _this = this;
13245         
13246         Roo.each(buttons.cn, function(c){
13247             if (_this.size) {
13248                 c.cls += ' btn-' + _this.size;
13249             }
13250
13251             if (_this.disabled) {
13252                 c.disabled = true;
13253             }
13254         });
13255         
13256         var box = {
13257             tag: 'div',
13258             cn: [
13259                 {
13260                     tag: 'input',
13261                     type : 'hidden',
13262                     cls: 'form-hidden-field'
13263                 },
13264                 {
13265                     tag: 'ul',
13266                     cls: 'roo-select2-choices',
13267                     cn:[
13268                         {
13269                             tag: 'li',
13270                             cls: 'roo-select2-search-field',
13271                             cn: [
13272                                 buttons
13273                             ]
13274                         }
13275                     ]
13276                 }
13277             ]
13278         };
13279         
13280         var combobox = {
13281             cls: 'roo-select2-container input-group roo-select2-container-multi',
13282             cn: [
13283                 box
13284 //                {
13285 //                    tag: 'ul',
13286 //                    cls: 'typeahead typeahead-long dropdown-menu',
13287 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13288 //                }
13289             ]
13290         };
13291         
13292         if(this.hasFeedback && !this.allowBlank){
13293             
13294             var feedback = {
13295                 tag: 'span',
13296                 cls: 'glyphicon form-control-feedback'
13297             };
13298
13299             combobox.cn.push(feedback);
13300         }
13301         
13302         
13303         if (align ==='left' && this.fieldLabel.length) {
13304             
13305             cfg.cls += ' roo-form-group-label-left';
13306             
13307             cfg.cn = [
13308                 {
13309                     tag : 'i',
13310                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13311                     tooltip : 'This field is required'
13312                 },
13313                 {
13314                     tag: 'label',
13315                     'for' :  id,
13316                     cls : 'control-label',
13317                     html : this.fieldLabel
13318
13319                 },
13320                 {
13321                     cls : "", 
13322                     cn: [
13323                         combobox
13324                     ]
13325                 }
13326
13327             ];
13328             
13329             var labelCfg = cfg.cn[1];
13330             var contentCfg = cfg.cn[2];
13331             
13332
13333             if(this.indicatorpos == 'right'){
13334                 
13335                 cfg.cn = [
13336                     {
13337                         tag: 'label',
13338                         'for' :  id,
13339                         cls : 'control-label',
13340                         cn : [
13341                             {
13342                                 tag : 'span',
13343                                 html : this.fieldLabel
13344                             },
13345                             {
13346                                 tag : 'i',
13347                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13348                                 tooltip : 'This field is required'
13349                             }
13350                         ]
13351                     },
13352                     {
13353                         cls : "",
13354                         cn: [
13355                             combobox
13356                         ]
13357                     }
13358
13359                 ];
13360                 
13361                 
13362                 
13363                 labelCfg = cfg.cn[0];
13364                 contentCfg = cfg.cn[1];
13365             
13366             }
13367             
13368             if(this.labelWidth > 12){
13369                 labelCfg.style = "width: " + this.labelWidth + 'px';
13370             }
13371             
13372             if(this.labelWidth < 13 && this.labelmd == 0){
13373                 this.labelmd = this.labelWidth;
13374             }
13375             
13376             if(this.labellg > 0){
13377                 labelCfg.cls += ' col-lg-' + this.labellg;
13378                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13379             }
13380             
13381             if(this.labelmd > 0){
13382                 labelCfg.cls += ' col-md-' + this.labelmd;
13383                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13384             }
13385             
13386             if(this.labelsm > 0){
13387                 labelCfg.cls += ' col-sm-' + this.labelsm;
13388                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13389             }
13390             
13391             if(this.labelxs > 0){
13392                 labelCfg.cls += ' col-xs-' + this.labelxs;
13393                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13394             }
13395                 
13396                 
13397         } else if ( this.fieldLabel.length) {
13398 //                Roo.log(" label");
13399                  cfg.cn = [
13400                     {
13401                         tag : 'i',
13402                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13403                         tooltip : 'This field is required'
13404                     },
13405                     {
13406                         tag: 'label',
13407                         //cls : 'input-group-addon',
13408                         html : this.fieldLabel
13409                     },
13410                     combobox
13411                 ];
13412                 
13413                 if(this.indicatorpos == 'right'){
13414                     cfg.cn = [
13415                         {
13416                             tag: 'label',
13417                             //cls : 'input-group-addon',
13418                             html : this.fieldLabel
13419                         },
13420                         {
13421                             tag : 'i',
13422                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13423                             tooltip : 'This field is required'
13424                         },
13425                         combobox
13426                     ];
13427                     
13428                 }
13429
13430         } else {
13431             
13432 //                Roo.log(" no label && no align");
13433                 cfg = combobox
13434                      
13435                 
13436         }
13437          
13438         var settings=this;
13439         ['xs','sm','md','lg'].map(function(size){
13440             if (settings[size]) {
13441                 cfg.cls += ' col-' + size + '-' + settings[size];
13442             }
13443         });
13444         
13445         return cfg;
13446         
13447     },
13448     
13449     _initEventsCalled : false,
13450     
13451     // private
13452     initEvents: function()
13453     {   
13454         if (this._initEventsCalled) { // as we call render... prevent looping...
13455             return;
13456         }
13457         this._initEventsCalled = true;
13458         
13459         if (!this.store) {
13460             throw "can not find store for combo";
13461         }
13462         
13463         this.indicator = this.indicatorEl();
13464         
13465         this.store = Roo.factory(this.store, Roo.data);
13466         this.store.parent = this;
13467         
13468         // if we are building from html. then this element is so complex, that we can not really
13469         // use the rendered HTML.
13470         // so we have to trash and replace the previous code.
13471         if (Roo.XComponent.build_from_html) {
13472             // remove this element....
13473             var e = this.el.dom, k=0;
13474             while (e ) { e = e.previousSibling;  ++k;}
13475
13476             this.el.remove();
13477             
13478             this.el=false;
13479             this.rendered = false;
13480             
13481             this.render(this.parent().getChildContainer(true), k);
13482         }
13483         
13484         if(Roo.isIOS && this.useNativeIOS){
13485             this.initIOSView();
13486             return;
13487         }
13488         
13489         /*
13490          * Touch Devices
13491          */
13492         
13493         if(Roo.isTouch && this.mobileTouchView){
13494             this.initTouchView();
13495             return;
13496         }
13497         
13498         if(this.tickable){
13499             this.initTickableEvents();
13500             return;
13501         }
13502         
13503         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13504         
13505         if(this.hiddenName){
13506             
13507             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13508             
13509             this.hiddenField.dom.value =
13510                 this.hiddenValue !== undefined ? this.hiddenValue :
13511                 this.value !== undefined ? this.value : '';
13512
13513             // prevent input submission
13514             this.el.dom.removeAttribute('name');
13515             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13516              
13517              
13518         }
13519         //if(Roo.isGecko){
13520         //    this.el.dom.setAttribute('autocomplete', 'off');
13521         //}
13522         
13523         var cls = 'x-combo-list';
13524         
13525         //this.list = new Roo.Layer({
13526         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13527         //});
13528         
13529         var _this = this;
13530         
13531         (function(){
13532             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13533             _this.list.setWidth(lw);
13534         }).defer(100);
13535         
13536         this.list.on('mouseover', this.onViewOver, this);
13537         this.list.on('mousemove', this.onViewMove, this);
13538         this.list.on('scroll', this.onViewScroll, this);
13539         
13540         /*
13541         this.list.swallowEvent('mousewheel');
13542         this.assetHeight = 0;
13543
13544         if(this.title){
13545             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13546             this.assetHeight += this.header.getHeight();
13547         }
13548
13549         this.innerList = this.list.createChild({cls:cls+'-inner'});
13550         this.innerList.on('mouseover', this.onViewOver, this);
13551         this.innerList.on('mousemove', this.onViewMove, this);
13552         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13553         
13554         if(this.allowBlank && !this.pageSize && !this.disableClear){
13555             this.footer = this.list.createChild({cls:cls+'-ft'});
13556             this.pageTb = new Roo.Toolbar(this.footer);
13557            
13558         }
13559         if(this.pageSize){
13560             this.footer = this.list.createChild({cls:cls+'-ft'});
13561             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13562                     {pageSize: this.pageSize});
13563             
13564         }
13565         
13566         if (this.pageTb && this.allowBlank && !this.disableClear) {
13567             var _this = this;
13568             this.pageTb.add(new Roo.Toolbar.Fill(), {
13569                 cls: 'x-btn-icon x-btn-clear',
13570                 text: '&#160;',
13571                 handler: function()
13572                 {
13573                     _this.collapse();
13574                     _this.clearValue();
13575                     _this.onSelect(false, -1);
13576                 }
13577             });
13578         }
13579         if (this.footer) {
13580             this.assetHeight += this.footer.getHeight();
13581         }
13582         */
13583             
13584         if(!this.tpl){
13585             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13586         }
13587
13588         this.view = new Roo.View(this.list, this.tpl, {
13589             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13590         });
13591         //this.view.wrapEl.setDisplayed(false);
13592         this.view.on('click', this.onViewClick, this);
13593         
13594         
13595         this.store.on('beforeload', this.onBeforeLoad, this);
13596         this.store.on('load', this.onLoad, this);
13597         this.store.on('loadexception', this.onLoadException, this);
13598         /*
13599         if(this.resizable){
13600             this.resizer = new Roo.Resizable(this.list,  {
13601                pinned:true, handles:'se'
13602             });
13603             this.resizer.on('resize', function(r, w, h){
13604                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13605                 this.listWidth = w;
13606                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13607                 this.restrictHeight();
13608             }, this);
13609             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13610         }
13611         */
13612         if(!this.editable){
13613             this.editable = true;
13614             this.setEditable(false);
13615         }
13616         
13617         /*
13618         
13619         if (typeof(this.events.add.listeners) != 'undefined') {
13620             
13621             this.addicon = this.wrap.createChild(
13622                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13623        
13624             this.addicon.on('click', function(e) {
13625                 this.fireEvent('add', this);
13626             }, this);
13627         }
13628         if (typeof(this.events.edit.listeners) != 'undefined') {
13629             
13630             this.editicon = this.wrap.createChild(
13631                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13632             if (this.addicon) {
13633                 this.editicon.setStyle('margin-left', '40px');
13634             }
13635             this.editicon.on('click', function(e) {
13636                 
13637                 // we fire even  if inothing is selected..
13638                 this.fireEvent('edit', this, this.lastData );
13639                 
13640             }, this);
13641         }
13642         */
13643         
13644         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13645             "up" : function(e){
13646                 this.inKeyMode = true;
13647                 this.selectPrev();
13648             },
13649
13650             "down" : function(e){
13651                 if(!this.isExpanded()){
13652                     this.onTriggerClick();
13653                 }else{
13654                     this.inKeyMode = true;
13655                     this.selectNext();
13656                 }
13657             },
13658
13659             "enter" : function(e){
13660 //                this.onViewClick();
13661                 //return true;
13662                 this.collapse();
13663                 
13664                 if(this.fireEvent("specialkey", this, e)){
13665                     this.onViewClick(false);
13666                 }
13667                 
13668                 return true;
13669             },
13670
13671             "esc" : function(e){
13672                 this.collapse();
13673             },
13674
13675             "tab" : function(e){
13676                 this.collapse();
13677                 
13678                 if(this.fireEvent("specialkey", this, e)){
13679                     this.onViewClick(false);
13680                 }
13681                 
13682                 return true;
13683             },
13684
13685             scope : this,
13686
13687             doRelay : function(foo, bar, hname){
13688                 if(hname == 'down' || this.scope.isExpanded()){
13689                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13690                 }
13691                 return true;
13692             },
13693
13694             forceKeyDown: true
13695         });
13696         
13697         
13698         this.queryDelay = Math.max(this.queryDelay || 10,
13699                 this.mode == 'local' ? 10 : 250);
13700         
13701         
13702         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13703         
13704         if(this.typeAhead){
13705             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13706         }
13707         if(this.editable !== false){
13708             this.inputEl().on("keyup", this.onKeyUp, this);
13709         }
13710         if(this.forceSelection){
13711             this.inputEl().on('blur', this.doForce, this);
13712         }
13713         
13714         if(this.multiple){
13715             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13716             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13717         }
13718     },
13719     
13720     initTickableEvents: function()
13721     {   
13722         this.createList();
13723         
13724         if(this.hiddenName){
13725             
13726             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13727             
13728             this.hiddenField.dom.value =
13729                 this.hiddenValue !== undefined ? this.hiddenValue :
13730                 this.value !== undefined ? this.value : '';
13731
13732             // prevent input submission
13733             this.el.dom.removeAttribute('name');
13734             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13735              
13736              
13737         }
13738         
13739 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13740         
13741         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13742         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13743         if(this.triggerList){
13744             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13745         }
13746          
13747         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13748         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13749         
13750         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13751         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13752         
13753         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13754         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13755         
13756         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13757         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13758         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13759         
13760         this.okBtn.hide();
13761         this.cancelBtn.hide();
13762         
13763         var _this = this;
13764         
13765         (function(){
13766             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13767             _this.list.setWidth(lw);
13768         }).defer(100);
13769         
13770         this.list.on('mouseover', this.onViewOver, this);
13771         this.list.on('mousemove', this.onViewMove, this);
13772         
13773         this.list.on('scroll', this.onViewScroll, this);
13774         
13775         if(!this.tpl){
13776             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13777                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13778         }
13779
13780         this.view = new Roo.View(this.list, this.tpl, {
13781             singleSelect:true,
13782             tickable:true,
13783             parent:this,
13784             store: this.store,
13785             selectedClass: this.selectedClass
13786         });
13787         
13788         //this.view.wrapEl.setDisplayed(false);
13789         this.view.on('click', this.onViewClick, this);
13790         
13791         
13792         
13793         this.store.on('beforeload', this.onBeforeLoad, this);
13794         this.store.on('load', this.onLoad, this);
13795         this.store.on('loadexception', this.onLoadException, this);
13796         
13797         if(this.editable){
13798             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13799                 "up" : function(e){
13800                     this.inKeyMode = true;
13801                     this.selectPrev();
13802                 },
13803
13804                 "down" : function(e){
13805                     this.inKeyMode = true;
13806                     this.selectNext();
13807                 },
13808
13809                 "enter" : function(e){
13810                     if(this.fireEvent("specialkey", this, e)){
13811                         this.onViewClick(false);
13812                     }
13813                     
13814                     return true;
13815                 },
13816
13817                 "esc" : function(e){
13818                     this.onTickableFooterButtonClick(e, false, false);
13819                 },
13820
13821                 "tab" : function(e){
13822                     this.fireEvent("specialkey", this, e);
13823                     
13824                     this.onTickableFooterButtonClick(e, false, false);
13825                     
13826                     return true;
13827                 },
13828
13829                 scope : this,
13830
13831                 doRelay : function(e, fn, key){
13832                     if(this.scope.isExpanded()){
13833                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13834                     }
13835                     return true;
13836                 },
13837
13838                 forceKeyDown: true
13839             });
13840         }
13841         
13842         this.queryDelay = Math.max(this.queryDelay || 10,
13843                 this.mode == 'local' ? 10 : 250);
13844         
13845         
13846         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13847         
13848         if(this.typeAhead){
13849             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13850         }
13851         
13852         if(this.editable !== false){
13853             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13854         }
13855         
13856         this.indicator = this.indicatorEl();
13857         
13858         if(this.indicator){
13859             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13860             this.indicator.hide();
13861         }
13862         
13863     },
13864
13865     onDestroy : function(){
13866         if(this.view){
13867             this.view.setStore(null);
13868             this.view.el.removeAllListeners();
13869             this.view.el.remove();
13870             this.view.purgeListeners();
13871         }
13872         if(this.list){
13873             this.list.dom.innerHTML  = '';
13874         }
13875         
13876         if(this.store){
13877             this.store.un('beforeload', this.onBeforeLoad, this);
13878             this.store.un('load', this.onLoad, this);
13879             this.store.un('loadexception', this.onLoadException, this);
13880         }
13881         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13882     },
13883
13884     // private
13885     fireKey : function(e){
13886         if(e.isNavKeyPress() && !this.list.isVisible()){
13887             this.fireEvent("specialkey", this, e);
13888         }
13889     },
13890
13891     // private
13892     onResize: function(w, h){
13893 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13894 //        
13895 //        if(typeof w != 'number'){
13896 //            // we do not handle it!?!?
13897 //            return;
13898 //        }
13899 //        var tw = this.trigger.getWidth();
13900 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13901 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13902 //        var x = w - tw;
13903 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13904 //            
13905 //        //this.trigger.setStyle('left', x+'px');
13906 //        
13907 //        if(this.list && this.listWidth === undefined){
13908 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13909 //            this.list.setWidth(lw);
13910 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13911 //        }
13912         
13913     
13914         
13915     },
13916
13917     /**
13918      * Allow or prevent the user from directly editing the field text.  If false is passed,
13919      * the user will only be able to select from the items defined in the dropdown list.  This method
13920      * is the runtime equivalent of setting the 'editable' config option at config time.
13921      * @param {Boolean} value True to allow the user to directly edit the field text
13922      */
13923     setEditable : function(value){
13924         if(value == this.editable){
13925             return;
13926         }
13927         this.editable = value;
13928         if(!value){
13929             this.inputEl().dom.setAttribute('readOnly', true);
13930             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13931             this.inputEl().addClass('x-combo-noedit');
13932         }else{
13933             this.inputEl().dom.setAttribute('readOnly', false);
13934             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13935             this.inputEl().removeClass('x-combo-noedit');
13936         }
13937     },
13938
13939     // private
13940     
13941     onBeforeLoad : function(combo,opts){
13942         if(!this.hasFocus){
13943             return;
13944         }
13945          if (!opts.add) {
13946             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13947          }
13948         this.restrictHeight();
13949         this.selectedIndex = -1;
13950     },
13951
13952     // private
13953     onLoad : function(){
13954         
13955         this.hasQuery = false;
13956         
13957         if(!this.hasFocus){
13958             return;
13959         }
13960         
13961         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13962             this.loading.hide();
13963         }
13964         
13965         if(this.store.getCount() > 0){
13966             
13967             this.expand();
13968             this.restrictHeight();
13969             if(this.lastQuery == this.allQuery){
13970                 if(this.editable && !this.tickable){
13971                     this.inputEl().dom.select();
13972                 }
13973                 
13974                 if(
13975                     !this.selectByValue(this.value, true) &&
13976                     this.autoFocus && 
13977                     (
13978                         !this.store.lastOptions ||
13979                         typeof(this.store.lastOptions.add) == 'undefined' || 
13980                         this.store.lastOptions.add != true
13981                     )
13982                 ){
13983                     this.select(0, true);
13984                 }
13985             }else{
13986                 if(this.autoFocus){
13987                     this.selectNext();
13988                 }
13989                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13990                     this.taTask.delay(this.typeAheadDelay);
13991                 }
13992             }
13993         }else{
13994             this.onEmptyResults();
13995         }
13996         
13997         //this.el.focus();
13998     },
13999     // private
14000     onLoadException : function()
14001     {
14002         this.hasQuery = false;
14003         
14004         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14005             this.loading.hide();
14006         }
14007         
14008         if(this.tickable && this.editable){
14009             return;
14010         }
14011         
14012         this.collapse();
14013         // only causes errors at present
14014         //Roo.log(this.store.reader.jsonData);
14015         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14016             // fixme
14017             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14018         //}
14019         
14020         
14021     },
14022     // private
14023     onTypeAhead : function(){
14024         if(this.store.getCount() > 0){
14025             var r = this.store.getAt(0);
14026             var newValue = r.data[this.displayField];
14027             var len = newValue.length;
14028             var selStart = this.getRawValue().length;
14029             
14030             if(selStart != len){
14031                 this.setRawValue(newValue);
14032                 this.selectText(selStart, newValue.length);
14033             }
14034         }
14035     },
14036
14037     // private
14038     onSelect : function(record, index){
14039         
14040         if(this.fireEvent('beforeselect', this, record, index) !== false){
14041         
14042             this.setFromData(index > -1 ? record.data : false);
14043             
14044             this.collapse();
14045             this.fireEvent('select', this, record, index);
14046         }
14047     },
14048
14049     /**
14050      * Returns the currently selected field value or empty string if no value is set.
14051      * @return {String} value The selected value
14052      */
14053     getValue : function()
14054     {
14055         if(Roo.isIOS && this.useNativeIOS){
14056             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14057         }
14058         
14059         if(this.multiple){
14060             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14061         }
14062         
14063         if(this.valueField){
14064             return typeof this.value != 'undefined' ? this.value : '';
14065         }else{
14066             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14067         }
14068     },
14069     
14070     getRawValue : function()
14071     {
14072         if(Roo.isIOS && this.useNativeIOS){
14073             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14074         }
14075         
14076         var v = this.inputEl().getValue();
14077         
14078         return v;
14079     },
14080
14081     /**
14082      * Clears any text/value currently set in the field
14083      */
14084     clearValue : function(){
14085         
14086         if(this.hiddenField){
14087             this.hiddenField.dom.value = '';
14088         }
14089         this.value = '';
14090         this.setRawValue('');
14091         this.lastSelectionText = '';
14092         this.lastData = false;
14093         
14094         var close = this.closeTriggerEl();
14095         
14096         if(close){
14097             close.hide();
14098         }
14099         
14100         this.validate();
14101         
14102     },
14103
14104     /**
14105      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14106      * will be displayed in the field.  If the value does not match the data value of an existing item,
14107      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14108      * Otherwise the field will be blank (although the value will still be set).
14109      * @param {String} value The value to match
14110      */
14111     setValue : function(v)
14112     {
14113         if(Roo.isIOS && this.useNativeIOS){
14114             this.setIOSValue(v);
14115             return;
14116         }
14117         
14118         if(this.multiple){
14119             this.syncValue();
14120             return;
14121         }
14122         
14123         var text = v;
14124         if(this.valueField){
14125             var r = this.findRecord(this.valueField, v);
14126             if(r){
14127                 text = r.data[this.displayField];
14128             }else if(this.valueNotFoundText !== undefined){
14129                 text = this.valueNotFoundText;
14130             }
14131         }
14132         this.lastSelectionText = text;
14133         if(this.hiddenField){
14134             this.hiddenField.dom.value = v;
14135         }
14136         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14137         this.value = v;
14138         
14139         var close = this.closeTriggerEl();
14140         
14141         if(close){
14142             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14143         }
14144         
14145         this.validate();
14146     },
14147     /**
14148      * @property {Object} the last set data for the element
14149      */
14150     
14151     lastData : false,
14152     /**
14153      * Sets the value of the field based on a object which is related to the record format for the store.
14154      * @param {Object} value the value to set as. or false on reset?
14155      */
14156     setFromData : function(o){
14157         
14158         if(this.multiple){
14159             this.addItem(o);
14160             return;
14161         }
14162             
14163         var dv = ''; // display value
14164         var vv = ''; // value value..
14165         this.lastData = o;
14166         if (this.displayField) {
14167             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14168         } else {
14169             // this is an error condition!!!
14170             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14171         }
14172         
14173         if(this.valueField){
14174             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14175         }
14176         
14177         var close = this.closeTriggerEl();
14178         
14179         if(close){
14180             if(dv.length || vv * 1 > 0){
14181                 close.show() ;
14182                 this.blockFocus=true;
14183             } else {
14184                 close.hide();
14185             }             
14186         }
14187         
14188         if(this.hiddenField){
14189             this.hiddenField.dom.value = vv;
14190             
14191             this.lastSelectionText = dv;
14192             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14193             this.value = vv;
14194             return;
14195         }
14196         // no hidden field.. - we store the value in 'value', but still display
14197         // display field!!!!
14198         this.lastSelectionText = dv;
14199         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14200         this.value = vv;
14201         
14202         
14203         
14204     },
14205     // private
14206     reset : function(){
14207         // overridden so that last data is reset..
14208         
14209         if(this.multiple){
14210             this.clearItem();
14211             return;
14212         }
14213         
14214         this.setValue(this.originalValue);
14215         //this.clearInvalid();
14216         this.lastData = false;
14217         if (this.view) {
14218             this.view.clearSelections();
14219         }
14220         
14221         this.validate();
14222     },
14223     // private
14224     findRecord : function(prop, value){
14225         var record;
14226         if(this.store.getCount() > 0){
14227             this.store.each(function(r){
14228                 if(r.data[prop] == value){
14229                     record = r;
14230                     return false;
14231                 }
14232                 return true;
14233             });
14234         }
14235         return record;
14236     },
14237     
14238     getName: function()
14239     {
14240         // returns hidden if it's set..
14241         if (!this.rendered) {return ''};
14242         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14243         
14244     },
14245     // private
14246     onViewMove : function(e, t){
14247         this.inKeyMode = false;
14248     },
14249
14250     // private
14251     onViewOver : function(e, t){
14252         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14253             return;
14254         }
14255         var item = this.view.findItemFromChild(t);
14256         
14257         if(item){
14258             var index = this.view.indexOf(item);
14259             this.select(index, false);
14260         }
14261     },
14262
14263     // private
14264     onViewClick : function(view, doFocus, el, e)
14265     {
14266         var index = this.view.getSelectedIndexes()[0];
14267         
14268         var r = this.store.getAt(index);
14269         
14270         if(this.tickable){
14271             
14272             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14273                 return;
14274             }
14275             
14276             var rm = false;
14277             var _this = this;
14278             
14279             Roo.each(this.tickItems, function(v,k){
14280                 
14281                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14282                     Roo.log(v);
14283                     _this.tickItems.splice(k, 1);
14284                     
14285                     if(typeof(e) == 'undefined' && view == false){
14286                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14287                     }
14288                     
14289                     rm = true;
14290                     return;
14291                 }
14292             });
14293             
14294             if(rm){
14295                 return;
14296             }
14297             
14298             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14299                 this.tickItems.push(r.data);
14300             }
14301             
14302             if(typeof(e) == 'undefined' && view == false){
14303                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14304             }
14305                     
14306             return;
14307         }
14308         
14309         if(r){
14310             this.onSelect(r, index);
14311         }
14312         if(doFocus !== false && !this.blockFocus){
14313             this.inputEl().focus();
14314         }
14315     },
14316
14317     // private
14318     restrictHeight : function(){
14319         //this.innerList.dom.style.height = '';
14320         //var inner = this.innerList.dom;
14321         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14322         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14323         //this.list.beginUpdate();
14324         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14325         this.list.alignTo(this.inputEl(), this.listAlign);
14326         this.list.alignTo(this.inputEl(), this.listAlign);
14327         //this.list.endUpdate();
14328     },
14329
14330     // private
14331     onEmptyResults : function(){
14332         
14333         if(this.tickable && this.editable){
14334             this.hasFocus = false;
14335             this.restrictHeight();
14336             return;
14337         }
14338         
14339         this.collapse();
14340     },
14341
14342     /**
14343      * Returns true if the dropdown list is expanded, else false.
14344      */
14345     isExpanded : function(){
14346         return this.list.isVisible();
14347     },
14348
14349     /**
14350      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14351      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14352      * @param {String} value The data value of the item to select
14353      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14354      * selected item if it is not currently in view (defaults to true)
14355      * @return {Boolean} True if the value matched an item in the list, else false
14356      */
14357     selectByValue : function(v, scrollIntoView){
14358         if(v !== undefined && v !== null){
14359             var r = this.findRecord(this.valueField || this.displayField, v);
14360             if(r){
14361                 this.select(this.store.indexOf(r), scrollIntoView);
14362                 return true;
14363             }
14364         }
14365         return false;
14366     },
14367
14368     /**
14369      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14370      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14371      * @param {Number} index The zero-based index of the list item to select
14372      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14373      * selected item if it is not currently in view (defaults to true)
14374      */
14375     select : function(index, scrollIntoView){
14376         this.selectedIndex = index;
14377         this.view.select(index);
14378         if(scrollIntoView !== false){
14379             var el = this.view.getNode(index);
14380             /*
14381              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14382              */
14383             if(el){
14384                 this.list.scrollChildIntoView(el, false);
14385             }
14386         }
14387     },
14388
14389     // private
14390     selectNext : function(){
14391         var ct = this.store.getCount();
14392         if(ct > 0){
14393             if(this.selectedIndex == -1){
14394                 this.select(0);
14395             }else if(this.selectedIndex < ct-1){
14396                 this.select(this.selectedIndex+1);
14397             }
14398         }
14399     },
14400
14401     // private
14402     selectPrev : function(){
14403         var ct = this.store.getCount();
14404         if(ct > 0){
14405             if(this.selectedIndex == -1){
14406                 this.select(0);
14407             }else if(this.selectedIndex != 0){
14408                 this.select(this.selectedIndex-1);
14409             }
14410         }
14411     },
14412
14413     // private
14414     onKeyUp : function(e){
14415         if(this.editable !== false && !e.isSpecialKey()){
14416             this.lastKey = e.getKey();
14417             this.dqTask.delay(this.queryDelay);
14418         }
14419     },
14420
14421     // private
14422     validateBlur : function(){
14423         return !this.list || !this.list.isVisible();   
14424     },
14425
14426     // private
14427     initQuery : function(){
14428         
14429         var v = this.getRawValue();
14430         
14431         if(this.tickable && this.editable){
14432             v = this.tickableInputEl().getValue();
14433         }
14434         
14435         this.doQuery(v);
14436     },
14437
14438     // private
14439     doForce : function(){
14440         if(this.inputEl().dom.value.length > 0){
14441             this.inputEl().dom.value =
14442                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14443              
14444         }
14445     },
14446
14447     /**
14448      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14449      * query allowing the query action to be canceled if needed.
14450      * @param {String} query The SQL query to execute
14451      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14452      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14453      * saved in the current store (defaults to false)
14454      */
14455     doQuery : function(q, forceAll){
14456         
14457         if(q === undefined || q === null){
14458             q = '';
14459         }
14460         var qe = {
14461             query: q,
14462             forceAll: forceAll,
14463             combo: this,
14464             cancel:false
14465         };
14466         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14467             return false;
14468         }
14469         q = qe.query;
14470         
14471         forceAll = qe.forceAll;
14472         if(forceAll === true || (q.length >= this.minChars)){
14473             
14474             this.hasQuery = true;
14475             
14476             if(this.lastQuery != q || this.alwaysQuery){
14477                 this.lastQuery = q;
14478                 if(this.mode == 'local'){
14479                     this.selectedIndex = -1;
14480                     if(forceAll){
14481                         this.store.clearFilter();
14482                     }else{
14483                         
14484                         if(this.specialFilter){
14485                             this.fireEvent('specialfilter', this);
14486                             this.onLoad();
14487                             return;
14488                         }
14489                         
14490                         this.store.filter(this.displayField, q);
14491                     }
14492                     
14493                     this.store.fireEvent("datachanged", this.store);
14494                     
14495                     this.onLoad();
14496                     
14497                     
14498                 }else{
14499                     
14500                     this.store.baseParams[this.queryParam] = q;
14501                     
14502                     var options = {params : this.getParams(q)};
14503                     
14504                     if(this.loadNext){
14505                         options.add = true;
14506                         options.params.start = this.page * this.pageSize;
14507                     }
14508                     
14509                     this.store.load(options);
14510                     
14511                     /*
14512                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14513                      *  we should expand the list on onLoad
14514                      *  so command out it
14515                      */
14516 //                    this.expand();
14517                 }
14518             }else{
14519                 this.selectedIndex = -1;
14520                 this.onLoad();   
14521             }
14522         }
14523         
14524         this.loadNext = false;
14525     },
14526     
14527     // private
14528     getParams : function(q){
14529         var p = {};
14530         //p[this.queryParam] = q;
14531         
14532         if(this.pageSize){
14533             p.start = 0;
14534             p.limit = this.pageSize;
14535         }
14536         return p;
14537     },
14538
14539     /**
14540      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14541      */
14542     collapse : function(){
14543         if(!this.isExpanded()){
14544             return;
14545         }
14546         
14547         this.list.hide();
14548         
14549         this.hasFocus = false;
14550         
14551         if(this.tickable){
14552             this.okBtn.hide();
14553             this.cancelBtn.hide();
14554             this.trigger.show();
14555             
14556             if(this.editable){
14557                 this.tickableInputEl().dom.value = '';
14558                 this.tickableInputEl().blur();
14559             }
14560             
14561         }
14562         
14563         Roo.get(document).un('mousedown', this.collapseIf, this);
14564         Roo.get(document).un('mousewheel', this.collapseIf, this);
14565         if (!this.editable) {
14566             Roo.get(document).un('keydown', this.listKeyPress, this);
14567         }
14568         this.fireEvent('collapse', this);
14569         
14570         this.validate();
14571     },
14572
14573     // private
14574     collapseIf : function(e){
14575         var in_combo  = e.within(this.el);
14576         var in_list =  e.within(this.list);
14577         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14578         
14579         if (in_combo || in_list || is_list) {
14580             //e.stopPropagation();
14581             return;
14582         }
14583         
14584         if(this.tickable){
14585             this.onTickableFooterButtonClick(e, false, false);
14586         }
14587
14588         this.collapse();
14589         
14590     },
14591
14592     /**
14593      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14594      */
14595     expand : function(){
14596        
14597         if(this.isExpanded() || !this.hasFocus){
14598             return;
14599         }
14600         
14601         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14602         this.list.setWidth(lw);
14603         
14604         Roo.log('expand');
14605         
14606         this.list.show();
14607         
14608         this.restrictHeight();
14609         
14610         if(this.tickable){
14611             
14612             this.tickItems = Roo.apply([], this.item);
14613             
14614             this.okBtn.show();
14615             this.cancelBtn.show();
14616             this.trigger.hide();
14617             
14618             if(this.editable){
14619                 this.tickableInputEl().focus();
14620             }
14621             
14622         }
14623         
14624         Roo.get(document).on('mousedown', this.collapseIf, this);
14625         Roo.get(document).on('mousewheel', this.collapseIf, this);
14626         if (!this.editable) {
14627             Roo.get(document).on('keydown', this.listKeyPress, this);
14628         }
14629         
14630         this.fireEvent('expand', this);
14631     },
14632
14633     // private
14634     // Implements the default empty TriggerField.onTriggerClick function
14635     onTriggerClick : function(e)
14636     {
14637         Roo.log('trigger click');
14638         
14639         if(this.disabled || !this.triggerList){
14640             return;
14641         }
14642         
14643         this.page = 0;
14644         this.loadNext = false;
14645         
14646         if(this.isExpanded()){
14647             this.collapse();
14648             if (!this.blockFocus) {
14649                 this.inputEl().focus();
14650             }
14651             
14652         }else {
14653             this.hasFocus = true;
14654             if(this.triggerAction == 'all') {
14655                 this.doQuery(this.allQuery, true);
14656             } else {
14657                 this.doQuery(this.getRawValue());
14658             }
14659             if (!this.blockFocus) {
14660                 this.inputEl().focus();
14661             }
14662         }
14663     },
14664     
14665     onTickableTriggerClick : function(e)
14666     {
14667         if(this.disabled){
14668             return;
14669         }
14670         
14671         this.page = 0;
14672         this.loadNext = false;
14673         this.hasFocus = true;
14674         
14675         if(this.triggerAction == 'all') {
14676             this.doQuery(this.allQuery, true);
14677         } else {
14678             this.doQuery(this.getRawValue());
14679         }
14680     },
14681     
14682     onSearchFieldClick : function(e)
14683     {
14684         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14685             this.onTickableFooterButtonClick(e, false, false);
14686             return;
14687         }
14688         
14689         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14690             return;
14691         }
14692         
14693         this.page = 0;
14694         this.loadNext = false;
14695         this.hasFocus = true;
14696         
14697         if(this.triggerAction == 'all') {
14698             this.doQuery(this.allQuery, true);
14699         } else {
14700             this.doQuery(this.getRawValue());
14701         }
14702     },
14703     
14704     listKeyPress : function(e)
14705     {
14706         //Roo.log('listkeypress');
14707         // scroll to first matching element based on key pres..
14708         if (e.isSpecialKey()) {
14709             return false;
14710         }
14711         var k = String.fromCharCode(e.getKey()).toUpperCase();
14712         //Roo.log(k);
14713         var match  = false;
14714         var csel = this.view.getSelectedNodes();
14715         var cselitem = false;
14716         if (csel.length) {
14717             var ix = this.view.indexOf(csel[0]);
14718             cselitem  = this.store.getAt(ix);
14719             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14720                 cselitem = false;
14721             }
14722             
14723         }
14724         
14725         this.store.each(function(v) { 
14726             if (cselitem) {
14727                 // start at existing selection.
14728                 if (cselitem.id == v.id) {
14729                     cselitem = false;
14730                 }
14731                 return true;
14732             }
14733                 
14734             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14735                 match = this.store.indexOf(v);
14736                 return false;
14737             }
14738             return true;
14739         }, this);
14740         
14741         if (match === false) {
14742             return true; // no more action?
14743         }
14744         // scroll to?
14745         this.view.select(match);
14746         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14747         sn.scrollIntoView(sn.dom.parentNode, false);
14748     },
14749     
14750     onViewScroll : function(e, t){
14751         
14752         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){
14753             return;
14754         }
14755         
14756         this.hasQuery = true;
14757         
14758         this.loading = this.list.select('.loading', true).first();
14759         
14760         if(this.loading === null){
14761             this.list.createChild({
14762                 tag: 'div',
14763                 cls: 'loading roo-select2-more-results roo-select2-active',
14764                 html: 'Loading more results...'
14765             });
14766             
14767             this.loading = this.list.select('.loading', true).first();
14768             
14769             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14770             
14771             this.loading.hide();
14772         }
14773         
14774         this.loading.show();
14775         
14776         var _combo = this;
14777         
14778         this.page++;
14779         this.loadNext = true;
14780         
14781         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14782         
14783         return;
14784     },
14785     
14786     addItem : function(o)
14787     {   
14788         var dv = ''; // display value
14789         
14790         if (this.displayField) {
14791             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14792         } else {
14793             // this is an error condition!!!
14794             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14795         }
14796         
14797         if(!dv.length){
14798             return;
14799         }
14800         
14801         var choice = this.choices.createChild({
14802             tag: 'li',
14803             cls: 'roo-select2-search-choice',
14804             cn: [
14805                 {
14806                     tag: 'div',
14807                     html: dv
14808                 },
14809                 {
14810                     tag: 'a',
14811                     href: '#',
14812                     cls: 'roo-select2-search-choice-close fa fa-times',
14813                     tabindex: '-1'
14814                 }
14815             ]
14816             
14817         }, this.searchField);
14818         
14819         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14820         
14821         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14822         
14823         this.item.push(o);
14824         
14825         this.lastData = o;
14826         
14827         this.syncValue();
14828         
14829         this.inputEl().dom.value = '';
14830         
14831         this.validate();
14832     },
14833     
14834     onRemoveItem : function(e, _self, o)
14835     {
14836         e.preventDefault();
14837         
14838         this.lastItem = Roo.apply([], this.item);
14839         
14840         var index = this.item.indexOf(o.data) * 1;
14841         
14842         if( index < 0){
14843             Roo.log('not this item?!');
14844             return;
14845         }
14846         
14847         this.item.splice(index, 1);
14848         o.item.remove();
14849         
14850         this.syncValue();
14851         
14852         this.fireEvent('remove', this, e);
14853         
14854         this.validate();
14855         
14856     },
14857     
14858     syncValue : function()
14859     {
14860         if(!this.item.length){
14861             this.clearValue();
14862             return;
14863         }
14864             
14865         var value = [];
14866         var _this = this;
14867         Roo.each(this.item, function(i){
14868             if(_this.valueField){
14869                 value.push(i[_this.valueField]);
14870                 return;
14871             }
14872
14873             value.push(i);
14874         });
14875
14876         this.value = value.join(',');
14877
14878         if(this.hiddenField){
14879             this.hiddenField.dom.value = this.value;
14880         }
14881         
14882         this.store.fireEvent("datachanged", this.store);
14883         
14884         this.validate();
14885     },
14886     
14887     clearItem : function()
14888     {
14889         if(!this.multiple){
14890             return;
14891         }
14892         
14893         this.item = [];
14894         
14895         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14896            c.remove();
14897         });
14898         
14899         this.syncValue();
14900         
14901         this.validate();
14902         
14903         if(this.tickable && !Roo.isTouch){
14904             this.view.refresh();
14905         }
14906     },
14907     
14908     inputEl: function ()
14909     {
14910         if(Roo.isIOS && this.useNativeIOS){
14911             return this.el.select('select.roo-ios-select', true).first();
14912         }
14913         
14914         if(Roo.isTouch && this.mobileTouchView){
14915             return this.el.select('input.form-control',true).first();
14916         }
14917         
14918         if(this.tickable){
14919             return this.searchField;
14920         }
14921         
14922         return this.el.select('input.form-control',true).first();
14923     },
14924     
14925     onTickableFooterButtonClick : function(e, btn, el)
14926     {
14927         e.preventDefault();
14928         
14929         this.lastItem = Roo.apply([], this.item);
14930         
14931         if(btn && btn.name == 'cancel'){
14932             this.tickItems = Roo.apply([], this.item);
14933             this.collapse();
14934             return;
14935         }
14936         
14937         this.clearItem();
14938         
14939         var _this = this;
14940         
14941         Roo.each(this.tickItems, function(o){
14942             _this.addItem(o);
14943         });
14944         
14945         this.collapse();
14946         
14947     },
14948     
14949     validate : function()
14950     {
14951         if(this.getVisibilityEl().hasClass('hidden')){
14952             return true;
14953         }
14954         
14955         var v = this.getRawValue();
14956         
14957         if(this.multiple){
14958             v = this.getValue();
14959         }
14960         
14961         if(this.disabled || this.allowBlank || v.length){
14962             this.markValid();
14963             return true;
14964         }
14965         
14966         this.markInvalid();
14967         return false;
14968     },
14969     
14970     tickableInputEl : function()
14971     {
14972         if(!this.tickable || !this.editable){
14973             return this.inputEl();
14974         }
14975         
14976         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14977     },
14978     
14979     
14980     getAutoCreateTouchView : function()
14981     {
14982         var id = Roo.id();
14983         
14984         var cfg = {
14985             cls: 'form-group' //input-group
14986         };
14987         
14988         var input =  {
14989             tag: 'input',
14990             id : id,
14991             type : this.inputType,
14992             cls : 'form-control x-combo-noedit',
14993             autocomplete: 'new-password',
14994             placeholder : this.placeholder || '',
14995             readonly : true
14996         };
14997         
14998         if (this.name) {
14999             input.name = this.name;
15000         }
15001         
15002         if (this.size) {
15003             input.cls += ' input-' + this.size;
15004         }
15005         
15006         if (this.disabled) {
15007             input.disabled = true;
15008         }
15009         
15010         var inputblock = {
15011             cls : '',
15012             cn : [
15013                 input
15014             ]
15015         };
15016         
15017         if(this.before){
15018             inputblock.cls += ' input-group';
15019             
15020             inputblock.cn.unshift({
15021                 tag :'span',
15022                 cls : 'input-group-addon',
15023                 html : this.before
15024             });
15025         }
15026         
15027         if(this.removable && !this.multiple){
15028             inputblock.cls += ' roo-removable';
15029             
15030             inputblock.cn.push({
15031                 tag: 'button',
15032                 html : 'x',
15033                 cls : 'roo-combo-removable-btn close'
15034             });
15035         }
15036
15037         if(this.hasFeedback && !this.allowBlank){
15038             
15039             inputblock.cls += ' has-feedback';
15040             
15041             inputblock.cn.push({
15042                 tag: 'span',
15043                 cls: 'glyphicon form-control-feedback'
15044             });
15045             
15046         }
15047         
15048         if (this.after) {
15049             
15050             inputblock.cls += (this.before) ? '' : ' input-group';
15051             
15052             inputblock.cn.push({
15053                 tag :'span',
15054                 cls : 'input-group-addon',
15055                 html : this.after
15056             });
15057         }
15058
15059         var box = {
15060             tag: 'div',
15061             cn: [
15062                 {
15063                     tag: 'input',
15064                     type : 'hidden',
15065                     cls: 'form-hidden-field'
15066                 },
15067                 inputblock
15068             ]
15069             
15070         };
15071         
15072         if(this.multiple){
15073             box = {
15074                 tag: 'div',
15075                 cn: [
15076                     {
15077                         tag: 'input',
15078                         type : 'hidden',
15079                         cls: 'form-hidden-field'
15080                     },
15081                     {
15082                         tag: 'ul',
15083                         cls: 'roo-select2-choices',
15084                         cn:[
15085                             {
15086                                 tag: 'li',
15087                                 cls: 'roo-select2-search-field',
15088                                 cn: [
15089
15090                                     inputblock
15091                                 ]
15092                             }
15093                         ]
15094                     }
15095                 ]
15096             }
15097         };
15098         
15099         var combobox = {
15100             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15101             cn: [
15102                 box
15103             ]
15104         };
15105         
15106         if(!this.multiple && this.showToggleBtn){
15107             
15108             var caret = {
15109                         tag: 'span',
15110                         cls: 'caret'
15111             };
15112             
15113             if (this.caret != false) {
15114                 caret = {
15115                      tag: 'i',
15116                      cls: 'fa fa-' + this.caret
15117                 };
15118                 
15119             }
15120             
15121             combobox.cn.push({
15122                 tag :'span',
15123                 cls : 'input-group-addon btn dropdown-toggle',
15124                 cn : [
15125                     caret,
15126                     {
15127                         tag: 'span',
15128                         cls: 'combobox-clear',
15129                         cn  : [
15130                             {
15131                                 tag : 'i',
15132                                 cls: 'icon-remove'
15133                             }
15134                         ]
15135                     }
15136                 ]
15137
15138             })
15139         }
15140         
15141         if(this.multiple){
15142             combobox.cls += ' roo-select2-container-multi';
15143         }
15144         
15145         var align = this.labelAlign || this.parentLabelAlign();
15146         
15147         if (align ==='left' && this.fieldLabel.length) {
15148
15149             cfg.cn = [
15150                 {
15151                    tag : 'i',
15152                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15153                    tooltip : 'This field is required'
15154                 },
15155                 {
15156                     tag: 'label',
15157                     cls : 'control-label',
15158                     html : this.fieldLabel
15159
15160                 },
15161                 {
15162                     cls : '', 
15163                     cn: [
15164                         combobox
15165                     ]
15166                 }
15167             ];
15168             
15169             var labelCfg = cfg.cn[1];
15170             var contentCfg = cfg.cn[2];
15171             
15172
15173             if(this.indicatorpos == 'right'){
15174                 cfg.cn = [
15175                     {
15176                         tag: 'label',
15177                         'for' :  id,
15178                         cls : 'control-label',
15179                         cn : [
15180                             {
15181                                 tag : 'span',
15182                                 html : this.fieldLabel
15183                             },
15184                             {
15185                                 tag : 'i',
15186                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15187                                 tooltip : 'This field is required'
15188                             }
15189                         ]
15190                     },
15191                     {
15192                         cls : "",
15193                         cn: [
15194                             combobox
15195                         ]
15196                     }
15197
15198                 ];
15199                 
15200                 labelCfg = cfg.cn[0];
15201                 contentCfg = cfg.cn[1];
15202             }
15203             
15204            
15205             
15206             if(this.labelWidth > 12){
15207                 labelCfg.style = "width: " + this.labelWidth + 'px';
15208             }
15209             
15210             if(this.labelWidth < 13 && this.labelmd == 0){
15211                 this.labelmd = this.labelWidth;
15212             }
15213             
15214             if(this.labellg > 0){
15215                 labelCfg.cls += ' col-lg-' + this.labellg;
15216                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15217             }
15218             
15219             if(this.labelmd > 0){
15220                 labelCfg.cls += ' col-md-' + this.labelmd;
15221                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15222             }
15223             
15224             if(this.labelsm > 0){
15225                 labelCfg.cls += ' col-sm-' + this.labelsm;
15226                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15227             }
15228             
15229             if(this.labelxs > 0){
15230                 labelCfg.cls += ' col-xs-' + this.labelxs;
15231                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15232             }
15233                 
15234                 
15235         } else if ( this.fieldLabel.length) {
15236             cfg.cn = [
15237                 {
15238                    tag : 'i',
15239                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15240                    tooltip : 'This field is required'
15241                 },
15242                 {
15243                     tag: 'label',
15244                     cls : 'control-label',
15245                     html : this.fieldLabel
15246
15247                 },
15248                 {
15249                     cls : '', 
15250                     cn: [
15251                         combobox
15252                     ]
15253                 }
15254             ];
15255             
15256             if(this.indicatorpos == 'right'){
15257                 cfg.cn = [
15258                     {
15259                         tag: 'label',
15260                         cls : 'control-label',
15261                         html : this.fieldLabel,
15262                         cn : [
15263                             {
15264                                tag : 'i',
15265                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15266                                tooltip : 'This field is required'
15267                             }
15268                         ]
15269                     },
15270                     {
15271                         cls : '', 
15272                         cn: [
15273                             combobox
15274                         ]
15275                     }
15276                 ];
15277             }
15278         } else {
15279             cfg.cn = combobox;    
15280         }
15281         
15282         
15283         var settings = this;
15284         
15285         ['xs','sm','md','lg'].map(function(size){
15286             if (settings[size]) {
15287                 cfg.cls += ' col-' + size + '-' + settings[size];
15288             }
15289         });
15290         
15291         return cfg;
15292     },
15293     
15294     initTouchView : function()
15295     {
15296         this.renderTouchView();
15297         
15298         this.touchViewEl.on('scroll', function(){
15299             this.el.dom.scrollTop = 0;
15300         }, this);
15301         
15302         this.originalValue = this.getValue();
15303         
15304         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15305         
15306         this.inputEl().on("click", this.showTouchView, this);
15307         if (this.triggerEl) {
15308             this.triggerEl.on("click", this.showTouchView, this);
15309         }
15310         
15311         
15312         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15313         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15314         
15315         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15316         
15317         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15318         this.store.on('load', this.onTouchViewLoad, this);
15319         this.store.on('loadexception', this.onTouchViewLoadException, this);
15320         
15321         if(this.hiddenName){
15322             
15323             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15324             
15325             this.hiddenField.dom.value =
15326                 this.hiddenValue !== undefined ? this.hiddenValue :
15327                 this.value !== undefined ? this.value : '';
15328         
15329             this.el.dom.removeAttribute('name');
15330             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15331         }
15332         
15333         if(this.multiple){
15334             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15335             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15336         }
15337         
15338         if(this.removable && !this.multiple){
15339             var close = this.closeTriggerEl();
15340             if(close){
15341                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15342                 close.on('click', this.removeBtnClick, this, close);
15343             }
15344         }
15345         /*
15346          * fix the bug in Safari iOS8
15347          */
15348         this.inputEl().on("focus", function(e){
15349             document.activeElement.blur();
15350         }, this);
15351         
15352         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15353         
15354         return;
15355         
15356         
15357     },
15358     
15359     renderTouchView : function()
15360     {
15361         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15362         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15363         
15364         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15365         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15366         
15367         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15368         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15369         this.touchViewBodyEl.setStyle('overflow', 'auto');
15370         
15371         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15372         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15373         
15374         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15375         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15376         
15377     },
15378     
15379     showTouchView : function()
15380     {
15381         if(this.disabled){
15382             return;
15383         }
15384         
15385         this.touchViewHeaderEl.hide();
15386
15387         if(this.modalTitle.length){
15388             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15389             this.touchViewHeaderEl.show();
15390         }
15391
15392         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15393         this.touchViewEl.show();
15394
15395         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15396         
15397         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15398         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15399
15400         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15401
15402         if(this.modalTitle.length){
15403             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15404         }
15405         
15406         this.touchViewBodyEl.setHeight(bodyHeight);
15407
15408         if(this.animate){
15409             var _this = this;
15410             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15411         }else{
15412             this.touchViewEl.addClass('in');
15413         }
15414         
15415         if(this._touchViewMask){
15416             Roo.get(document.body).addClass("x-body-masked");
15417             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15418             this._touchViewMask.setStyle('z-index', 10000);
15419             this._touchViewMask.addClass('show');
15420         }
15421         
15422         this.doTouchViewQuery();
15423         
15424     },
15425     
15426     hideTouchView : function()
15427     {
15428         this.touchViewEl.removeClass('in');
15429
15430         if(this.animate){
15431             var _this = this;
15432             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15433         }else{
15434             this.touchViewEl.setStyle('display', 'none');
15435         }
15436         
15437         if(this._touchViewMask){
15438             this._touchViewMask.removeClass('show');
15439             Roo.get(document.body).removeClass("x-body-masked");
15440         }
15441     },
15442     
15443     setTouchViewValue : function()
15444     {
15445         if(this.multiple){
15446             this.clearItem();
15447         
15448             var _this = this;
15449
15450             Roo.each(this.tickItems, function(o){
15451                 this.addItem(o);
15452             }, this);
15453         }
15454         
15455         this.hideTouchView();
15456     },
15457     
15458     doTouchViewQuery : function()
15459     {
15460         var qe = {
15461             query: '',
15462             forceAll: true,
15463             combo: this,
15464             cancel:false
15465         };
15466         
15467         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15468             return false;
15469         }
15470         
15471         if(!this.alwaysQuery || this.mode == 'local'){
15472             this.onTouchViewLoad();
15473             return;
15474         }
15475         
15476         this.store.load();
15477     },
15478     
15479     onTouchViewBeforeLoad : function(combo,opts)
15480     {
15481         return;
15482     },
15483
15484     // private
15485     onTouchViewLoad : function()
15486     {
15487         if(this.store.getCount() < 1){
15488             this.onTouchViewEmptyResults();
15489             return;
15490         }
15491         
15492         this.clearTouchView();
15493         
15494         var rawValue = this.getRawValue();
15495         
15496         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15497         
15498         this.tickItems = [];
15499         
15500         this.store.data.each(function(d, rowIndex){
15501             var row = this.touchViewListGroup.createChild(template);
15502             
15503             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15504                 row.addClass(d.data.cls);
15505             }
15506             
15507             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15508                 var cfg = {
15509                     data : d.data,
15510                     html : d.data[this.displayField]
15511                 };
15512                 
15513                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15514                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15515                 }
15516             }
15517             row.removeClass('selected');
15518             if(!this.multiple && this.valueField &&
15519                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15520             {
15521                 // radio buttons..
15522                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15523                 row.addClass('selected');
15524             }
15525             
15526             if(this.multiple && this.valueField &&
15527                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15528             {
15529                 
15530                 // checkboxes...
15531                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15532                 this.tickItems.push(d.data);
15533             }
15534             
15535             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15536             
15537         }, this);
15538         
15539         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15540         
15541         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15542
15543         if(this.modalTitle.length){
15544             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15545         }
15546
15547         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15548         
15549         if(this.mobile_restrict_height && listHeight < bodyHeight){
15550             this.touchViewBodyEl.setHeight(listHeight);
15551         }
15552         
15553         var _this = this;
15554         
15555         if(firstChecked && listHeight > bodyHeight){
15556             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15557         }
15558         
15559     },
15560     
15561     onTouchViewLoadException : function()
15562     {
15563         this.hideTouchView();
15564     },
15565     
15566     onTouchViewEmptyResults : function()
15567     {
15568         this.clearTouchView();
15569         
15570         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15571         
15572         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15573         
15574     },
15575     
15576     clearTouchView : function()
15577     {
15578         this.touchViewListGroup.dom.innerHTML = '';
15579     },
15580     
15581     onTouchViewClick : function(e, el, o)
15582     {
15583         e.preventDefault();
15584         
15585         var row = o.row;
15586         var rowIndex = o.rowIndex;
15587         
15588         var r = this.store.getAt(rowIndex);
15589         
15590         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15591             
15592             if(!this.multiple){
15593                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15594                     c.dom.removeAttribute('checked');
15595                 }, this);
15596
15597                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15598
15599                 this.setFromData(r.data);
15600
15601                 var close = this.closeTriggerEl();
15602
15603                 if(close){
15604                     close.show();
15605                 }
15606
15607                 this.hideTouchView();
15608
15609                 this.fireEvent('select', this, r, rowIndex);
15610
15611                 return;
15612             }
15613
15614             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15615                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15616                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15617                 return;
15618             }
15619
15620             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15621             this.addItem(r.data);
15622             this.tickItems.push(r.data);
15623         }
15624     },
15625     
15626     getAutoCreateNativeIOS : function()
15627     {
15628         var cfg = {
15629             cls: 'form-group' //input-group,
15630         };
15631         
15632         var combobox =  {
15633             tag: 'select',
15634             cls : 'roo-ios-select'
15635         };
15636         
15637         if (this.name) {
15638             combobox.name = this.name;
15639         }
15640         
15641         if (this.disabled) {
15642             combobox.disabled = true;
15643         }
15644         
15645         var settings = this;
15646         
15647         ['xs','sm','md','lg'].map(function(size){
15648             if (settings[size]) {
15649                 cfg.cls += ' col-' + size + '-' + settings[size];
15650             }
15651         });
15652         
15653         cfg.cn = combobox;
15654         
15655         return cfg;
15656         
15657     },
15658     
15659     initIOSView : function()
15660     {
15661         this.store.on('load', this.onIOSViewLoad, this);
15662         
15663         return;
15664     },
15665     
15666     onIOSViewLoad : function()
15667     {
15668         if(this.store.getCount() < 1){
15669             return;
15670         }
15671         
15672         this.clearIOSView();
15673         
15674         if(this.allowBlank) {
15675             
15676             var default_text = '-- SELECT --';
15677             
15678             if(this.placeholder.length){
15679                 default_text = this.placeholder;
15680             }
15681             
15682             if(this.emptyTitle.length){
15683                 default_text += ' - ' + this.emptyTitle + ' -';
15684             }
15685             
15686             var opt = this.inputEl().createChild({
15687                 tag: 'option',
15688                 value : 0,
15689                 html : default_text
15690             });
15691             
15692             var o = {};
15693             o[this.valueField] = 0;
15694             o[this.displayField] = default_text;
15695             
15696             this.ios_options.push({
15697                 data : o,
15698                 el : opt
15699             });
15700             
15701         }
15702         
15703         this.store.data.each(function(d, rowIndex){
15704             
15705             var html = '';
15706             
15707             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15708                 html = d.data[this.displayField];
15709             }
15710             
15711             var value = '';
15712             
15713             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15714                 value = d.data[this.valueField];
15715             }
15716             
15717             var option = {
15718                 tag: 'option',
15719                 value : value,
15720                 html : html
15721             };
15722             
15723             if(this.value == d.data[this.valueField]){
15724                 option['selected'] = true;
15725             }
15726             
15727             var opt = this.inputEl().createChild(option);
15728             
15729             this.ios_options.push({
15730                 data : d.data,
15731                 el : opt
15732             });
15733             
15734         }, this);
15735         
15736         this.inputEl().on('change', function(){
15737            this.fireEvent('select', this);
15738         }, this);
15739         
15740     },
15741     
15742     clearIOSView: function()
15743     {
15744         this.inputEl().dom.innerHTML = '';
15745         
15746         this.ios_options = [];
15747     },
15748     
15749     setIOSValue: function(v)
15750     {
15751         this.value = v;
15752         
15753         if(!this.ios_options){
15754             return;
15755         }
15756         
15757         Roo.each(this.ios_options, function(opts){
15758            
15759            opts.el.dom.removeAttribute('selected');
15760            
15761            if(opts.data[this.valueField] != v){
15762                return;
15763            }
15764            
15765            opts.el.dom.setAttribute('selected', true);
15766            
15767         }, this);
15768     }
15769
15770     /** 
15771     * @cfg {Boolean} grow 
15772     * @hide 
15773     */
15774     /** 
15775     * @cfg {Number} growMin 
15776     * @hide 
15777     */
15778     /** 
15779     * @cfg {Number} growMax 
15780     * @hide 
15781     */
15782     /**
15783      * @hide
15784      * @method autoSize
15785      */
15786 });
15787
15788 Roo.apply(Roo.bootstrap.ComboBox,  {
15789     
15790     header : {
15791         tag: 'div',
15792         cls: 'modal-header',
15793         cn: [
15794             {
15795                 tag: 'h4',
15796                 cls: 'modal-title'
15797             }
15798         ]
15799     },
15800     
15801     body : {
15802         tag: 'div',
15803         cls: 'modal-body',
15804         cn: [
15805             {
15806                 tag: 'ul',
15807                 cls: 'list-group'
15808             }
15809         ]
15810     },
15811     
15812     listItemRadio : {
15813         tag: 'li',
15814         cls: 'list-group-item',
15815         cn: [
15816             {
15817                 tag: 'span',
15818                 cls: 'roo-combobox-list-group-item-value'
15819             },
15820             {
15821                 tag: 'div',
15822                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15823                 cn: [
15824                     {
15825                         tag: 'input',
15826                         type: 'radio'
15827                     },
15828                     {
15829                         tag: 'label'
15830                     }
15831                 ]
15832             }
15833         ]
15834     },
15835     
15836     listItemCheckbox : {
15837         tag: 'li',
15838         cls: 'list-group-item',
15839         cn: [
15840             {
15841                 tag: 'span',
15842                 cls: 'roo-combobox-list-group-item-value'
15843             },
15844             {
15845                 tag: 'div',
15846                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15847                 cn: [
15848                     {
15849                         tag: 'input',
15850                         type: 'checkbox'
15851                     },
15852                     {
15853                         tag: 'label'
15854                     }
15855                 ]
15856             }
15857         ]
15858     },
15859     
15860     emptyResult : {
15861         tag: 'div',
15862         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15863     },
15864     
15865     footer : {
15866         tag: 'div',
15867         cls: 'modal-footer',
15868         cn: [
15869             {
15870                 tag: 'div',
15871                 cls: 'row',
15872                 cn: [
15873                     {
15874                         tag: 'div',
15875                         cls: 'col-xs-6 text-left',
15876                         cn: {
15877                             tag: 'button',
15878                             cls: 'btn btn-danger roo-touch-view-cancel',
15879                             html: 'Cancel'
15880                         }
15881                     },
15882                     {
15883                         tag: 'div',
15884                         cls: 'col-xs-6 text-right',
15885                         cn: {
15886                             tag: 'button',
15887                             cls: 'btn btn-success roo-touch-view-ok',
15888                             html: 'OK'
15889                         }
15890                     }
15891                 ]
15892             }
15893         ]
15894         
15895     }
15896 });
15897
15898 Roo.apply(Roo.bootstrap.ComboBox,  {
15899     
15900     touchViewTemplate : {
15901         tag: 'div',
15902         cls: 'modal fade roo-combobox-touch-view',
15903         cn: [
15904             {
15905                 tag: 'div',
15906                 cls: 'modal-dialog',
15907                 style : 'position:fixed', // we have to fix position....
15908                 cn: [
15909                     {
15910                         tag: 'div',
15911                         cls: 'modal-content',
15912                         cn: [
15913                             Roo.bootstrap.ComboBox.header,
15914                             Roo.bootstrap.ComboBox.body,
15915                             Roo.bootstrap.ComboBox.footer
15916                         ]
15917                     }
15918                 ]
15919             }
15920         ]
15921     }
15922 });/*
15923  * Based on:
15924  * Ext JS Library 1.1.1
15925  * Copyright(c) 2006-2007, Ext JS, LLC.
15926  *
15927  * Originally Released Under LGPL - original licence link has changed is not relivant.
15928  *
15929  * Fork - LGPL
15930  * <script type="text/javascript">
15931  */
15932
15933 /**
15934  * @class Roo.View
15935  * @extends Roo.util.Observable
15936  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15937  * This class also supports single and multi selection modes. <br>
15938  * Create a data model bound view:
15939  <pre><code>
15940  var store = new Roo.data.Store(...);
15941
15942  var view = new Roo.View({
15943     el : "my-element",
15944     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15945  
15946     singleSelect: true,
15947     selectedClass: "ydataview-selected",
15948     store: store
15949  });
15950
15951  // listen for node click?
15952  view.on("click", function(vw, index, node, e){
15953  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15954  });
15955
15956  // load XML data
15957  dataModel.load("foobar.xml");
15958  </code></pre>
15959  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15960  * <br><br>
15961  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15962  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15963  * 
15964  * Note: old style constructor is still suported (container, template, config)
15965  * 
15966  * @constructor
15967  * Create a new View
15968  * @param {Object} config The config object
15969  * 
15970  */
15971 Roo.View = function(config, depreciated_tpl, depreciated_config){
15972     
15973     this.parent = false;
15974     
15975     if (typeof(depreciated_tpl) == 'undefined') {
15976         // new way.. - universal constructor.
15977         Roo.apply(this, config);
15978         this.el  = Roo.get(this.el);
15979     } else {
15980         // old format..
15981         this.el  = Roo.get(config);
15982         this.tpl = depreciated_tpl;
15983         Roo.apply(this, depreciated_config);
15984     }
15985     this.wrapEl  = this.el.wrap().wrap();
15986     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15987     
15988     
15989     if(typeof(this.tpl) == "string"){
15990         this.tpl = new Roo.Template(this.tpl);
15991     } else {
15992         // support xtype ctors..
15993         this.tpl = new Roo.factory(this.tpl, Roo);
15994     }
15995     
15996     
15997     this.tpl.compile();
15998     
15999     /** @private */
16000     this.addEvents({
16001         /**
16002          * @event beforeclick
16003          * Fires before a click is processed. Returns false to cancel the default action.
16004          * @param {Roo.View} this
16005          * @param {Number} index The index of the target node
16006          * @param {HTMLElement} node The target node
16007          * @param {Roo.EventObject} e The raw event object
16008          */
16009             "beforeclick" : true,
16010         /**
16011          * @event click
16012          * Fires when a template node is clicked.
16013          * @param {Roo.View} this
16014          * @param {Number} index The index of the target node
16015          * @param {HTMLElement} node The target node
16016          * @param {Roo.EventObject} e The raw event object
16017          */
16018             "click" : true,
16019         /**
16020          * @event dblclick
16021          * Fires when a template node is double clicked.
16022          * @param {Roo.View} this
16023          * @param {Number} index The index of the target node
16024          * @param {HTMLElement} node The target node
16025          * @param {Roo.EventObject} e The raw event object
16026          */
16027             "dblclick" : true,
16028         /**
16029          * @event contextmenu
16030          * Fires when a template node is right clicked.
16031          * @param {Roo.View} this
16032          * @param {Number} index The index of the target node
16033          * @param {HTMLElement} node The target node
16034          * @param {Roo.EventObject} e The raw event object
16035          */
16036             "contextmenu" : true,
16037         /**
16038          * @event selectionchange
16039          * Fires when the selected nodes change.
16040          * @param {Roo.View} this
16041          * @param {Array} selections Array of the selected nodes
16042          */
16043             "selectionchange" : true,
16044     
16045         /**
16046          * @event beforeselect
16047          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16048          * @param {Roo.View} this
16049          * @param {HTMLElement} node The node to be selected
16050          * @param {Array} selections Array of currently selected nodes
16051          */
16052             "beforeselect" : true,
16053         /**
16054          * @event preparedata
16055          * Fires on every row to render, to allow you to change the data.
16056          * @param {Roo.View} this
16057          * @param {Object} data to be rendered (change this)
16058          */
16059           "preparedata" : true
16060           
16061           
16062         });
16063
16064
16065
16066     this.el.on({
16067         "click": this.onClick,
16068         "dblclick": this.onDblClick,
16069         "contextmenu": this.onContextMenu,
16070         scope:this
16071     });
16072
16073     this.selections = [];
16074     this.nodes = [];
16075     this.cmp = new Roo.CompositeElementLite([]);
16076     if(this.store){
16077         this.store = Roo.factory(this.store, Roo.data);
16078         this.setStore(this.store, true);
16079     }
16080     
16081     if ( this.footer && this.footer.xtype) {
16082            
16083          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16084         
16085         this.footer.dataSource = this.store;
16086         this.footer.container = fctr;
16087         this.footer = Roo.factory(this.footer, Roo);
16088         fctr.insertFirst(this.el);
16089         
16090         // this is a bit insane - as the paging toolbar seems to detach the el..
16091 //        dom.parentNode.parentNode.parentNode
16092          // they get detached?
16093     }
16094     
16095     
16096     Roo.View.superclass.constructor.call(this);
16097     
16098     
16099 };
16100
16101 Roo.extend(Roo.View, Roo.util.Observable, {
16102     
16103      /**
16104      * @cfg {Roo.data.Store} store Data store to load data from.
16105      */
16106     store : false,
16107     
16108     /**
16109      * @cfg {String|Roo.Element} el The container element.
16110      */
16111     el : '',
16112     
16113     /**
16114      * @cfg {String|Roo.Template} tpl The template used by this View 
16115      */
16116     tpl : false,
16117     /**
16118      * @cfg {String} dataName the named area of the template to use as the data area
16119      *                          Works with domtemplates roo-name="name"
16120      */
16121     dataName: false,
16122     /**
16123      * @cfg {String} selectedClass The css class to add to selected nodes
16124      */
16125     selectedClass : "x-view-selected",
16126      /**
16127      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16128      */
16129     emptyText : "",
16130     
16131     /**
16132      * @cfg {String} text to display on mask (default Loading)
16133      */
16134     mask : false,
16135     /**
16136      * @cfg {Boolean} multiSelect Allow multiple selection
16137      */
16138     multiSelect : false,
16139     /**
16140      * @cfg {Boolean} singleSelect Allow single selection
16141      */
16142     singleSelect:  false,
16143     
16144     /**
16145      * @cfg {Boolean} toggleSelect - selecting 
16146      */
16147     toggleSelect : false,
16148     
16149     /**
16150      * @cfg {Boolean} tickable - selecting 
16151      */
16152     tickable : false,
16153     
16154     /**
16155      * Returns the element this view is bound to.
16156      * @return {Roo.Element}
16157      */
16158     getEl : function(){
16159         return this.wrapEl;
16160     },
16161     
16162     
16163
16164     /**
16165      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16166      */
16167     refresh : function(){
16168         //Roo.log('refresh');
16169         var t = this.tpl;
16170         
16171         // if we are using something like 'domtemplate', then
16172         // the what gets used is:
16173         // t.applySubtemplate(NAME, data, wrapping data..)
16174         // the outer template then get' applied with
16175         //     the store 'extra data'
16176         // and the body get's added to the
16177         //      roo-name="data" node?
16178         //      <span class='roo-tpl-{name}'></span> ?????
16179         
16180         
16181         
16182         this.clearSelections();
16183         this.el.update("");
16184         var html = [];
16185         var records = this.store.getRange();
16186         if(records.length < 1) {
16187             
16188             // is this valid??  = should it render a template??
16189             
16190             this.el.update(this.emptyText);
16191             return;
16192         }
16193         var el = this.el;
16194         if (this.dataName) {
16195             this.el.update(t.apply(this.store.meta)); //????
16196             el = this.el.child('.roo-tpl-' + this.dataName);
16197         }
16198         
16199         for(var i = 0, len = records.length; i < len; i++){
16200             var data = this.prepareData(records[i].data, i, records[i]);
16201             this.fireEvent("preparedata", this, data, i, records[i]);
16202             
16203             var d = Roo.apply({}, data);
16204             
16205             if(this.tickable){
16206                 Roo.apply(d, {'roo-id' : Roo.id()});
16207                 
16208                 var _this = this;
16209             
16210                 Roo.each(this.parent.item, function(item){
16211                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16212                         return;
16213                     }
16214                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16215                 });
16216             }
16217             
16218             html[html.length] = Roo.util.Format.trim(
16219                 this.dataName ?
16220                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16221                     t.apply(d)
16222             );
16223         }
16224         
16225         
16226         
16227         el.update(html.join(""));
16228         this.nodes = el.dom.childNodes;
16229         this.updateIndexes(0);
16230     },
16231     
16232
16233     /**
16234      * Function to override to reformat the data that is sent to
16235      * the template for each node.
16236      * DEPRICATED - use the preparedata event handler.
16237      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16238      * a JSON object for an UpdateManager bound view).
16239      */
16240     prepareData : function(data, index, record)
16241     {
16242         this.fireEvent("preparedata", this, data, index, record);
16243         return data;
16244     },
16245
16246     onUpdate : function(ds, record){
16247         // Roo.log('on update');   
16248         this.clearSelections();
16249         var index = this.store.indexOf(record);
16250         var n = this.nodes[index];
16251         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16252         n.parentNode.removeChild(n);
16253         this.updateIndexes(index, index);
16254     },
16255
16256     
16257     
16258 // --------- FIXME     
16259     onAdd : function(ds, records, index)
16260     {
16261         //Roo.log(['on Add', ds, records, index] );        
16262         this.clearSelections();
16263         if(this.nodes.length == 0){
16264             this.refresh();
16265             return;
16266         }
16267         var n = this.nodes[index];
16268         for(var i = 0, len = records.length; i < len; i++){
16269             var d = this.prepareData(records[i].data, i, records[i]);
16270             if(n){
16271                 this.tpl.insertBefore(n, d);
16272             }else{
16273                 
16274                 this.tpl.append(this.el, d);
16275             }
16276         }
16277         this.updateIndexes(index);
16278     },
16279
16280     onRemove : function(ds, record, index){
16281        // Roo.log('onRemove');
16282         this.clearSelections();
16283         var el = this.dataName  ?
16284             this.el.child('.roo-tpl-' + this.dataName) :
16285             this.el; 
16286         
16287         el.dom.removeChild(this.nodes[index]);
16288         this.updateIndexes(index);
16289     },
16290
16291     /**
16292      * Refresh an individual node.
16293      * @param {Number} index
16294      */
16295     refreshNode : function(index){
16296         this.onUpdate(this.store, this.store.getAt(index));
16297     },
16298
16299     updateIndexes : function(startIndex, endIndex){
16300         var ns = this.nodes;
16301         startIndex = startIndex || 0;
16302         endIndex = endIndex || ns.length - 1;
16303         for(var i = startIndex; i <= endIndex; i++){
16304             ns[i].nodeIndex = i;
16305         }
16306     },
16307
16308     /**
16309      * Changes the data store this view uses and refresh the view.
16310      * @param {Store} store
16311      */
16312     setStore : function(store, initial){
16313         if(!initial && this.store){
16314             this.store.un("datachanged", this.refresh);
16315             this.store.un("add", this.onAdd);
16316             this.store.un("remove", this.onRemove);
16317             this.store.un("update", this.onUpdate);
16318             this.store.un("clear", this.refresh);
16319             this.store.un("beforeload", this.onBeforeLoad);
16320             this.store.un("load", this.onLoad);
16321             this.store.un("loadexception", this.onLoad);
16322         }
16323         if(store){
16324           
16325             store.on("datachanged", this.refresh, this);
16326             store.on("add", this.onAdd, this);
16327             store.on("remove", this.onRemove, this);
16328             store.on("update", this.onUpdate, this);
16329             store.on("clear", this.refresh, this);
16330             store.on("beforeload", this.onBeforeLoad, this);
16331             store.on("load", this.onLoad, this);
16332             store.on("loadexception", this.onLoad, this);
16333         }
16334         
16335         if(store){
16336             this.refresh();
16337         }
16338     },
16339     /**
16340      * onbeforeLoad - masks the loading area.
16341      *
16342      */
16343     onBeforeLoad : function(store,opts)
16344     {
16345          //Roo.log('onBeforeLoad');   
16346         if (!opts.add) {
16347             this.el.update("");
16348         }
16349         this.el.mask(this.mask ? this.mask : "Loading" ); 
16350     },
16351     onLoad : function ()
16352     {
16353         this.el.unmask();
16354     },
16355     
16356
16357     /**
16358      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16359      * @param {HTMLElement} node
16360      * @return {HTMLElement} The template node
16361      */
16362     findItemFromChild : function(node){
16363         var el = this.dataName  ?
16364             this.el.child('.roo-tpl-' + this.dataName,true) :
16365             this.el.dom; 
16366         
16367         if(!node || node.parentNode == el){
16368                     return node;
16369             }
16370             var p = node.parentNode;
16371             while(p && p != el){
16372             if(p.parentNode == el){
16373                 return p;
16374             }
16375             p = p.parentNode;
16376         }
16377             return null;
16378     },
16379
16380     /** @ignore */
16381     onClick : function(e){
16382         var item = this.findItemFromChild(e.getTarget());
16383         if(item){
16384             var index = this.indexOf(item);
16385             if(this.onItemClick(item, index, e) !== false){
16386                 this.fireEvent("click", this, index, item, e);
16387             }
16388         }else{
16389             this.clearSelections();
16390         }
16391     },
16392
16393     /** @ignore */
16394     onContextMenu : function(e){
16395         var item = this.findItemFromChild(e.getTarget());
16396         if(item){
16397             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16398         }
16399     },
16400
16401     /** @ignore */
16402     onDblClick : function(e){
16403         var item = this.findItemFromChild(e.getTarget());
16404         if(item){
16405             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16406         }
16407     },
16408
16409     onItemClick : function(item, index, e)
16410     {
16411         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16412             return false;
16413         }
16414         if (this.toggleSelect) {
16415             var m = this.isSelected(item) ? 'unselect' : 'select';
16416             //Roo.log(m);
16417             var _t = this;
16418             _t[m](item, true, false);
16419             return true;
16420         }
16421         if(this.multiSelect || this.singleSelect){
16422             if(this.multiSelect && e.shiftKey && this.lastSelection){
16423                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16424             }else{
16425                 this.select(item, this.multiSelect && e.ctrlKey);
16426                 this.lastSelection = item;
16427             }
16428             
16429             if(!this.tickable){
16430                 e.preventDefault();
16431             }
16432             
16433         }
16434         return true;
16435     },
16436
16437     /**
16438      * Get the number of selected nodes.
16439      * @return {Number}
16440      */
16441     getSelectionCount : function(){
16442         return this.selections.length;
16443     },
16444
16445     /**
16446      * Get the currently selected nodes.
16447      * @return {Array} An array of HTMLElements
16448      */
16449     getSelectedNodes : function(){
16450         return this.selections;
16451     },
16452
16453     /**
16454      * Get the indexes of the selected nodes.
16455      * @return {Array}
16456      */
16457     getSelectedIndexes : function(){
16458         var indexes = [], s = this.selections;
16459         for(var i = 0, len = s.length; i < len; i++){
16460             indexes.push(s[i].nodeIndex);
16461         }
16462         return indexes;
16463     },
16464
16465     /**
16466      * Clear all selections
16467      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16468      */
16469     clearSelections : function(suppressEvent){
16470         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16471             this.cmp.elements = this.selections;
16472             this.cmp.removeClass(this.selectedClass);
16473             this.selections = [];
16474             if(!suppressEvent){
16475                 this.fireEvent("selectionchange", this, this.selections);
16476             }
16477         }
16478     },
16479
16480     /**
16481      * Returns true if the passed node is selected
16482      * @param {HTMLElement/Number} node The node or node index
16483      * @return {Boolean}
16484      */
16485     isSelected : function(node){
16486         var s = this.selections;
16487         if(s.length < 1){
16488             return false;
16489         }
16490         node = this.getNode(node);
16491         return s.indexOf(node) !== -1;
16492     },
16493
16494     /**
16495      * Selects nodes.
16496      * @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
16497      * @param {Boolean} keepExisting (optional) true to keep existing selections
16498      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16499      */
16500     select : function(nodeInfo, keepExisting, suppressEvent){
16501         if(nodeInfo instanceof Array){
16502             if(!keepExisting){
16503                 this.clearSelections(true);
16504             }
16505             for(var i = 0, len = nodeInfo.length; i < len; i++){
16506                 this.select(nodeInfo[i], true, true);
16507             }
16508             return;
16509         } 
16510         var node = this.getNode(nodeInfo);
16511         if(!node || this.isSelected(node)){
16512             return; // already selected.
16513         }
16514         if(!keepExisting){
16515             this.clearSelections(true);
16516         }
16517         
16518         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16519             Roo.fly(node).addClass(this.selectedClass);
16520             this.selections.push(node);
16521             if(!suppressEvent){
16522                 this.fireEvent("selectionchange", this, this.selections);
16523             }
16524         }
16525         
16526         
16527     },
16528       /**
16529      * Unselects nodes.
16530      * @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
16531      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16532      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16533      */
16534     unselect : function(nodeInfo, keepExisting, suppressEvent)
16535     {
16536         if(nodeInfo instanceof Array){
16537             Roo.each(this.selections, function(s) {
16538                 this.unselect(s, nodeInfo);
16539             }, this);
16540             return;
16541         }
16542         var node = this.getNode(nodeInfo);
16543         if(!node || !this.isSelected(node)){
16544             //Roo.log("not selected");
16545             return; // not selected.
16546         }
16547         // fireevent???
16548         var ns = [];
16549         Roo.each(this.selections, function(s) {
16550             if (s == node ) {
16551                 Roo.fly(node).removeClass(this.selectedClass);
16552
16553                 return;
16554             }
16555             ns.push(s);
16556         },this);
16557         
16558         this.selections= ns;
16559         this.fireEvent("selectionchange", this, this.selections);
16560     },
16561
16562     /**
16563      * Gets a template node.
16564      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16565      * @return {HTMLElement} The node or null if it wasn't found
16566      */
16567     getNode : function(nodeInfo){
16568         if(typeof nodeInfo == "string"){
16569             return document.getElementById(nodeInfo);
16570         }else if(typeof nodeInfo == "number"){
16571             return this.nodes[nodeInfo];
16572         }
16573         return nodeInfo;
16574     },
16575
16576     /**
16577      * Gets a range template nodes.
16578      * @param {Number} startIndex
16579      * @param {Number} endIndex
16580      * @return {Array} An array of nodes
16581      */
16582     getNodes : function(start, end){
16583         var ns = this.nodes;
16584         start = start || 0;
16585         end = typeof end == "undefined" ? ns.length - 1 : end;
16586         var nodes = [];
16587         if(start <= end){
16588             for(var i = start; i <= end; i++){
16589                 nodes.push(ns[i]);
16590             }
16591         } else{
16592             for(var i = start; i >= end; i--){
16593                 nodes.push(ns[i]);
16594             }
16595         }
16596         return nodes;
16597     },
16598
16599     /**
16600      * Finds the index of the passed node
16601      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16602      * @return {Number} The index of the node or -1
16603      */
16604     indexOf : function(node){
16605         node = this.getNode(node);
16606         if(typeof node.nodeIndex == "number"){
16607             return node.nodeIndex;
16608         }
16609         var ns = this.nodes;
16610         for(var i = 0, len = ns.length; i < len; i++){
16611             if(ns[i] == node){
16612                 return i;
16613             }
16614         }
16615         return -1;
16616     }
16617 });
16618 /*
16619  * - LGPL
16620  *
16621  * based on jquery fullcalendar
16622  * 
16623  */
16624
16625 Roo.bootstrap = Roo.bootstrap || {};
16626 /**
16627  * @class Roo.bootstrap.Calendar
16628  * @extends Roo.bootstrap.Component
16629  * Bootstrap Calendar class
16630  * @cfg {Boolean} loadMask (true|false) default false
16631  * @cfg {Object} header generate the user specific header of the calendar, default false
16632
16633  * @constructor
16634  * Create a new Container
16635  * @param {Object} config The config object
16636  */
16637
16638
16639
16640 Roo.bootstrap.Calendar = function(config){
16641     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16642      this.addEvents({
16643         /**
16644              * @event select
16645              * Fires when a date is selected
16646              * @param {DatePicker} this
16647              * @param {Date} date The selected date
16648              */
16649         'select': true,
16650         /**
16651              * @event monthchange
16652              * Fires when the displayed month changes 
16653              * @param {DatePicker} this
16654              * @param {Date} date The selected month
16655              */
16656         'monthchange': true,
16657         /**
16658              * @event evententer
16659              * Fires when mouse over an event
16660              * @param {Calendar} this
16661              * @param {event} Event
16662              */
16663         'evententer': true,
16664         /**
16665              * @event eventleave
16666              * Fires when the mouse leaves an
16667              * @param {Calendar} this
16668              * @param {event}
16669              */
16670         'eventleave': true,
16671         /**
16672              * @event eventclick
16673              * Fires when the mouse click an
16674              * @param {Calendar} this
16675              * @param {event}
16676              */
16677         'eventclick': true
16678         
16679     });
16680
16681 };
16682
16683 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16684     
16685      /**
16686      * @cfg {Number} startDay
16687      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16688      */
16689     startDay : 0,
16690     
16691     loadMask : false,
16692     
16693     header : false,
16694       
16695     getAutoCreate : function(){
16696         
16697         
16698         var fc_button = function(name, corner, style, content ) {
16699             return Roo.apply({},{
16700                 tag : 'span',
16701                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16702                          (corner.length ?
16703                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16704                             ''
16705                         ),
16706                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16707                 unselectable: 'on'
16708             });
16709         };
16710         
16711         var header = {};
16712         
16713         if(!this.header){
16714             header = {
16715                 tag : 'table',
16716                 cls : 'fc-header',
16717                 style : 'width:100%',
16718                 cn : [
16719                     {
16720                         tag: 'tr',
16721                         cn : [
16722                             {
16723                                 tag : 'td',
16724                                 cls : 'fc-header-left',
16725                                 cn : [
16726                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16727                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16728                                     { tag: 'span', cls: 'fc-header-space' },
16729                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16730
16731
16732                                 ]
16733                             },
16734
16735                             {
16736                                 tag : 'td',
16737                                 cls : 'fc-header-center',
16738                                 cn : [
16739                                     {
16740                                         tag: 'span',
16741                                         cls: 'fc-header-title',
16742                                         cn : {
16743                                             tag: 'H2',
16744                                             html : 'month / year'
16745                                         }
16746                                     }
16747
16748                                 ]
16749                             },
16750                             {
16751                                 tag : 'td',
16752                                 cls : 'fc-header-right',
16753                                 cn : [
16754                               /*      fc_button('month', 'left', '', 'month' ),
16755                                     fc_button('week', '', '', 'week' ),
16756                                     fc_button('day', 'right', '', 'day' )
16757                                 */    
16758
16759                                 ]
16760                             }
16761
16762                         ]
16763                     }
16764                 ]
16765             };
16766         }
16767         
16768         header = this.header;
16769         
16770        
16771         var cal_heads = function() {
16772             var ret = [];
16773             // fixme - handle this.
16774             
16775             for (var i =0; i < Date.dayNames.length; i++) {
16776                 var d = Date.dayNames[i];
16777                 ret.push({
16778                     tag: 'th',
16779                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16780                     html : d.substring(0,3)
16781                 });
16782                 
16783             }
16784             ret[0].cls += ' fc-first';
16785             ret[6].cls += ' fc-last';
16786             return ret;
16787         };
16788         var cal_cell = function(n) {
16789             return  {
16790                 tag: 'td',
16791                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16792                 cn : [
16793                     {
16794                         cn : [
16795                             {
16796                                 cls: 'fc-day-number',
16797                                 html: 'D'
16798                             },
16799                             {
16800                                 cls: 'fc-day-content',
16801                              
16802                                 cn : [
16803                                      {
16804                                         style: 'position: relative;' // height: 17px;
16805                                     }
16806                                 ]
16807                             }
16808                             
16809                             
16810                         ]
16811                     }
16812                 ]
16813                 
16814             }
16815         };
16816         var cal_rows = function() {
16817             
16818             var ret = [];
16819             for (var r = 0; r < 6; r++) {
16820                 var row= {
16821                     tag : 'tr',
16822                     cls : 'fc-week',
16823                     cn : []
16824                 };
16825                 
16826                 for (var i =0; i < Date.dayNames.length; i++) {
16827                     var d = Date.dayNames[i];
16828                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16829
16830                 }
16831                 row.cn[0].cls+=' fc-first';
16832                 row.cn[0].cn[0].style = 'min-height:90px';
16833                 row.cn[6].cls+=' fc-last';
16834                 ret.push(row);
16835                 
16836             }
16837             ret[0].cls += ' fc-first';
16838             ret[4].cls += ' fc-prev-last';
16839             ret[5].cls += ' fc-last';
16840             return ret;
16841             
16842         };
16843         
16844         var cal_table = {
16845             tag: 'table',
16846             cls: 'fc-border-separate',
16847             style : 'width:100%',
16848             cellspacing  : 0,
16849             cn : [
16850                 { 
16851                     tag: 'thead',
16852                     cn : [
16853                         { 
16854                             tag: 'tr',
16855                             cls : 'fc-first fc-last',
16856                             cn : cal_heads()
16857                         }
16858                     ]
16859                 },
16860                 { 
16861                     tag: 'tbody',
16862                     cn : cal_rows()
16863                 }
16864                   
16865             ]
16866         };
16867          
16868          var cfg = {
16869             cls : 'fc fc-ltr',
16870             cn : [
16871                 header,
16872                 {
16873                     cls : 'fc-content',
16874                     style : "position: relative;",
16875                     cn : [
16876                         {
16877                             cls : 'fc-view fc-view-month fc-grid',
16878                             style : 'position: relative',
16879                             unselectable : 'on',
16880                             cn : [
16881                                 {
16882                                     cls : 'fc-event-container',
16883                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16884                                 },
16885                                 cal_table
16886                             ]
16887                         }
16888                     ]
16889     
16890                 }
16891            ] 
16892             
16893         };
16894         
16895          
16896         
16897         return cfg;
16898     },
16899     
16900     
16901     initEvents : function()
16902     {
16903         if(!this.store){
16904             throw "can not find store for calendar";
16905         }
16906         
16907         var mark = {
16908             tag: "div",
16909             cls:"x-dlg-mask",
16910             style: "text-align:center",
16911             cn: [
16912                 {
16913                     tag: "div",
16914                     style: "background-color:white;width:50%;margin:250 auto",
16915                     cn: [
16916                         {
16917                             tag: "img",
16918                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16919                         },
16920                         {
16921                             tag: "span",
16922                             html: "Loading"
16923                         }
16924                         
16925                     ]
16926                 }
16927             ]
16928         };
16929         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16930         
16931         var size = this.el.select('.fc-content', true).first().getSize();
16932         this.maskEl.setSize(size.width, size.height);
16933         this.maskEl.enableDisplayMode("block");
16934         if(!this.loadMask){
16935             this.maskEl.hide();
16936         }
16937         
16938         this.store = Roo.factory(this.store, Roo.data);
16939         this.store.on('load', this.onLoad, this);
16940         this.store.on('beforeload', this.onBeforeLoad, this);
16941         
16942         this.resize();
16943         
16944         this.cells = this.el.select('.fc-day',true);
16945         //Roo.log(this.cells);
16946         this.textNodes = this.el.query('.fc-day-number');
16947         this.cells.addClassOnOver('fc-state-hover');
16948         
16949         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16950         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16951         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16952         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16953         
16954         this.on('monthchange', this.onMonthChange, this);
16955         
16956         this.update(new Date().clearTime());
16957     },
16958     
16959     resize : function() {
16960         var sz  = this.el.getSize();
16961         
16962         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16963         this.el.select('.fc-day-content div',true).setHeight(34);
16964     },
16965     
16966     
16967     // private
16968     showPrevMonth : function(e){
16969         this.update(this.activeDate.add("mo", -1));
16970     },
16971     showToday : function(e){
16972         this.update(new Date().clearTime());
16973     },
16974     // private
16975     showNextMonth : function(e){
16976         this.update(this.activeDate.add("mo", 1));
16977     },
16978
16979     // private
16980     showPrevYear : function(){
16981         this.update(this.activeDate.add("y", -1));
16982     },
16983
16984     // private
16985     showNextYear : function(){
16986         this.update(this.activeDate.add("y", 1));
16987     },
16988
16989     
16990    // private
16991     update : function(date)
16992     {
16993         var vd = this.activeDate;
16994         this.activeDate = date;
16995 //        if(vd && this.el){
16996 //            var t = date.getTime();
16997 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16998 //                Roo.log('using add remove');
16999 //                
17000 //                this.fireEvent('monthchange', this, date);
17001 //                
17002 //                this.cells.removeClass("fc-state-highlight");
17003 //                this.cells.each(function(c){
17004 //                   if(c.dateValue == t){
17005 //                       c.addClass("fc-state-highlight");
17006 //                       setTimeout(function(){
17007 //                            try{c.dom.firstChild.focus();}catch(e){}
17008 //                       }, 50);
17009 //                       return false;
17010 //                   }
17011 //                   return true;
17012 //                });
17013 //                return;
17014 //            }
17015 //        }
17016         
17017         var days = date.getDaysInMonth();
17018         
17019         var firstOfMonth = date.getFirstDateOfMonth();
17020         var startingPos = firstOfMonth.getDay()-this.startDay;
17021         
17022         if(startingPos < this.startDay){
17023             startingPos += 7;
17024         }
17025         
17026         var pm = date.add(Date.MONTH, -1);
17027         var prevStart = pm.getDaysInMonth()-startingPos;
17028 //        
17029         this.cells = this.el.select('.fc-day',true);
17030         this.textNodes = this.el.query('.fc-day-number');
17031         this.cells.addClassOnOver('fc-state-hover');
17032         
17033         var cells = this.cells.elements;
17034         var textEls = this.textNodes;
17035         
17036         Roo.each(cells, function(cell){
17037             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17038         });
17039         
17040         days += startingPos;
17041
17042         // convert everything to numbers so it's fast
17043         var day = 86400000;
17044         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17045         //Roo.log(d);
17046         //Roo.log(pm);
17047         //Roo.log(prevStart);
17048         
17049         var today = new Date().clearTime().getTime();
17050         var sel = date.clearTime().getTime();
17051         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17052         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17053         var ddMatch = this.disabledDatesRE;
17054         var ddText = this.disabledDatesText;
17055         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17056         var ddaysText = this.disabledDaysText;
17057         var format = this.format;
17058         
17059         var setCellClass = function(cal, cell){
17060             cell.row = 0;
17061             cell.events = [];
17062             cell.more = [];
17063             //Roo.log('set Cell Class');
17064             cell.title = "";
17065             var t = d.getTime();
17066             
17067             //Roo.log(d);
17068             
17069             cell.dateValue = t;
17070             if(t == today){
17071                 cell.className += " fc-today";
17072                 cell.className += " fc-state-highlight";
17073                 cell.title = cal.todayText;
17074             }
17075             if(t == sel){
17076                 // disable highlight in other month..
17077                 //cell.className += " fc-state-highlight";
17078                 
17079             }
17080             // disabling
17081             if(t < min) {
17082                 cell.className = " fc-state-disabled";
17083                 cell.title = cal.minText;
17084                 return;
17085             }
17086             if(t > max) {
17087                 cell.className = " fc-state-disabled";
17088                 cell.title = cal.maxText;
17089                 return;
17090             }
17091             if(ddays){
17092                 if(ddays.indexOf(d.getDay()) != -1){
17093                     cell.title = ddaysText;
17094                     cell.className = " fc-state-disabled";
17095                 }
17096             }
17097             if(ddMatch && format){
17098                 var fvalue = d.dateFormat(format);
17099                 if(ddMatch.test(fvalue)){
17100                     cell.title = ddText.replace("%0", fvalue);
17101                     cell.className = " fc-state-disabled";
17102                 }
17103             }
17104             
17105             if (!cell.initialClassName) {
17106                 cell.initialClassName = cell.dom.className;
17107             }
17108             
17109             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17110         };
17111
17112         var i = 0;
17113         
17114         for(; i < startingPos; i++) {
17115             textEls[i].innerHTML = (++prevStart);
17116             d.setDate(d.getDate()+1);
17117             
17118             cells[i].className = "fc-past fc-other-month";
17119             setCellClass(this, cells[i]);
17120         }
17121         
17122         var intDay = 0;
17123         
17124         for(; i < days; i++){
17125             intDay = i - startingPos + 1;
17126             textEls[i].innerHTML = (intDay);
17127             d.setDate(d.getDate()+1);
17128             
17129             cells[i].className = ''; // "x-date-active";
17130             setCellClass(this, cells[i]);
17131         }
17132         var extraDays = 0;
17133         
17134         for(; i < 42; i++) {
17135             textEls[i].innerHTML = (++extraDays);
17136             d.setDate(d.getDate()+1);
17137             
17138             cells[i].className = "fc-future fc-other-month";
17139             setCellClass(this, cells[i]);
17140         }
17141         
17142         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17143         
17144         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17145         
17146         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17147         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17148         
17149         if(totalRows != 6){
17150             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17151             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17152         }
17153         
17154         this.fireEvent('monthchange', this, date);
17155         
17156         
17157         /*
17158         if(!this.internalRender){
17159             var main = this.el.dom.firstChild;
17160             var w = main.offsetWidth;
17161             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17162             Roo.fly(main).setWidth(w);
17163             this.internalRender = true;
17164             // opera does not respect the auto grow header center column
17165             // then, after it gets a width opera refuses to recalculate
17166             // without a second pass
17167             if(Roo.isOpera && !this.secondPass){
17168                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17169                 this.secondPass = true;
17170                 this.update.defer(10, this, [date]);
17171             }
17172         }
17173         */
17174         
17175     },
17176     
17177     findCell : function(dt) {
17178         dt = dt.clearTime().getTime();
17179         var ret = false;
17180         this.cells.each(function(c){
17181             //Roo.log("check " +c.dateValue + '?=' + dt);
17182             if(c.dateValue == dt){
17183                 ret = c;
17184                 return false;
17185             }
17186             return true;
17187         });
17188         
17189         return ret;
17190     },
17191     
17192     findCells : function(ev) {
17193         var s = ev.start.clone().clearTime().getTime();
17194        // Roo.log(s);
17195         var e= ev.end.clone().clearTime().getTime();
17196        // Roo.log(e);
17197         var ret = [];
17198         this.cells.each(function(c){
17199              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17200             
17201             if(c.dateValue > e){
17202                 return ;
17203             }
17204             if(c.dateValue < s){
17205                 return ;
17206             }
17207             ret.push(c);
17208         });
17209         
17210         return ret;    
17211     },
17212     
17213 //    findBestRow: function(cells)
17214 //    {
17215 //        var ret = 0;
17216 //        
17217 //        for (var i =0 ; i < cells.length;i++) {
17218 //            ret  = Math.max(cells[i].rows || 0,ret);
17219 //        }
17220 //        return ret;
17221 //        
17222 //    },
17223     
17224     
17225     addItem : function(ev)
17226     {
17227         // look for vertical location slot in
17228         var cells = this.findCells(ev);
17229         
17230 //        ev.row = this.findBestRow(cells);
17231         
17232         // work out the location.
17233         
17234         var crow = false;
17235         var rows = [];
17236         for(var i =0; i < cells.length; i++) {
17237             
17238             cells[i].row = cells[0].row;
17239             
17240             if(i == 0){
17241                 cells[i].row = cells[i].row + 1;
17242             }
17243             
17244             if (!crow) {
17245                 crow = {
17246                     start : cells[i],
17247                     end :  cells[i]
17248                 };
17249                 continue;
17250             }
17251             if (crow.start.getY() == cells[i].getY()) {
17252                 // on same row.
17253                 crow.end = cells[i];
17254                 continue;
17255             }
17256             // different row.
17257             rows.push(crow);
17258             crow = {
17259                 start: cells[i],
17260                 end : cells[i]
17261             };
17262             
17263         }
17264         
17265         rows.push(crow);
17266         ev.els = [];
17267         ev.rows = rows;
17268         ev.cells = cells;
17269         
17270         cells[0].events.push(ev);
17271         
17272         this.calevents.push(ev);
17273     },
17274     
17275     clearEvents: function() {
17276         
17277         if(!this.calevents){
17278             return;
17279         }
17280         
17281         Roo.each(this.cells.elements, function(c){
17282             c.row = 0;
17283             c.events = [];
17284             c.more = [];
17285         });
17286         
17287         Roo.each(this.calevents, function(e) {
17288             Roo.each(e.els, function(el) {
17289                 el.un('mouseenter' ,this.onEventEnter, this);
17290                 el.un('mouseleave' ,this.onEventLeave, this);
17291                 el.remove();
17292             },this);
17293         },this);
17294         
17295         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17296             e.remove();
17297         });
17298         
17299     },
17300     
17301     renderEvents: function()
17302     {   
17303         var _this = this;
17304         
17305         this.cells.each(function(c) {
17306             
17307             if(c.row < 5){
17308                 return;
17309             }
17310             
17311             var ev = c.events;
17312             
17313             var r = 4;
17314             if(c.row != c.events.length){
17315                 r = 4 - (4 - (c.row - c.events.length));
17316             }
17317             
17318             c.events = ev.slice(0, r);
17319             c.more = ev.slice(r);
17320             
17321             if(c.more.length && c.more.length == 1){
17322                 c.events.push(c.more.pop());
17323             }
17324             
17325             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17326             
17327         });
17328             
17329         this.cells.each(function(c) {
17330             
17331             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17332             
17333             
17334             for (var e = 0; e < c.events.length; e++){
17335                 var ev = c.events[e];
17336                 var rows = ev.rows;
17337                 
17338                 for(var i = 0; i < rows.length; i++) {
17339                 
17340                     // how many rows should it span..
17341
17342                     var  cfg = {
17343                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17344                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17345
17346                         unselectable : "on",
17347                         cn : [
17348                             {
17349                                 cls: 'fc-event-inner',
17350                                 cn : [
17351     //                                {
17352     //                                  tag:'span',
17353     //                                  cls: 'fc-event-time',
17354     //                                  html : cells.length > 1 ? '' : ev.time
17355     //                                },
17356                                     {
17357                                       tag:'span',
17358                                       cls: 'fc-event-title',
17359                                       html : String.format('{0}', ev.title)
17360                                     }
17361
17362
17363                                 ]
17364                             },
17365                             {
17366                                 cls: 'ui-resizable-handle ui-resizable-e',
17367                                 html : '&nbsp;&nbsp;&nbsp'
17368                             }
17369
17370                         ]
17371                     };
17372
17373                     if (i == 0) {
17374                         cfg.cls += ' fc-event-start';
17375                     }
17376                     if ((i+1) == rows.length) {
17377                         cfg.cls += ' fc-event-end';
17378                     }
17379
17380                     var ctr = _this.el.select('.fc-event-container',true).first();
17381                     var cg = ctr.createChild(cfg);
17382
17383                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17384                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17385
17386                     var r = (c.more.length) ? 1 : 0;
17387                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17388                     cg.setWidth(ebox.right - sbox.x -2);
17389
17390                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17391                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17392                     cg.on('click', _this.onEventClick, _this, ev);
17393
17394                     ev.els.push(cg);
17395                     
17396                 }
17397                 
17398             }
17399             
17400             
17401             if(c.more.length){
17402                 var  cfg = {
17403                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17404                     style : 'position: absolute',
17405                     unselectable : "on",
17406                     cn : [
17407                         {
17408                             cls: 'fc-event-inner',
17409                             cn : [
17410                                 {
17411                                   tag:'span',
17412                                   cls: 'fc-event-title',
17413                                   html : 'More'
17414                                 }
17415
17416
17417                             ]
17418                         },
17419                         {
17420                             cls: 'ui-resizable-handle ui-resizable-e',
17421                             html : '&nbsp;&nbsp;&nbsp'
17422                         }
17423
17424                     ]
17425                 };
17426
17427                 var ctr = _this.el.select('.fc-event-container',true).first();
17428                 var cg = ctr.createChild(cfg);
17429
17430                 var sbox = c.select('.fc-day-content',true).first().getBox();
17431                 var ebox = c.select('.fc-day-content',true).first().getBox();
17432                 //Roo.log(cg);
17433                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17434                 cg.setWidth(ebox.right - sbox.x -2);
17435
17436                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17437                 
17438             }
17439             
17440         });
17441         
17442         
17443         
17444     },
17445     
17446     onEventEnter: function (e, el,event,d) {
17447         this.fireEvent('evententer', this, el, event);
17448     },
17449     
17450     onEventLeave: function (e, el,event,d) {
17451         this.fireEvent('eventleave', this, el, event);
17452     },
17453     
17454     onEventClick: function (e, el,event,d) {
17455         this.fireEvent('eventclick', this, el, event);
17456     },
17457     
17458     onMonthChange: function () {
17459         this.store.load();
17460     },
17461     
17462     onMoreEventClick: function(e, el, more)
17463     {
17464         var _this = this;
17465         
17466         this.calpopover.placement = 'right';
17467         this.calpopover.setTitle('More');
17468         
17469         this.calpopover.setContent('');
17470         
17471         var ctr = this.calpopover.el.select('.popover-content', true).first();
17472         
17473         Roo.each(more, function(m){
17474             var cfg = {
17475                 cls : 'fc-event-hori fc-event-draggable',
17476                 html : m.title
17477             };
17478             var cg = ctr.createChild(cfg);
17479             
17480             cg.on('click', _this.onEventClick, _this, m);
17481         });
17482         
17483         this.calpopover.show(el);
17484         
17485         
17486     },
17487     
17488     onLoad: function () 
17489     {   
17490         this.calevents = [];
17491         var cal = this;
17492         
17493         if(this.store.getCount() > 0){
17494             this.store.data.each(function(d){
17495                cal.addItem({
17496                     id : d.data.id,
17497                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17498                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17499                     time : d.data.start_time,
17500                     title : d.data.title,
17501                     description : d.data.description,
17502                     venue : d.data.venue
17503                 });
17504             });
17505         }
17506         
17507         this.renderEvents();
17508         
17509         if(this.calevents.length && this.loadMask){
17510             this.maskEl.hide();
17511         }
17512     },
17513     
17514     onBeforeLoad: function()
17515     {
17516         this.clearEvents();
17517         if(this.loadMask){
17518             this.maskEl.show();
17519         }
17520     }
17521 });
17522
17523  
17524  /*
17525  * - LGPL
17526  *
17527  * element
17528  * 
17529  */
17530
17531 /**
17532  * @class Roo.bootstrap.Popover
17533  * @extends Roo.bootstrap.Component
17534  * Bootstrap Popover class
17535  * @cfg {String} html contents of the popover   (or false to use children..)
17536  * @cfg {String} title of popover (or false to hide)
17537  * @cfg {String} placement how it is placed
17538  * @cfg {String} trigger click || hover (or false to trigger manually)
17539  * @cfg {String} over what (parent or false to trigger manually.)
17540  * @cfg {Number} delay - delay before showing
17541  
17542  * @constructor
17543  * Create a new Popover
17544  * @param {Object} config The config object
17545  */
17546
17547 Roo.bootstrap.Popover = function(config){
17548     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17549     
17550     this.addEvents({
17551         // raw events
17552          /**
17553          * @event show
17554          * After the popover show
17555          * 
17556          * @param {Roo.bootstrap.Popover} this
17557          */
17558         "show" : true,
17559         /**
17560          * @event hide
17561          * After the popover hide
17562          * 
17563          * @param {Roo.bootstrap.Popover} this
17564          */
17565         "hide" : true
17566     });
17567 };
17568
17569 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17570     
17571     title: 'Fill in a title',
17572     html: false,
17573     
17574     placement : 'right',
17575     trigger : 'hover', // hover
17576     
17577     delay : 0,
17578     
17579     over: 'parent',
17580     
17581     can_build_overlaid : false,
17582     
17583     getChildContainer : function()
17584     {
17585         return this.el.select('.popover-content',true).first();
17586     },
17587     
17588     getAutoCreate : function(){
17589          
17590         var cfg = {
17591            cls : 'popover roo-dynamic',
17592            style: 'display:block',
17593            cn : [
17594                 {
17595                     cls : 'arrow'
17596                 },
17597                 {
17598                     cls : 'popover-inner',
17599                     cn : [
17600                         {
17601                             tag: 'h3',
17602                             cls: 'popover-title',
17603                             html : this.title
17604                         },
17605                         {
17606                             cls : 'popover-content',
17607                             html : this.html
17608                         }
17609                     ]
17610                     
17611                 }
17612            ]
17613         };
17614         
17615         return cfg;
17616     },
17617     setTitle: function(str)
17618     {
17619         this.title = str;
17620         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17621     },
17622     setContent: function(str)
17623     {
17624         this.html = str;
17625         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17626     },
17627     // as it get's added to the bottom of the page.
17628     onRender : function(ct, position)
17629     {
17630         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17631         if(!this.el){
17632             var cfg = Roo.apply({},  this.getAutoCreate());
17633             cfg.id = Roo.id();
17634             
17635             if (this.cls) {
17636                 cfg.cls += ' ' + this.cls;
17637             }
17638             if (this.style) {
17639                 cfg.style = this.style;
17640             }
17641             //Roo.log("adding to ");
17642             this.el = Roo.get(document.body).createChild(cfg, position);
17643 //            Roo.log(this.el);
17644         }
17645         this.initEvents();
17646     },
17647     
17648     initEvents : function()
17649     {
17650         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17651         this.el.enableDisplayMode('block');
17652         this.el.hide();
17653         if (this.over === false) {
17654             return; 
17655         }
17656         if (this.triggers === false) {
17657             return;
17658         }
17659         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17660         var triggers = this.trigger ? this.trigger.split(' ') : [];
17661         Roo.each(triggers, function(trigger) {
17662         
17663             if (trigger == 'click') {
17664                 on_el.on('click', this.toggle, this);
17665             } else if (trigger != 'manual') {
17666                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17667                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17668       
17669                 on_el.on(eventIn  ,this.enter, this);
17670                 on_el.on(eventOut, this.leave, this);
17671             }
17672         }, this);
17673         
17674     },
17675     
17676     
17677     // private
17678     timeout : null,
17679     hoverState : null,
17680     
17681     toggle : function () {
17682         this.hoverState == 'in' ? this.leave() : this.enter();
17683     },
17684     
17685     enter : function () {
17686         
17687         clearTimeout(this.timeout);
17688     
17689         this.hoverState = 'in';
17690     
17691         if (!this.delay || !this.delay.show) {
17692             this.show();
17693             return;
17694         }
17695         var _t = this;
17696         this.timeout = setTimeout(function () {
17697             if (_t.hoverState == 'in') {
17698                 _t.show();
17699             }
17700         }, this.delay.show)
17701     },
17702     
17703     leave : function() {
17704         clearTimeout(this.timeout);
17705     
17706         this.hoverState = 'out';
17707     
17708         if (!this.delay || !this.delay.hide) {
17709             this.hide();
17710             return;
17711         }
17712         var _t = this;
17713         this.timeout = setTimeout(function () {
17714             if (_t.hoverState == 'out') {
17715                 _t.hide();
17716             }
17717         }, this.delay.hide)
17718     },
17719     
17720     show : function (on_el)
17721     {
17722         if (!on_el) {
17723             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17724         }
17725         
17726         // set content.
17727         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17728         if (this.html !== false) {
17729             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17730         }
17731         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17732         if (!this.title.length) {
17733             this.el.select('.popover-title',true).hide();
17734         }
17735         
17736         var placement = typeof this.placement == 'function' ?
17737             this.placement.call(this, this.el, on_el) :
17738             this.placement;
17739             
17740         var autoToken = /\s?auto?\s?/i;
17741         var autoPlace = autoToken.test(placement);
17742         if (autoPlace) {
17743             placement = placement.replace(autoToken, '') || 'top';
17744         }
17745         
17746         //this.el.detach()
17747         //this.el.setXY([0,0]);
17748         this.el.show();
17749         this.el.dom.style.display='block';
17750         this.el.addClass(placement);
17751         
17752         //this.el.appendTo(on_el);
17753         
17754         var p = this.getPosition();
17755         var box = this.el.getBox();
17756         
17757         if (autoPlace) {
17758             // fixme..
17759         }
17760         var align = Roo.bootstrap.Popover.alignment[placement];
17761         
17762 //        Roo.log(align);
17763         this.el.alignTo(on_el, align[0],align[1]);
17764         //var arrow = this.el.select('.arrow',true).first();
17765         //arrow.set(align[2], 
17766         
17767         this.el.addClass('in');
17768         
17769         
17770         if (this.el.hasClass('fade')) {
17771             // fade it?
17772         }
17773         
17774         this.hoverState = 'in';
17775         
17776         this.fireEvent('show', this);
17777         
17778     },
17779     hide : function()
17780     {
17781         this.el.setXY([0,0]);
17782         this.el.removeClass('in');
17783         this.el.hide();
17784         this.hoverState = null;
17785         
17786         this.fireEvent('hide', this);
17787     }
17788     
17789 });
17790
17791 Roo.bootstrap.Popover.alignment = {
17792     'left' : ['r-l', [-10,0], 'right'],
17793     'right' : ['l-r', [10,0], 'left'],
17794     'bottom' : ['t-b', [0,10], 'top'],
17795     'top' : [ 'b-t', [0,-10], 'bottom']
17796 };
17797
17798  /*
17799  * - LGPL
17800  *
17801  * Progress
17802  * 
17803  */
17804
17805 /**
17806  * @class Roo.bootstrap.Progress
17807  * @extends Roo.bootstrap.Component
17808  * Bootstrap Progress class
17809  * @cfg {Boolean} striped striped of the progress bar
17810  * @cfg {Boolean} active animated of the progress bar
17811  * 
17812  * 
17813  * @constructor
17814  * Create a new Progress
17815  * @param {Object} config The config object
17816  */
17817
17818 Roo.bootstrap.Progress = function(config){
17819     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17820 };
17821
17822 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17823     
17824     striped : false,
17825     active: false,
17826     
17827     getAutoCreate : function(){
17828         var cfg = {
17829             tag: 'div',
17830             cls: 'progress'
17831         };
17832         
17833         
17834         if(this.striped){
17835             cfg.cls += ' progress-striped';
17836         }
17837       
17838         if(this.active){
17839             cfg.cls += ' active';
17840         }
17841         
17842         
17843         return cfg;
17844     }
17845    
17846 });
17847
17848  
17849
17850  /*
17851  * - LGPL
17852  *
17853  * ProgressBar
17854  * 
17855  */
17856
17857 /**
17858  * @class Roo.bootstrap.ProgressBar
17859  * @extends Roo.bootstrap.Component
17860  * Bootstrap ProgressBar class
17861  * @cfg {Number} aria_valuenow aria-value now
17862  * @cfg {Number} aria_valuemin aria-value min
17863  * @cfg {Number} aria_valuemax aria-value max
17864  * @cfg {String} label label for the progress bar
17865  * @cfg {String} panel (success | info | warning | danger )
17866  * @cfg {String} role role of the progress bar
17867  * @cfg {String} sr_only text
17868  * 
17869  * 
17870  * @constructor
17871  * Create a new ProgressBar
17872  * @param {Object} config The config object
17873  */
17874
17875 Roo.bootstrap.ProgressBar = function(config){
17876     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17877 };
17878
17879 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17880     
17881     aria_valuenow : 0,
17882     aria_valuemin : 0,
17883     aria_valuemax : 100,
17884     label : false,
17885     panel : false,
17886     role : false,
17887     sr_only: false,
17888     
17889     getAutoCreate : function()
17890     {
17891         
17892         var cfg = {
17893             tag: 'div',
17894             cls: 'progress-bar',
17895             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17896         };
17897         
17898         if(this.sr_only){
17899             cfg.cn = {
17900                 tag: 'span',
17901                 cls: 'sr-only',
17902                 html: this.sr_only
17903             }
17904         }
17905         
17906         if(this.role){
17907             cfg.role = this.role;
17908         }
17909         
17910         if(this.aria_valuenow){
17911             cfg['aria-valuenow'] = this.aria_valuenow;
17912         }
17913         
17914         if(this.aria_valuemin){
17915             cfg['aria-valuemin'] = this.aria_valuemin;
17916         }
17917         
17918         if(this.aria_valuemax){
17919             cfg['aria-valuemax'] = this.aria_valuemax;
17920         }
17921         
17922         if(this.label && !this.sr_only){
17923             cfg.html = this.label;
17924         }
17925         
17926         if(this.panel){
17927             cfg.cls += ' progress-bar-' + this.panel;
17928         }
17929         
17930         return cfg;
17931     },
17932     
17933     update : function(aria_valuenow)
17934     {
17935         this.aria_valuenow = aria_valuenow;
17936         
17937         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17938     }
17939    
17940 });
17941
17942  
17943
17944  /*
17945  * - LGPL
17946  *
17947  * column
17948  * 
17949  */
17950
17951 /**
17952  * @class Roo.bootstrap.TabGroup
17953  * @extends Roo.bootstrap.Column
17954  * Bootstrap Column class
17955  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17956  * @cfg {Boolean} carousel true to make the group behave like a carousel
17957  * @cfg {Boolean} bullets show bullets for the panels
17958  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17959  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17960  * @cfg {Boolean} showarrow (true|false) show arrow default true
17961  * 
17962  * @constructor
17963  * Create a new TabGroup
17964  * @param {Object} config The config object
17965  */
17966
17967 Roo.bootstrap.TabGroup = function(config){
17968     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17969     if (!this.navId) {
17970         this.navId = Roo.id();
17971     }
17972     this.tabs = [];
17973     Roo.bootstrap.TabGroup.register(this);
17974     
17975 };
17976
17977 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17978     
17979     carousel : false,
17980     transition : false,
17981     bullets : 0,
17982     timer : 0,
17983     autoslide : false,
17984     slideFn : false,
17985     slideOnTouch : false,
17986     showarrow : true,
17987     
17988     getAutoCreate : function()
17989     {
17990         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17991         
17992         cfg.cls += ' tab-content';
17993         
17994         if (this.carousel) {
17995             cfg.cls += ' carousel slide';
17996             
17997             cfg.cn = [{
17998                cls : 'carousel-inner',
17999                cn : []
18000             }];
18001         
18002             if(this.bullets  && !Roo.isTouch){
18003                 
18004                 var bullets = {
18005                     cls : 'carousel-bullets',
18006                     cn : []
18007                 };
18008                
18009                 if(this.bullets_cls){
18010                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18011                 }
18012                 
18013                 bullets.cn.push({
18014                     cls : 'clear'
18015                 });
18016                 
18017                 cfg.cn[0].cn.push(bullets);
18018             }
18019             
18020             if(this.showarrow){
18021                 cfg.cn[0].cn.push({
18022                     tag : 'div',
18023                     class : 'carousel-arrow',
18024                     cn : [
18025                         {
18026                             tag : 'div',
18027                             class : 'carousel-prev',
18028                             cn : [
18029                                 {
18030                                     tag : 'i',
18031                                     class : 'fa fa-chevron-left'
18032                                 }
18033                             ]
18034                         },
18035                         {
18036                             tag : 'div',
18037                             class : 'carousel-next',
18038                             cn : [
18039                                 {
18040                                     tag : 'i',
18041                                     class : 'fa fa-chevron-right'
18042                                 }
18043                             ]
18044                         }
18045                     ]
18046                 });
18047             }
18048             
18049         }
18050         
18051         return cfg;
18052     },
18053     
18054     initEvents:  function()
18055     {
18056 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18057 //            this.el.on("touchstart", this.onTouchStart, this);
18058 //        }
18059         
18060         if(this.autoslide){
18061             var _this = this;
18062             
18063             this.slideFn = window.setInterval(function() {
18064                 _this.showPanelNext();
18065             }, this.timer);
18066         }
18067         
18068         if(this.showarrow){
18069             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18070             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18071         }
18072         
18073         
18074     },
18075     
18076 //    onTouchStart : function(e, el, o)
18077 //    {
18078 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18079 //            return;
18080 //        }
18081 //        
18082 //        this.showPanelNext();
18083 //    },
18084     
18085     
18086     getChildContainer : function()
18087     {
18088         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18089     },
18090     
18091     /**
18092     * register a Navigation item
18093     * @param {Roo.bootstrap.NavItem} the navitem to add
18094     */
18095     register : function(item)
18096     {
18097         this.tabs.push( item);
18098         item.navId = this.navId; // not really needed..
18099         this.addBullet();
18100     
18101     },
18102     
18103     getActivePanel : function()
18104     {
18105         var r = false;
18106         Roo.each(this.tabs, function(t) {
18107             if (t.active) {
18108                 r = t;
18109                 return false;
18110             }
18111             return null;
18112         });
18113         return r;
18114         
18115     },
18116     getPanelByName : function(n)
18117     {
18118         var r = false;
18119         Roo.each(this.tabs, function(t) {
18120             if (t.tabId == n) {
18121                 r = t;
18122                 return false;
18123             }
18124             return null;
18125         });
18126         return r;
18127     },
18128     indexOfPanel : function(p)
18129     {
18130         var r = false;
18131         Roo.each(this.tabs, function(t,i) {
18132             if (t.tabId == p.tabId) {
18133                 r = i;
18134                 return false;
18135             }
18136             return null;
18137         });
18138         return r;
18139     },
18140     /**
18141      * show a specific panel
18142      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18143      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18144      */
18145     showPanel : function (pan)
18146     {
18147         if(this.transition || typeof(pan) == 'undefined'){
18148             Roo.log("waiting for the transitionend");
18149             return;
18150         }
18151         
18152         if (typeof(pan) == 'number') {
18153             pan = this.tabs[pan];
18154         }
18155         
18156         if (typeof(pan) == 'string') {
18157             pan = this.getPanelByName(pan);
18158         }
18159         
18160         var cur = this.getActivePanel();
18161         
18162         if(!pan || !cur){
18163             Roo.log('pan or acitve pan is undefined');
18164             return false;
18165         }
18166         
18167         if (pan.tabId == this.getActivePanel().tabId) {
18168             return true;
18169         }
18170         
18171         if (false === cur.fireEvent('beforedeactivate')) {
18172             return false;
18173         }
18174         
18175         if(this.bullets > 0 && !Roo.isTouch){
18176             this.setActiveBullet(this.indexOfPanel(pan));
18177         }
18178         
18179         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18180             
18181             this.transition = true;
18182             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18183             var lr = dir == 'next' ? 'left' : 'right';
18184             pan.el.addClass(dir); // or prev
18185             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18186             cur.el.addClass(lr); // or right
18187             pan.el.addClass(lr);
18188             
18189             var _this = this;
18190             cur.el.on('transitionend', function() {
18191                 Roo.log("trans end?");
18192                 
18193                 pan.el.removeClass([lr,dir]);
18194                 pan.setActive(true);
18195                 
18196                 cur.el.removeClass([lr]);
18197                 cur.setActive(false);
18198                 
18199                 _this.transition = false;
18200                 
18201             }, this, { single:  true } );
18202             
18203             return true;
18204         }
18205         
18206         cur.setActive(false);
18207         pan.setActive(true);
18208         
18209         return true;
18210         
18211     },
18212     showPanelNext : function()
18213     {
18214         var i = this.indexOfPanel(this.getActivePanel());
18215         
18216         if (i >= this.tabs.length - 1 && !this.autoslide) {
18217             return;
18218         }
18219         
18220         if (i >= this.tabs.length - 1 && this.autoslide) {
18221             i = -1;
18222         }
18223         
18224         this.showPanel(this.tabs[i+1]);
18225     },
18226     
18227     showPanelPrev : function()
18228     {
18229         var i = this.indexOfPanel(this.getActivePanel());
18230         
18231         if (i  < 1 && !this.autoslide) {
18232             return;
18233         }
18234         
18235         if (i < 1 && this.autoslide) {
18236             i = this.tabs.length;
18237         }
18238         
18239         this.showPanel(this.tabs[i-1]);
18240     },
18241     
18242     
18243     addBullet: function()
18244     {
18245         if(!this.bullets || Roo.isTouch){
18246             return;
18247         }
18248         var ctr = this.el.select('.carousel-bullets',true).first();
18249         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18250         var bullet = ctr.createChild({
18251             cls : 'bullet bullet-' + i
18252         },ctr.dom.lastChild);
18253         
18254         
18255         var _this = this;
18256         
18257         bullet.on('click', (function(e, el, o, ii, t){
18258
18259             e.preventDefault();
18260
18261             this.showPanel(ii);
18262
18263             if(this.autoslide && this.slideFn){
18264                 clearInterval(this.slideFn);
18265                 this.slideFn = window.setInterval(function() {
18266                     _this.showPanelNext();
18267                 }, this.timer);
18268             }
18269
18270         }).createDelegate(this, [i, bullet], true));
18271                 
18272         
18273     },
18274      
18275     setActiveBullet : function(i)
18276     {
18277         if(Roo.isTouch){
18278             return;
18279         }
18280         
18281         Roo.each(this.el.select('.bullet', true).elements, function(el){
18282             el.removeClass('selected');
18283         });
18284
18285         var bullet = this.el.select('.bullet-' + i, true).first();
18286         
18287         if(!bullet){
18288             return;
18289         }
18290         
18291         bullet.addClass('selected');
18292     }
18293     
18294     
18295   
18296 });
18297
18298  
18299
18300  
18301  
18302 Roo.apply(Roo.bootstrap.TabGroup, {
18303     
18304     groups: {},
18305      /**
18306     * register a Navigation Group
18307     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18308     */
18309     register : function(navgrp)
18310     {
18311         this.groups[navgrp.navId] = navgrp;
18312         
18313     },
18314     /**
18315     * fetch a Navigation Group based on the navigation ID
18316     * if one does not exist , it will get created.
18317     * @param {string} the navgroup to add
18318     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18319     */
18320     get: function(navId) {
18321         if (typeof(this.groups[navId]) == 'undefined') {
18322             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18323         }
18324         return this.groups[navId] ;
18325     }
18326     
18327     
18328     
18329 });
18330
18331  /*
18332  * - LGPL
18333  *
18334  * TabPanel
18335  * 
18336  */
18337
18338 /**
18339  * @class Roo.bootstrap.TabPanel
18340  * @extends Roo.bootstrap.Component
18341  * Bootstrap TabPanel class
18342  * @cfg {Boolean} active panel active
18343  * @cfg {String} html panel content
18344  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18345  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18346  * @cfg {String} href click to link..
18347  * 
18348  * 
18349  * @constructor
18350  * Create a new TabPanel
18351  * @param {Object} config The config object
18352  */
18353
18354 Roo.bootstrap.TabPanel = function(config){
18355     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18356     this.addEvents({
18357         /**
18358              * @event changed
18359              * Fires when the active status changes
18360              * @param {Roo.bootstrap.TabPanel} this
18361              * @param {Boolean} state the new state
18362             
18363          */
18364         'changed': true,
18365         /**
18366              * @event beforedeactivate
18367              * Fires before a tab is de-activated - can be used to do validation on a form.
18368              * @param {Roo.bootstrap.TabPanel} this
18369              * @return {Boolean} false if there is an error
18370             
18371          */
18372         'beforedeactivate': true
18373      });
18374     
18375     this.tabId = this.tabId || Roo.id();
18376   
18377 };
18378
18379 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18380     
18381     active: false,
18382     html: false,
18383     tabId: false,
18384     navId : false,
18385     href : '',
18386     
18387     getAutoCreate : function(){
18388         var cfg = {
18389             tag: 'div',
18390             // item is needed for carousel - not sure if it has any effect otherwise
18391             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18392             html: this.html || ''
18393         };
18394         
18395         if(this.active){
18396             cfg.cls += ' active';
18397         }
18398         
18399         if(this.tabId){
18400             cfg.tabId = this.tabId;
18401         }
18402         
18403         
18404         return cfg;
18405     },
18406     
18407     initEvents:  function()
18408     {
18409         var p = this.parent();
18410         
18411         this.navId = this.navId || p.navId;
18412         
18413         if (typeof(this.navId) != 'undefined') {
18414             // not really needed.. but just in case.. parent should be a NavGroup.
18415             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18416             
18417             tg.register(this);
18418             
18419             var i = tg.tabs.length - 1;
18420             
18421             if(this.active && tg.bullets > 0 && i < tg.bullets){
18422                 tg.setActiveBullet(i);
18423             }
18424         }
18425         
18426         this.el.on('click', this.onClick, this);
18427         
18428         if(Roo.isTouch){
18429             this.el.on("touchstart", this.onTouchStart, this);
18430             this.el.on("touchmove", this.onTouchMove, this);
18431             this.el.on("touchend", this.onTouchEnd, this);
18432         }
18433         
18434     },
18435     
18436     onRender : function(ct, position)
18437     {
18438         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18439     },
18440     
18441     setActive : function(state)
18442     {
18443         Roo.log("panel - set active " + this.tabId + "=" + state);
18444         
18445         this.active = state;
18446         if (!state) {
18447             this.el.removeClass('active');
18448             
18449         } else  if (!this.el.hasClass('active')) {
18450             this.el.addClass('active');
18451         }
18452         
18453         this.fireEvent('changed', this, state);
18454     },
18455     
18456     onClick : function(e)
18457     {
18458         e.preventDefault();
18459         
18460         if(!this.href.length){
18461             return;
18462         }
18463         
18464         window.location.href = this.href;
18465     },
18466     
18467     startX : 0,
18468     startY : 0,
18469     endX : 0,
18470     endY : 0,
18471     swiping : false,
18472     
18473     onTouchStart : function(e)
18474     {
18475         this.swiping = false;
18476         
18477         this.startX = e.browserEvent.touches[0].clientX;
18478         this.startY = e.browserEvent.touches[0].clientY;
18479     },
18480     
18481     onTouchMove : function(e)
18482     {
18483         this.swiping = true;
18484         
18485         this.endX = e.browserEvent.touches[0].clientX;
18486         this.endY = e.browserEvent.touches[0].clientY;
18487     },
18488     
18489     onTouchEnd : function(e)
18490     {
18491         if(!this.swiping){
18492             this.onClick(e);
18493             return;
18494         }
18495         
18496         var tabGroup = this.parent();
18497         
18498         if(this.endX > this.startX){ // swiping right
18499             tabGroup.showPanelPrev();
18500             return;
18501         }
18502         
18503         if(this.startX > this.endX){ // swiping left
18504             tabGroup.showPanelNext();
18505             return;
18506         }
18507     }
18508     
18509     
18510 });
18511  
18512
18513  
18514
18515  /*
18516  * - LGPL
18517  *
18518  * DateField
18519  * 
18520  */
18521
18522 /**
18523  * @class Roo.bootstrap.DateField
18524  * @extends Roo.bootstrap.Input
18525  * Bootstrap DateField class
18526  * @cfg {Number} weekStart default 0
18527  * @cfg {String} viewMode default empty, (months|years)
18528  * @cfg {String} minViewMode default empty, (months|years)
18529  * @cfg {Number} startDate default -Infinity
18530  * @cfg {Number} endDate default Infinity
18531  * @cfg {Boolean} todayHighlight default false
18532  * @cfg {Boolean} todayBtn default false
18533  * @cfg {Boolean} calendarWeeks default false
18534  * @cfg {Object} daysOfWeekDisabled default empty
18535  * @cfg {Boolean} singleMode default false (true | false)
18536  * 
18537  * @cfg {Boolean} keyboardNavigation default true
18538  * @cfg {String} language default en
18539  * 
18540  * @constructor
18541  * Create a new DateField
18542  * @param {Object} config The config object
18543  */
18544
18545 Roo.bootstrap.DateField = function(config){
18546     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18547      this.addEvents({
18548             /**
18549              * @event show
18550              * Fires when this field show.
18551              * @param {Roo.bootstrap.DateField} this
18552              * @param {Mixed} date The date value
18553              */
18554             show : true,
18555             /**
18556              * @event show
18557              * Fires when this field hide.
18558              * @param {Roo.bootstrap.DateField} this
18559              * @param {Mixed} date The date value
18560              */
18561             hide : true,
18562             /**
18563              * @event select
18564              * Fires when select a date.
18565              * @param {Roo.bootstrap.DateField} this
18566              * @param {Mixed} date The date value
18567              */
18568             select : true,
18569             /**
18570              * @event beforeselect
18571              * Fires when before select a date.
18572              * @param {Roo.bootstrap.DateField} this
18573              * @param {Mixed} date The date value
18574              */
18575             beforeselect : true
18576         });
18577 };
18578
18579 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18580     
18581     /**
18582      * @cfg {String} format
18583      * The default date format string which can be overriden for localization support.  The format must be
18584      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18585      */
18586     format : "m/d/y",
18587     /**
18588      * @cfg {String} altFormats
18589      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18590      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18591      */
18592     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18593     
18594     weekStart : 0,
18595     
18596     viewMode : '',
18597     
18598     minViewMode : '',
18599     
18600     todayHighlight : false,
18601     
18602     todayBtn: false,
18603     
18604     language: 'en',
18605     
18606     keyboardNavigation: true,
18607     
18608     calendarWeeks: false,
18609     
18610     startDate: -Infinity,
18611     
18612     endDate: Infinity,
18613     
18614     daysOfWeekDisabled: [],
18615     
18616     _events: [],
18617     
18618     singleMode : false,
18619     
18620     UTCDate: function()
18621     {
18622         return new Date(Date.UTC.apply(Date, arguments));
18623     },
18624     
18625     UTCToday: function()
18626     {
18627         var today = new Date();
18628         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18629     },
18630     
18631     getDate: function() {
18632             var d = this.getUTCDate();
18633             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18634     },
18635     
18636     getUTCDate: function() {
18637             return this.date;
18638     },
18639     
18640     setDate: function(d) {
18641             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18642     },
18643     
18644     setUTCDate: function(d) {
18645             this.date = d;
18646             this.setValue(this.formatDate(this.date));
18647     },
18648         
18649     onRender: function(ct, position)
18650     {
18651         
18652         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18653         
18654         this.language = this.language || 'en';
18655         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18656         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18657         
18658         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18659         this.format = this.format || 'm/d/y';
18660         this.isInline = false;
18661         this.isInput = true;
18662         this.component = this.el.select('.add-on', true).first() || false;
18663         this.component = (this.component && this.component.length === 0) ? false : this.component;
18664         this.hasInput = this.component && this.inputEl().length;
18665         
18666         if (typeof(this.minViewMode === 'string')) {
18667             switch (this.minViewMode) {
18668                 case 'months':
18669                     this.minViewMode = 1;
18670                     break;
18671                 case 'years':
18672                     this.minViewMode = 2;
18673                     break;
18674                 default:
18675                     this.minViewMode = 0;
18676                     break;
18677             }
18678         }
18679         
18680         if (typeof(this.viewMode === 'string')) {
18681             switch (this.viewMode) {
18682                 case 'months':
18683                     this.viewMode = 1;
18684                     break;
18685                 case 'years':
18686                     this.viewMode = 2;
18687                     break;
18688                 default:
18689                     this.viewMode = 0;
18690                     break;
18691             }
18692         }
18693                 
18694         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18695         
18696 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18697         
18698         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18699         
18700         this.picker().on('mousedown', this.onMousedown, this);
18701         this.picker().on('click', this.onClick, this);
18702         
18703         this.picker().addClass('datepicker-dropdown');
18704         
18705         this.startViewMode = this.viewMode;
18706         
18707         if(this.singleMode){
18708             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18709                 v.setVisibilityMode(Roo.Element.DISPLAY);
18710                 v.hide();
18711             });
18712             
18713             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18714                 v.setStyle('width', '189px');
18715             });
18716         }
18717         
18718         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18719             if(!this.calendarWeeks){
18720                 v.remove();
18721                 return;
18722             }
18723             
18724             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18725             v.attr('colspan', function(i, val){
18726                 return parseInt(val) + 1;
18727             });
18728         });
18729                         
18730         
18731         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18732         
18733         this.setStartDate(this.startDate);
18734         this.setEndDate(this.endDate);
18735         
18736         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18737         
18738         this.fillDow();
18739         this.fillMonths();
18740         this.update();
18741         this.showMode();
18742         
18743         if(this.isInline) {
18744             this.showPopup();
18745         }
18746     },
18747     
18748     picker : function()
18749     {
18750         return this.pickerEl;
18751 //        return this.el.select('.datepicker', true).first();
18752     },
18753     
18754     fillDow: function()
18755     {
18756         var dowCnt = this.weekStart;
18757         
18758         var dow = {
18759             tag: 'tr',
18760             cn: [
18761                 
18762             ]
18763         };
18764         
18765         if(this.calendarWeeks){
18766             dow.cn.push({
18767                 tag: 'th',
18768                 cls: 'cw',
18769                 html: '&nbsp;'
18770             })
18771         }
18772         
18773         while (dowCnt < this.weekStart + 7) {
18774             dow.cn.push({
18775                 tag: 'th',
18776                 cls: 'dow',
18777                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18778             });
18779         }
18780         
18781         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18782     },
18783     
18784     fillMonths: function()
18785     {    
18786         var i = 0;
18787         var months = this.picker().select('>.datepicker-months td', true).first();
18788         
18789         months.dom.innerHTML = '';
18790         
18791         while (i < 12) {
18792             var month = {
18793                 tag: 'span',
18794                 cls: 'month',
18795                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18796             };
18797             
18798             months.createChild(month);
18799         }
18800         
18801     },
18802     
18803     update: function()
18804     {
18805         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;
18806         
18807         if (this.date < this.startDate) {
18808             this.viewDate = new Date(this.startDate);
18809         } else if (this.date > this.endDate) {
18810             this.viewDate = new Date(this.endDate);
18811         } else {
18812             this.viewDate = new Date(this.date);
18813         }
18814         
18815         this.fill();
18816     },
18817     
18818     fill: function() 
18819     {
18820         var d = new Date(this.viewDate),
18821                 year = d.getUTCFullYear(),
18822                 month = d.getUTCMonth(),
18823                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18824                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18825                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18826                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18827                 currentDate = this.date && this.date.valueOf(),
18828                 today = this.UTCToday();
18829         
18830         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18831         
18832 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18833         
18834 //        this.picker.select('>tfoot th.today').
18835 //                                              .text(dates[this.language].today)
18836 //                                              .toggle(this.todayBtn !== false);
18837     
18838         this.updateNavArrows();
18839         this.fillMonths();
18840                                                 
18841         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18842         
18843         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18844          
18845         prevMonth.setUTCDate(day);
18846         
18847         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18848         
18849         var nextMonth = new Date(prevMonth);
18850         
18851         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18852         
18853         nextMonth = nextMonth.valueOf();
18854         
18855         var fillMonths = false;
18856         
18857         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18858         
18859         while(prevMonth.valueOf() <= nextMonth) {
18860             var clsName = '';
18861             
18862             if (prevMonth.getUTCDay() === this.weekStart) {
18863                 if(fillMonths){
18864                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18865                 }
18866                     
18867                 fillMonths = {
18868                     tag: 'tr',
18869                     cn: []
18870                 };
18871                 
18872                 if(this.calendarWeeks){
18873                     // ISO 8601: First week contains first thursday.
18874                     // ISO also states week starts on Monday, but we can be more abstract here.
18875                     var
18876                     // Start of current week: based on weekstart/current date
18877                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18878                     // Thursday of this week
18879                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18880                     // First Thursday of year, year from thursday
18881                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18882                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18883                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18884                     
18885                     fillMonths.cn.push({
18886                         tag: 'td',
18887                         cls: 'cw',
18888                         html: calWeek
18889                     });
18890                 }
18891             }
18892             
18893             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18894                 clsName += ' old';
18895             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18896                 clsName += ' new';
18897             }
18898             if (this.todayHighlight &&
18899                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18900                 prevMonth.getUTCMonth() == today.getMonth() &&
18901                 prevMonth.getUTCDate() == today.getDate()) {
18902                 clsName += ' today';
18903             }
18904             
18905             if (currentDate && prevMonth.valueOf() === currentDate) {
18906                 clsName += ' active';
18907             }
18908             
18909             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18910                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18911                     clsName += ' disabled';
18912             }
18913             
18914             fillMonths.cn.push({
18915                 tag: 'td',
18916                 cls: 'day ' + clsName,
18917                 html: prevMonth.getDate()
18918             });
18919             
18920             prevMonth.setDate(prevMonth.getDate()+1);
18921         }
18922           
18923         var currentYear = this.date && this.date.getUTCFullYear();
18924         var currentMonth = this.date && this.date.getUTCMonth();
18925         
18926         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18927         
18928         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18929             v.removeClass('active');
18930             
18931             if(currentYear === year && k === currentMonth){
18932                 v.addClass('active');
18933             }
18934             
18935             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18936                 v.addClass('disabled');
18937             }
18938             
18939         });
18940         
18941         
18942         year = parseInt(year/10, 10) * 10;
18943         
18944         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18945         
18946         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18947         
18948         year -= 1;
18949         for (var i = -1; i < 11; i++) {
18950             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18951                 tag: 'span',
18952                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18953                 html: year
18954             });
18955             
18956             year += 1;
18957         }
18958     },
18959     
18960     showMode: function(dir) 
18961     {
18962         if (dir) {
18963             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18964         }
18965         
18966         Roo.each(this.picker().select('>div',true).elements, function(v){
18967             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18968             v.hide();
18969         });
18970         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18971     },
18972     
18973     place: function()
18974     {
18975         if(this.isInline) {
18976             return;
18977         }
18978         
18979         this.picker().removeClass(['bottom', 'top']);
18980         
18981         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18982             /*
18983              * place to the top of element!
18984              *
18985              */
18986             
18987             this.picker().addClass('top');
18988             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18989             
18990             return;
18991         }
18992         
18993         this.picker().addClass('bottom');
18994         
18995         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18996     },
18997     
18998     parseDate : function(value)
18999     {
19000         if(!value || value instanceof Date){
19001             return value;
19002         }
19003         var v = Date.parseDate(value, this.format);
19004         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19005             v = Date.parseDate(value, 'Y-m-d');
19006         }
19007         if(!v && this.altFormats){
19008             if(!this.altFormatsArray){
19009                 this.altFormatsArray = this.altFormats.split("|");
19010             }
19011             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19012                 v = Date.parseDate(value, this.altFormatsArray[i]);
19013             }
19014         }
19015         return v;
19016     },
19017     
19018     formatDate : function(date, fmt)
19019     {   
19020         return (!date || !(date instanceof Date)) ?
19021         date : date.dateFormat(fmt || this.format);
19022     },
19023     
19024     onFocus : function()
19025     {
19026         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19027         this.showPopup();
19028     },
19029     
19030     onBlur : function()
19031     {
19032         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19033         
19034         var d = this.inputEl().getValue();
19035         
19036         this.setValue(d);
19037                 
19038         this.hidePopup();
19039     },
19040     
19041     showPopup : function()
19042     {
19043         this.picker().show();
19044         this.update();
19045         this.place();
19046         
19047         this.fireEvent('showpopup', this, this.date);
19048     },
19049     
19050     hidePopup : function()
19051     {
19052         if(this.isInline) {
19053             return;
19054         }
19055         this.picker().hide();
19056         this.viewMode = this.startViewMode;
19057         this.showMode();
19058         
19059         this.fireEvent('hidepopup', this, this.date);
19060         
19061     },
19062     
19063     onMousedown: function(e)
19064     {
19065         e.stopPropagation();
19066         e.preventDefault();
19067     },
19068     
19069     keyup: function(e)
19070     {
19071         Roo.bootstrap.DateField.superclass.keyup.call(this);
19072         this.update();
19073     },
19074
19075     setValue: function(v)
19076     {
19077         if(this.fireEvent('beforeselect', this, v) !== false){
19078             var d = new Date(this.parseDate(v) ).clearTime();
19079         
19080             if(isNaN(d.getTime())){
19081                 this.date = this.viewDate = '';
19082                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19083                 return;
19084             }
19085
19086             v = this.formatDate(d);
19087
19088             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19089
19090             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19091
19092             this.update();
19093
19094             this.fireEvent('select', this, this.date);
19095         }
19096     },
19097     
19098     getValue: function()
19099     {
19100         return this.formatDate(this.date);
19101     },
19102     
19103     fireKey: function(e)
19104     {
19105         if (!this.picker().isVisible()){
19106             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19107                 this.showPopup();
19108             }
19109             return;
19110         }
19111         
19112         var dateChanged = false,
19113         dir, day, month,
19114         newDate, newViewDate;
19115         
19116         switch(e.keyCode){
19117             case 27: // escape
19118                 this.hidePopup();
19119                 e.preventDefault();
19120                 break;
19121             case 37: // left
19122             case 39: // right
19123                 if (!this.keyboardNavigation) {
19124                     break;
19125                 }
19126                 dir = e.keyCode == 37 ? -1 : 1;
19127                 
19128                 if (e.ctrlKey){
19129                     newDate = this.moveYear(this.date, dir);
19130                     newViewDate = this.moveYear(this.viewDate, dir);
19131                 } else if (e.shiftKey){
19132                     newDate = this.moveMonth(this.date, dir);
19133                     newViewDate = this.moveMonth(this.viewDate, dir);
19134                 } else {
19135                     newDate = new Date(this.date);
19136                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19137                     newViewDate = new Date(this.viewDate);
19138                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19139                 }
19140                 if (this.dateWithinRange(newDate)){
19141                     this.date = newDate;
19142                     this.viewDate = newViewDate;
19143                     this.setValue(this.formatDate(this.date));
19144 //                    this.update();
19145                     e.preventDefault();
19146                     dateChanged = true;
19147                 }
19148                 break;
19149             case 38: // up
19150             case 40: // down
19151                 if (!this.keyboardNavigation) {
19152                     break;
19153                 }
19154                 dir = e.keyCode == 38 ? -1 : 1;
19155                 if (e.ctrlKey){
19156                     newDate = this.moveYear(this.date, dir);
19157                     newViewDate = this.moveYear(this.viewDate, dir);
19158                 } else if (e.shiftKey){
19159                     newDate = this.moveMonth(this.date, dir);
19160                     newViewDate = this.moveMonth(this.viewDate, dir);
19161                 } else {
19162                     newDate = new Date(this.date);
19163                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19164                     newViewDate = new Date(this.viewDate);
19165                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19166                 }
19167                 if (this.dateWithinRange(newDate)){
19168                     this.date = newDate;
19169                     this.viewDate = newViewDate;
19170                     this.setValue(this.formatDate(this.date));
19171 //                    this.update();
19172                     e.preventDefault();
19173                     dateChanged = true;
19174                 }
19175                 break;
19176             case 13: // enter
19177                 this.setValue(this.formatDate(this.date));
19178                 this.hidePopup();
19179                 e.preventDefault();
19180                 break;
19181             case 9: // tab
19182                 this.setValue(this.formatDate(this.date));
19183                 this.hidePopup();
19184                 break;
19185             case 16: // shift
19186             case 17: // ctrl
19187             case 18: // alt
19188                 break;
19189             default :
19190                 this.hidePopup();
19191                 
19192         }
19193     },
19194     
19195     
19196     onClick: function(e) 
19197     {
19198         e.stopPropagation();
19199         e.preventDefault();
19200         
19201         var target = e.getTarget();
19202         
19203         if(target.nodeName.toLowerCase() === 'i'){
19204             target = Roo.get(target).dom.parentNode;
19205         }
19206         
19207         var nodeName = target.nodeName;
19208         var className = target.className;
19209         var html = target.innerHTML;
19210         //Roo.log(nodeName);
19211         
19212         switch(nodeName.toLowerCase()) {
19213             case 'th':
19214                 switch(className) {
19215                     case 'switch':
19216                         this.showMode(1);
19217                         break;
19218                     case 'prev':
19219                     case 'next':
19220                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19221                         switch(this.viewMode){
19222                                 case 0:
19223                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19224                                         break;
19225                                 case 1:
19226                                 case 2:
19227                                         this.viewDate = this.moveYear(this.viewDate, dir);
19228                                         break;
19229                         }
19230                         this.fill();
19231                         break;
19232                     case 'today':
19233                         var date = new Date();
19234                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19235 //                        this.fill()
19236                         this.setValue(this.formatDate(this.date));
19237                         
19238                         this.hidePopup();
19239                         break;
19240                 }
19241                 break;
19242             case 'span':
19243                 if (className.indexOf('disabled') < 0) {
19244                     this.viewDate.setUTCDate(1);
19245                     if (className.indexOf('month') > -1) {
19246                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19247                     } else {
19248                         var year = parseInt(html, 10) || 0;
19249                         this.viewDate.setUTCFullYear(year);
19250                         
19251                     }
19252                     
19253                     if(this.singleMode){
19254                         this.setValue(this.formatDate(this.viewDate));
19255                         this.hidePopup();
19256                         return;
19257                     }
19258                     
19259                     this.showMode(-1);
19260                     this.fill();
19261                 }
19262                 break;
19263                 
19264             case 'td':
19265                 //Roo.log(className);
19266                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19267                     var day = parseInt(html, 10) || 1;
19268                     var year = this.viewDate.getUTCFullYear(),
19269                         month = this.viewDate.getUTCMonth();
19270
19271                     if (className.indexOf('old') > -1) {
19272                         if(month === 0 ){
19273                             month = 11;
19274                             year -= 1;
19275                         }else{
19276                             month -= 1;
19277                         }
19278                     } else if (className.indexOf('new') > -1) {
19279                         if (month == 11) {
19280                             month = 0;
19281                             year += 1;
19282                         } else {
19283                             month += 1;
19284                         }
19285                     }
19286                     //Roo.log([year,month,day]);
19287                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19288                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19289 //                    this.fill();
19290                     //Roo.log(this.formatDate(this.date));
19291                     this.setValue(this.formatDate(this.date));
19292                     this.hidePopup();
19293                 }
19294                 break;
19295         }
19296     },
19297     
19298     setStartDate: function(startDate)
19299     {
19300         this.startDate = startDate || -Infinity;
19301         if (this.startDate !== -Infinity) {
19302             this.startDate = this.parseDate(this.startDate);
19303         }
19304         this.update();
19305         this.updateNavArrows();
19306     },
19307
19308     setEndDate: function(endDate)
19309     {
19310         this.endDate = endDate || Infinity;
19311         if (this.endDate !== Infinity) {
19312             this.endDate = this.parseDate(this.endDate);
19313         }
19314         this.update();
19315         this.updateNavArrows();
19316     },
19317     
19318     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19319     {
19320         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19321         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19322             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19323         }
19324         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19325             return parseInt(d, 10);
19326         });
19327         this.update();
19328         this.updateNavArrows();
19329     },
19330     
19331     updateNavArrows: function() 
19332     {
19333         if(this.singleMode){
19334             return;
19335         }
19336         
19337         var d = new Date(this.viewDate),
19338         year = d.getUTCFullYear(),
19339         month = d.getUTCMonth();
19340         
19341         Roo.each(this.picker().select('.prev', true).elements, function(v){
19342             v.show();
19343             switch (this.viewMode) {
19344                 case 0:
19345
19346                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19347                         v.hide();
19348                     }
19349                     break;
19350                 case 1:
19351                 case 2:
19352                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19353                         v.hide();
19354                     }
19355                     break;
19356             }
19357         });
19358         
19359         Roo.each(this.picker().select('.next', true).elements, function(v){
19360             v.show();
19361             switch (this.viewMode) {
19362                 case 0:
19363
19364                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19365                         v.hide();
19366                     }
19367                     break;
19368                 case 1:
19369                 case 2:
19370                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19371                         v.hide();
19372                     }
19373                     break;
19374             }
19375         })
19376     },
19377     
19378     moveMonth: function(date, dir)
19379     {
19380         if (!dir) {
19381             return date;
19382         }
19383         var new_date = new Date(date.valueOf()),
19384         day = new_date.getUTCDate(),
19385         month = new_date.getUTCMonth(),
19386         mag = Math.abs(dir),
19387         new_month, test;
19388         dir = dir > 0 ? 1 : -1;
19389         if (mag == 1){
19390             test = dir == -1
19391             // If going back one month, make sure month is not current month
19392             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19393             ? function(){
19394                 return new_date.getUTCMonth() == month;
19395             }
19396             // If going forward one month, make sure month is as expected
19397             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19398             : function(){
19399                 return new_date.getUTCMonth() != new_month;
19400             };
19401             new_month = month + dir;
19402             new_date.setUTCMonth(new_month);
19403             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19404             if (new_month < 0 || new_month > 11) {
19405                 new_month = (new_month + 12) % 12;
19406             }
19407         } else {
19408             // For magnitudes >1, move one month at a time...
19409             for (var i=0; i<mag; i++) {
19410                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19411                 new_date = this.moveMonth(new_date, dir);
19412             }
19413             // ...then reset the day, keeping it in the new month
19414             new_month = new_date.getUTCMonth();
19415             new_date.setUTCDate(day);
19416             test = function(){
19417                 return new_month != new_date.getUTCMonth();
19418             };
19419         }
19420         // Common date-resetting loop -- if date is beyond end of month, make it
19421         // end of month
19422         while (test()){
19423             new_date.setUTCDate(--day);
19424             new_date.setUTCMonth(new_month);
19425         }
19426         return new_date;
19427     },
19428
19429     moveYear: function(date, dir)
19430     {
19431         return this.moveMonth(date, dir*12);
19432     },
19433
19434     dateWithinRange: function(date)
19435     {
19436         return date >= this.startDate && date <= this.endDate;
19437     },
19438
19439     
19440     remove: function() 
19441     {
19442         this.picker().remove();
19443     },
19444     
19445     validateValue : function(value)
19446     {
19447         if(this.getVisibilityEl().hasClass('hidden')){
19448             return true;
19449         }
19450         
19451         if(value.length < 1)  {
19452             if(this.allowBlank){
19453                 return true;
19454             }
19455             return false;
19456         }
19457         
19458         if(value.length < this.minLength){
19459             return false;
19460         }
19461         if(value.length > this.maxLength){
19462             return false;
19463         }
19464         if(this.vtype){
19465             var vt = Roo.form.VTypes;
19466             if(!vt[this.vtype](value, this)){
19467                 return false;
19468             }
19469         }
19470         if(typeof this.validator == "function"){
19471             var msg = this.validator(value);
19472             if(msg !== true){
19473                 return false;
19474             }
19475         }
19476         
19477         if(this.regex && !this.regex.test(value)){
19478             return false;
19479         }
19480         
19481         if(typeof(this.parseDate(value)) == 'undefined'){
19482             return false;
19483         }
19484         
19485         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19486             return false;
19487         }      
19488         
19489         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19490             return false;
19491         } 
19492         
19493         
19494         return true;
19495     },
19496     
19497     reset : function()
19498     {
19499         this.date = this.viewDate = '';
19500         
19501         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19502     }
19503    
19504 });
19505
19506 Roo.apply(Roo.bootstrap.DateField,  {
19507     
19508     head : {
19509         tag: 'thead',
19510         cn: [
19511         {
19512             tag: 'tr',
19513             cn: [
19514             {
19515                 tag: 'th',
19516                 cls: 'prev',
19517                 html: '<i class="fa fa-arrow-left"/>'
19518             },
19519             {
19520                 tag: 'th',
19521                 cls: 'switch',
19522                 colspan: '5'
19523             },
19524             {
19525                 tag: 'th',
19526                 cls: 'next',
19527                 html: '<i class="fa fa-arrow-right"/>'
19528             }
19529
19530             ]
19531         }
19532         ]
19533     },
19534     
19535     content : {
19536         tag: 'tbody',
19537         cn: [
19538         {
19539             tag: 'tr',
19540             cn: [
19541             {
19542                 tag: 'td',
19543                 colspan: '7'
19544             }
19545             ]
19546         }
19547         ]
19548     },
19549     
19550     footer : {
19551         tag: 'tfoot',
19552         cn: [
19553         {
19554             tag: 'tr',
19555             cn: [
19556             {
19557                 tag: 'th',
19558                 colspan: '7',
19559                 cls: 'today'
19560             }
19561                     
19562             ]
19563         }
19564         ]
19565     },
19566     
19567     dates:{
19568         en: {
19569             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19570             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19571             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19572             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19573             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19574             today: "Today"
19575         }
19576     },
19577     
19578     modes: [
19579     {
19580         clsName: 'days',
19581         navFnc: 'Month',
19582         navStep: 1
19583     },
19584     {
19585         clsName: 'months',
19586         navFnc: 'FullYear',
19587         navStep: 1
19588     },
19589     {
19590         clsName: 'years',
19591         navFnc: 'FullYear',
19592         navStep: 10
19593     }]
19594 });
19595
19596 Roo.apply(Roo.bootstrap.DateField,  {
19597   
19598     template : {
19599         tag: 'div',
19600         cls: 'datepicker dropdown-menu roo-dynamic',
19601         cn: [
19602         {
19603             tag: 'div',
19604             cls: 'datepicker-days',
19605             cn: [
19606             {
19607                 tag: 'table',
19608                 cls: 'table-condensed',
19609                 cn:[
19610                 Roo.bootstrap.DateField.head,
19611                 {
19612                     tag: 'tbody'
19613                 },
19614                 Roo.bootstrap.DateField.footer
19615                 ]
19616             }
19617             ]
19618         },
19619         {
19620             tag: 'div',
19621             cls: 'datepicker-months',
19622             cn: [
19623             {
19624                 tag: 'table',
19625                 cls: 'table-condensed',
19626                 cn:[
19627                 Roo.bootstrap.DateField.head,
19628                 Roo.bootstrap.DateField.content,
19629                 Roo.bootstrap.DateField.footer
19630                 ]
19631             }
19632             ]
19633         },
19634         {
19635             tag: 'div',
19636             cls: 'datepicker-years',
19637             cn: [
19638             {
19639                 tag: 'table',
19640                 cls: 'table-condensed',
19641                 cn:[
19642                 Roo.bootstrap.DateField.head,
19643                 Roo.bootstrap.DateField.content,
19644                 Roo.bootstrap.DateField.footer
19645                 ]
19646             }
19647             ]
19648         }
19649         ]
19650     }
19651 });
19652
19653  
19654
19655  /*
19656  * - LGPL
19657  *
19658  * TimeField
19659  * 
19660  */
19661
19662 /**
19663  * @class Roo.bootstrap.TimeField
19664  * @extends Roo.bootstrap.Input
19665  * Bootstrap DateField class
19666  * 
19667  * 
19668  * @constructor
19669  * Create a new TimeField
19670  * @param {Object} config The config object
19671  */
19672
19673 Roo.bootstrap.TimeField = function(config){
19674     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19675     this.addEvents({
19676             /**
19677              * @event show
19678              * Fires when this field show.
19679              * @param {Roo.bootstrap.DateField} thisthis
19680              * @param {Mixed} date The date value
19681              */
19682             show : true,
19683             /**
19684              * @event show
19685              * Fires when this field hide.
19686              * @param {Roo.bootstrap.DateField} this
19687              * @param {Mixed} date The date value
19688              */
19689             hide : true,
19690             /**
19691              * @event select
19692              * Fires when select a date.
19693              * @param {Roo.bootstrap.DateField} this
19694              * @param {Mixed} date The date value
19695              */
19696             select : true
19697         });
19698 };
19699
19700 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19701     
19702     /**
19703      * @cfg {String} format
19704      * The default time format string which can be overriden for localization support.  The format must be
19705      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19706      */
19707     format : "H:i",
19708        
19709     onRender: function(ct, position)
19710     {
19711         
19712         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19713                 
19714         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19715         
19716         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19717         
19718         this.pop = this.picker().select('>.datepicker-time',true).first();
19719         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19720         
19721         this.picker().on('mousedown', this.onMousedown, this);
19722         this.picker().on('click', this.onClick, this);
19723         
19724         this.picker().addClass('datepicker-dropdown');
19725     
19726         this.fillTime();
19727         this.update();
19728             
19729         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19730         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19731         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19732         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19733         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19734         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19735
19736     },
19737     
19738     fireKey: function(e){
19739         if (!this.picker().isVisible()){
19740             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19741                 this.show();
19742             }
19743             return;
19744         }
19745
19746         e.preventDefault();
19747         
19748         switch(e.keyCode){
19749             case 27: // escape
19750                 this.hide();
19751                 break;
19752             case 37: // left
19753             case 39: // right
19754                 this.onTogglePeriod();
19755                 break;
19756             case 38: // up
19757                 this.onIncrementMinutes();
19758                 break;
19759             case 40: // down
19760                 this.onDecrementMinutes();
19761                 break;
19762             case 13: // enter
19763             case 9: // tab
19764                 this.setTime();
19765                 break;
19766         }
19767     },
19768     
19769     onClick: function(e) {
19770         e.stopPropagation();
19771         e.preventDefault();
19772     },
19773     
19774     picker : function()
19775     {
19776         return this.el.select('.datepicker', true).first();
19777     },
19778     
19779     fillTime: function()
19780     {    
19781         var time = this.pop.select('tbody', true).first();
19782         
19783         time.dom.innerHTML = '';
19784         
19785         time.createChild({
19786             tag: 'tr',
19787             cn: [
19788                 {
19789                     tag: 'td',
19790                     cn: [
19791                         {
19792                             tag: 'a',
19793                             href: '#',
19794                             cls: 'btn',
19795                             cn: [
19796                                 {
19797                                     tag: 'span',
19798                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19799                                 }
19800                             ]
19801                         } 
19802                     ]
19803                 },
19804                 {
19805                     tag: 'td',
19806                     cls: 'separator'
19807                 },
19808                 {
19809                     tag: 'td',
19810                     cn: [
19811                         {
19812                             tag: 'a',
19813                             href: '#',
19814                             cls: 'btn',
19815                             cn: [
19816                                 {
19817                                     tag: 'span',
19818                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19819                                 }
19820                             ]
19821                         }
19822                     ]
19823                 },
19824                 {
19825                     tag: 'td',
19826                     cls: 'separator'
19827                 }
19828             ]
19829         });
19830         
19831         time.createChild({
19832             tag: 'tr',
19833             cn: [
19834                 {
19835                     tag: 'td',
19836                     cn: [
19837                         {
19838                             tag: 'span',
19839                             cls: 'timepicker-hour',
19840                             html: '00'
19841                         }  
19842                     ]
19843                 },
19844                 {
19845                     tag: 'td',
19846                     cls: 'separator',
19847                     html: ':'
19848                 },
19849                 {
19850                     tag: 'td',
19851                     cn: [
19852                         {
19853                             tag: 'span',
19854                             cls: 'timepicker-minute',
19855                             html: '00'
19856                         }  
19857                     ]
19858                 },
19859                 {
19860                     tag: 'td',
19861                     cls: 'separator'
19862                 },
19863                 {
19864                     tag: 'td',
19865                     cn: [
19866                         {
19867                             tag: 'button',
19868                             type: 'button',
19869                             cls: 'btn btn-primary period',
19870                             html: 'AM'
19871                             
19872                         }
19873                     ]
19874                 }
19875             ]
19876         });
19877         
19878         time.createChild({
19879             tag: 'tr',
19880             cn: [
19881                 {
19882                     tag: 'td',
19883                     cn: [
19884                         {
19885                             tag: 'a',
19886                             href: '#',
19887                             cls: 'btn',
19888                             cn: [
19889                                 {
19890                                     tag: 'span',
19891                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19892                                 }
19893                             ]
19894                         }
19895                     ]
19896                 },
19897                 {
19898                     tag: 'td',
19899                     cls: 'separator'
19900                 },
19901                 {
19902                     tag: 'td',
19903                     cn: [
19904                         {
19905                             tag: 'a',
19906                             href: '#',
19907                             cls: 'btn',
19908                             cn: [
19909                                 {
19910                                     tag: 'span',
19911                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19912                                 }
19913                             ]
19914                         }
19915                     ]
19916                 },
19917                 {
19918                     tag: 'td',
19919                     cls: 'separator'
19920                 }
19921             ]
19922         });
19923         
19924     },
19925     
19926     update: function()
19927     {
19928         
19929         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19930         
19931         this.fill();
19932     },
19933     
19934     fill: function() 
19935     {
19936         var hours = this.time.getHours();
19937         var minutes = this.time.getMinutes();
19938         var period = 'AM';
19939         
19940         if(hours > 11){
19941             period = 'PM';
19942         }
19943         
19944         if(hours == 0){
19945             hours = 12;
19946         }
19947         
19948         
19949         if(hours > 12){
19950             hours = hours - 12;
19951         }
19952         
19953         if(hours < 10){
19954             hours = '0' + hours;
19955         }
19956         
19957         if(minutes < 10){
19958             minutes = '0' + minutes;
19959         }
19960         
19961         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19962         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19963         this.pop.select('button', true).first().dom.innerHTML = period;
19964         
19965     },
19966     
19967     place: function()
19968     {   
19969         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19970         
19971         var cls = ['bottom'];
19972         
19973         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19974             cls.pop();
19975             cls.push('top');
19976         }
19977         
19978         cls.push('right');
19979         
19980         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19981             cls.pop();
19982             cls.push('left');
19983         }
19984         
19985         this.picker().addClass(cls.join('-'));
19986         
19987         var _this = this;
19988         
19989         Roo.each(cls, function(c){
19990             if(c == 'bottom'){
19991                 _this.picker().setTop(_this.inputEl().getHeight());
19992                 return;
19993             }
19994             if(c == 'top'){
19995                 _this.picker().setTop(0 - _this.picker().getHeight());
19996                 return;
19997             }
19998             
19999             if(c == 'left'){
20000                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20001                 return;
20002             }
20003             if(c == 'right'){
20004                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20005                 return;
20006             }
20007         });
20008         
20009     },
20010   
20011     onFocus : function()
20012     {
20013         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20014         this.show();
20015     },
20016     
20017     onBlur : function()
20018     {
20019         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20020         this.hide();
20021     },
20022     
20023     show : function()
20024     {
20025         this.picker().show();
20026         this.pop.show();
20027         this.update();
20028         this.place();
20029         
20030         this.fireEvent('show', this, this.date);
20031     },
20032     
20033     hide : function()
20034     {
20035         this.picker().hide();
20036         this.pop.hide();
20037         
20038         this.fireEvent('hide', this, this.date);
20039     },
20040     
20041     setTime : function()
20042     {
20043         this.hide();
20044         this.setValue(this.time.format(this.format));
20045         
20046         this.fireEvent('select', this, this.date);
20047         
20048         
20049     },
20050     
20051     onMousedown: function(e){
20052         e.stopPropagation();
20053         e.preventDefault();
20054     },
20055     
20056     onIncrementHours: function()
20057     {
20058         Roo.log('onIncrementHours');
20059         this.time = this.time.add(Date.HOUR, 1);
20060         this.update();
20061         
20062     },
20063     
20064     onDecrementHours: function()
20065     {
20066         Roo.log('onDecrementHours');
20067         this.time = this.time.add(Date.HOUR, -1);
20068         this.update();
20069     },
20070     
20071     onIncrementMinutes: function()
20072     {
20073         Roo.log('onIncrementMinutes');
20074         this.time = this.time.add(Date.MINUTE, 1);
20075         this.update();
20076     },
20077     
20078     onDecrementMinutes: function()
20079     {
20080         Roo.log('onDecrementMinutes');
20081         this.time = this.time.add(Date.MINUTE, -1);
20082         this.update();
20083     },
20084     
20085     onTogglePeriod: function()
20086     {
20087         Roo.log('onTogglePeriod');
20088         this.time = this.time.add(Date.HOUR, 12);
20089         this.update();
20090     }
20091     
20092    
20093 });
20094
20095 Roo.apply(Roo.bootstrap.TimeField,  {
20096     
20097     content : {
20098         tag: 'tbody',
20099         cn: [
20100             {
20101                 tag: 'tr',
20102                 cn: [
20103                 {
20104                     tag: 'td',
20105                     colspan: '7'
20106                 }
20107                 ]
20108             }
20109         ]
20110     },
20111     
20112     footer : {
20113         tag: 'tfoot',
20114         cn: [
20115             {
20116                 tag: 'tr',
20117                 cn: [
20118                 {
20119                     tag: 'th',
20120                     colspan: '7',
20121                     cls: '',
20122                     cn: [
20123                         {
20124                             tag: 'button',
20125                             cls: 'btn btn-info ok',
20126                             html: 'OK'
20127                         }
20128                     ]
20129                 }
20130
20131                 ]
20132             }
20133         ]
20134     }
20135 });
20136
20137 Roo.apply(Roo.bootstrap.TimeField,  {
20138   
20139     template : {
20140         tag: 'div',
20141         cls: 'datepicker dropdown-menu',
20142         cn: [
20143             {
20144                 tag: 'div',
20145                 cls: 'datepicker-time',
20146                 cn: [
20147                 {
20148                     tag: 'table',
20149                     cls: 'table-condensed',
20150                     cn:[
20151                     Roo.bootstrap.TimeField.content,
20152                     Roo.bootstrap.TimeField.footer
20153                     ]
20154                 }
20155                 ]
20156             }
20157         ]
20158     }
20159 });
20160
20161  
20162
20163  /*
20164  * - LGPL
20165  *
20166  * MonthField
20167  * 
20168  */
20169
20170 /**
20171  * @class Roo.bootstrap.MonthField
20172  * @extends Roo.bootstrap.Input
20173  * Bootstrap MonthField class
20174  * 
20175  * @cfg {String} language default en
20176  * 
20177  * @constructor
20178  * Create a new MonthField
20179  * @param {Object} config The config object
20180  */
20181
20182 Roo.bootstrap.MonthField = function(config){
20183     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20184     
20185     this.addEvents({
20186         /**
20187          * @event show
20188          * Fires when this field show.
20189          * @param {Roo.bootstrap.MonthField} this
20190          * @param {Mixed} date The date value
20191          */
20192         show : true,
20193         /**
20194          * @event show
20195          * Fires when this field hide.
20196          * @param {Roo.bootstrap.MonthField} this
20197          * @param {Mixed} date The date value
20198          */
20199         hide : true,
20200         /**
20201          * @event select
20202          * Fires when select a date.
20203          * @param {Roo.bootstrap.MonthField} this
20204          * @param {String} oldvalue The old value
20205          * @param {String} newvalue The new value
20206          */
20207         select : true
20208     });
20209 };
20210
20211 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20212     
20213     onRender: function(ct, position)
20214     {
20215         
20216         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20217         
20218         this.language = this.language || 'en';
20219         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20220         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20221         
20222         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20223         this.isInline = false;
20224         this.isInput = true;
20225         this.component = this.el.select('.add-on', true).first() || false;
20226         this.component = (this.component && this.component.length === 0) ? false : this.component;
20227         this.hasInput = this.component && this.inputEL().length;
20228         
20229         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20230         
20231         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20232         
20233         this.picker().on('mousedown', this.onMousedown, this);
20234         this.picker().on('click', this.onClick, this);
20235         
20236         this.picker().addClass('datepicker-dropdown');
20237         
20238         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20239             v.setStyle('width', '189px');
20240         });
20241         
20242         this.fillMonths();
20243         
20244         this.update();
20245         
20246         if(this.isInline) {
20247             this.show();
20248         }
20249         
20250     },
20251     
20252     setValue: function(v, suppressEvent)
20253     {   
20254         var o = this.getValue();
20255         
20256         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20257         
20258         this.update();
20259
20260         if(suppressEvent !== true){
20261             this.fireEvent('select', this, o, v);
20262         }
20263         
20264     },
20265     
20266     getValue: function()
20267     {
20268         return this.value;
20269     },
20270     
20271     onClick: function(e) 
20272     {
20273         e.stopPropagation();
20274         e.preventDefault();
20275         
20276         var target = e.getTarget();
20277         
20278         if(target.nodeName.toLowerCase() === 'i'){
20279             target = Roo.get(target).dom.parentNode;
20280         }
20281         
20282         var nodeName = target.nodeName;
20283         var className = target.className;
20284         var html = target.innerHTML;
20285         
20286         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20287             return;
20288         }
20289         
20290         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20291         
20292         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20293         
20294         this.hide();
20295                         
20296     },
20297     
20298     picker : function()
20299     {
20300         return this.pickerEl;
20301     },
20302     
20303     fillMonths: function()
20304     {    
20305         var i = 0;
20306         var months = this.picker().select('>.datepicker-months td', true).first();
20307         
20308         months.dom.innerHTML = '';
20309         
20310         while (i < 12) {
20311             var month = {
20312                 tag: 'span',
20313                 cls: 'month',
20314                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20315             };
20316             
20317             months.createChild(month);
20318         }
20319         
20320     },
20321     
20322     update: function()
20323     {
20324         var _this = this;
20325         
20326         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20327             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20328         }
20329         
20330         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20331             e.removeClass('active');
20332             
20333             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20334                 e.addClass('active');
20335             }
20336         })
20337     },
20338     
20339     place: function()
20340     {
20341         if(this.isInline) {
20342             return;
20343         }
20344         
20345         this.picker().removeClass(['bottom', 'top']);
20346         
20347         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20348             /*
20349              * place to the top of element!
20350              *
20351              */
20352             
20353             this.picker().addClass('top');
20354             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20355             
20356             return;
20357         }
20358         
20359         this.picker().addClass('bottom');
20360         
20361         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20362     },
20363     
20364     onFocus : function()
20365     {
20366         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20367         this.show();
20368     },
20369     
20370     onBlur : function()
20371     {
20372         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20373         
20374         var d = this.inputEl().getValue();
20375         
20376         this.setValue(d);
20377                 
20378         this.hide();
20379     },
20380     
20381     show : function()
20382     {
20383         this.picker().show();
20384         this.picker().select('>.datepicker-months', true).first().show();
20385         this.update();
20386         this.place();
20387         
20388         this.fireEvent('show', this, this.date);
20389     },
20390     
20391     hide : function()
20392     {
20393         if(this.isInline) {
20394             return;
20395         }
20396         this.picker().hide();
20397         this.fireEvent('hide', this, this.date);
20398         
20399     },
20400     
20401     onMousedown: function(e)
20402     {
20403         e.stopPropagation();
20404         e.preventDefault();
20405     },
20406     
20407     keyup: function(e)
20408     {
20409         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20410         this.update();
20411     },
20412
20413     fireKey: function(e)
20414     {
20415         if (!this.picker().isVisible()){
20416             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20417                 this.show();
20418             }
20419             return;
20420         }
20421         
20422         var dir;
20423         
20424         switch(e.keyCode){
20425             case 27: // escape
20426                 this.hide();
20427                 e.preventDefault();
20428                 break;
20429             case 37: // left
20430             case 39: // right
20431                 dir = e.keyCode == 37 ? -1 : 1;
20432                 
20433                 this.vIndex = this.vIndex + dir;
20434                 
20435                 if(this.vIndex < 0){
20436                     this.vIndex = 0;
20437                 }
20438                 
20439                 if(this.vIndex > 11){
20440                     this.vIndex = 11;
20441                 }
20442                 
20443                 if(isNaN(this.vIndex)){
20444                     this.vIndex = 0;
20445                 }
20446                 
20447                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20448                 
20449                 break;
20450             case 38: // up
20451             case 40: // down
20452                 
20453                 dir = e.keyCode == 38 ? -1 : 1;
20454                 
20455                 this.vIndex = this.vIndex + dir * 4;
20456                 
20457                 if(this.vIndex < 0){
20458                     this.vIndex = 0;
20459                 }
20460                 
20461                 if(this.vIndex > 11){
20462                     this.vIndex = 11;
20463                 }
20464                 
20465                 if(isNaN(this.vIndex)){
20466                     this.vIndex = 0;
20467                 }
20468                 
20469                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20470                 break;
20471                 
20472             case 13: // enter
20473                 
20474                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20475                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20476                 }
20477                 
20478                 this.hide();
20479                 e.preventDefault();
20480                 break;
20481             case 9: // tab
20482                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20483                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20484                 }
20485                 this.hide();
20486                 break;
20487             case 16: // shift
20488             case 17: // ctrl
20489             case 18: // alt
20490                 break;
20491             default :
20492                 this.hide();
20493                 
20494         }
20495     },
20496     
20497     remove: function() 
20498     {
20499         this.picker().remove();
20500     }
20501    
20502 });
20503
20504 Roo.apply(Roo.bootstrap.MonthField,  {
20505     
20506     content : {
20507         tag: 'tbody',
20508         cn: [
20509         {
20510             tag: 'tr',
20511             cn: [
20512             {
20513                 tag: 'td',
20514                 colspan: '7'
20515             }
20516             ]
20517         }
20518         ]
20519     },
20520     
20521     dates:{
20522         en: {
20523             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20524             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20525         }
20526     }
20527 });
20528
20529 Roo.apply(Roo.bootstrap.MonthField,  {
20530   
20531     template : {
20532         tag: 'div',
20533         cls: 'datepicker dropdown-menu roo-dynamic',
20534         cn: [
20535             {
20536                 tag: 'div',
20537                 cls: 'datepicker-months',
20538                 cn: [
20539                 {
20540                     tag: 'table',
20541                     cls: 'table-condensed',
20542                     cn:[
20543                         Roo.bootstrap.DateField.content
20544                     ]
20545                 }
20546                 ]
20547             }
20548         ]
20549     }
20550 });
20551
20552  
20553
20554  
20555  /*
20556  * - LGPL
20557  *
20558  * CheckBox
20559  * 
20560  */
20561
20562 /**
20563  * @class Roo.bootstrap.CheckBox
20564  * @extends Roo.bootstrap.Input
20565  * Bootstrap CheckBox class
20566  * 
20567  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20568  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20569  * @cfg {String} boxLabel The text that appears beside the checkbox
20570  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20571  * @cfg {Boolean} checked initnal the element
20572  * @cfg {Boolean} inline inline the element (default false)
20573  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20574  * @cfg {String} tooltip label tooltip
20575  * 
20576  * @constructor
20577  * Create a new CheckBox
20578  * @param {Object} config The config object
20579  */
20580
20581 Roo.bootstrap.CheckBox = function(config){
20582     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20583    
20584     this.addEvents({
20585         /**
20586         * @event check
20587         * Fires when the element is checked or unchecked.
20588         * @param {Roo.bootstrap.CheckBox} this This input
20589         * @param {Boolean} checked The new checked value
20590         */
20591        check : true,
20592        /**
20593         * @event click
20594         * Fires when the element is click.
20595         * @param {Roo.bootstrap.CheckBox} this This input
20596         */
20597        click : true
20598     });
20599     
20600 };
20601
20602 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20603   
20604     inputType: 'checkbox',
20605     inputValue: 1,
20606     valueOff: 0,
20607     boxLabel: false,
20608     checked: false,
20609     weight : false,
20610     inline: false,
20611     tooltip : '',
20612     
20613     getAutoCreate : function()
20614     {
20615         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20616         
20617         var id = Roo.id();
20618         
20619         var cfg = {};
20620         
20621         cfg.cls = 'form-group ' + this.inputType; //input-group
20622         
20623         if(this.inline){
20624             cfg.cls += ' ' + this.inputType + '-inline';
20625         }
20626         
20627         var input =  {
20628             tag: 'input',
20629             id : id,
20630             type : this.inputType,
20631             value : this.inputValue,
20632             cls : 'roo-' + this.inputType, //'form-box',
20633             placeholder : this.placeholder || ''
20634             
20635         };
20636         
20637         if(this.inputType != 'radio'){
20638             var hidden =  {
20639                 tag: 'input',
20640                 type : 'hidden',
20641                 cls : 'roo-hidden-value',
20642                 value : this.checked ? this.inputValue : this.valueOff
20643             };
20644         }
20645         
20646             
20647         if (this.weight) { // Validity check?
20648             cfg.cls += " " + this.inputType + "-" + this.weight;
20649         }
20650         
20651         if (this.disabled) {
20652             input.disabled=true;
20653         }
20654         
20655         if(this.checked){
20656             input.checked = this.checked;
20657         }
20658         
20659         if (this.name) {
20660             
20661             input.name = this.name;
20662             
20663             if(this.inputType != 'radio'){
20664                 hidden.name = this.name;
20665                 input.name = '_hidden_' + this.name;
20666             }
20667         }
20668         
20669         if (this.size) {
20670             input.cls += ' input-' + this.size;
20671         }
20672         
20673         var settings=this;
20674         
20675         ['xs','sm','md','lg'].map(function(size){
20676             if (settings[size]) {
20677                 cfg.cls += ' col-' + size + '-' + settings[size];
20678             }
20679         });
20680         
20681         var inputblock = input;
20682          
20683         if (this.before || this.after) {
20684             
20685             inputblock = {
20686                 cls : 'input-group',
20687                 cn :  [] 
20688             };
20689             
20690             if (this.before) {
20691                 inputblock.cn.push({
20692                     tag :'span',
20693                     cls : 'input-group-addon',
20694                     html : this.before
20695                 });
20696             }
20697             
20698             inputblock.cn.push(input);
20699             
20700             if(this.inputType != 'radio'){
20701                 inputblock.cn.push(hidden);
20702             }
20703             
20704             if (this.after) {
20705                 inputblock.cn.push({
20706                     tag :'span',
20707                     cls : 'input-group-addon',
20708                     html : this.after
20709                 });
20710             }
20711             
20712         }
20713         
20714         if (align ==='left' && this.fieldLabel.length) {
20715 //                Roo.log("left and has label");
20716             cfg.cn = [
20717                 {
20718                     tag: 'label',
20719                     'for' :  id,
20720                     cls : 'control-label',
20721                     html : this.fieldLabel
20722                 },
20723                 {
20724                     cls : "", 
20725                     cn: [
20726                         inputblock
20727                     ]
20728                 }
20729             ];
20730             
20731             if(this.labelWidth > 12){
20732                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20733             }
20734             
20735             if(this.labelWidth < 13 && this.labelmd == 0){
20736                 this.labelmd = this.labelWidth;
20737             }
20738             
20739             if(this.labellg > 0){
20740                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20741                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20742             }
20743             
20744             if(this.labelmd > 0){
20745                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20746                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20747             }
20748             
20749             if(this.labelsm > 0){
20750                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20751                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20752             }
20753             
20754             if(this.labelxs > 0){
20755                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20756                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20757             }
20758             
20759         } else if ( this.fieldLabel.length) {
20760 //                Roo.log(" label");
20761                 cfg.cn = [
20762                    
20763                     {
20764                         tag: this.boxLabel ? 'span' : 'label',
20765                         'for': id,
20766                         cls: 'control-label box-input-label',
20767                         //cls : 'input-group-addon',
20768                         html : this.fieldLabel
20769                     },
20770                     
20771                     inputblock
20772                     
20773                 ];
20774
20775         } else {
20776             
20777 //                Roo.log(" no label && no align");
20778                 cfg.cn = [  inputblock ] ;
20779                 
20780                 
20781         }
20782         
20783         if(this.boxLabel){
20784              var boxLabelCfg = {
20785                 tag: 'label',
20786                 //'for': id, // box label is handled by onclick - so no for...
20787                 cls: 'box-label',
20788                 html: this.boxLabel
20789             };
20790             
20791             if(this.tooltip){
20792                 boxLabelCfg.tooltip = this.tooltip;
20793             }
20794              
20795             cfg.cn.push(boxLabelCfg);
20796         }
20797         
20798         if(this.inputType != 'radio'){
20799             cfg.cn.push(hidden);
20800         }
20801         
20802         return cfg;
20803         
20804     },
20805     
20806     /**
20807      * return the real input element.
20808      */
20809     inputEl: function ()
20810     {
20811         return this.el.select('input.roo-' + this.inputType,true).first();
20812     },
20813     hiddenEl: function ()
20814     {
20815         return this.el.select('input.roo-hidden-value',true).first();
20816     },
20817     
20818     labelEl: function()
20819     {
20820         return this.el.select('label.control-label',true).first();
20821     },
20822     /* depricated... */
20823     
20824     label: function()
20825     {
20826         return this.labelEl();
20827     },
20828     
20829     boxLabelEl: function()
20830     {
20831         return this.el.select('label.box-label',true).first();
20832     },
20833     
20834     initEvents : function()
20835     {
20836 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20837         
20838         this.inputEl().on('click', this.onClick,  this);
20839         
20840         if (this.boxLabel) { 
20841             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20842         }
20843         
20844         this.startValue = this.getValue();
20845         
20846         if(this.groupId){
20847             Roo.bootstrap.CheckBox.register(this);
20848         }
20849     },
20850     
20851     onClick : function(e)
20852     {   
20853         if(this.fireEvent('click', this, e) !== false){
20854             this.setChecked(!this.checked);
20855         }
20856         
20857     },
20858     
20859     setChecked : function(state,suppressEvent)
20860     {
20861         this.startValue = this.getValue();
20862
20863         if(this.inputType == 'radio'){
20864             
20865             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20866                 e.dom.checked = false;
20867             });
20868             
20869             this.inputEl().dom.checked = true;
20870             
20871             this.inputEl().dom.value = this.inputValue;
20872             
20873             if(suppressEvent !== true){
20874                 this.fireEvent('check', this, true);
20875             }
20876             
20877             this.validate();
20878             
20879             return;
20880         }
20881         
20882         this.checked = state;
20883         
20884         this.inputEl().dom.checked = state;
20885         
20886         
20887         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20888         
20889         if(suppressEvent !== true){
20890             this.fireEvent('check', this, state);
20891         }
20892         
20893         this.validate();
20894     },
20895     
20896     getValue : function()
20897     {
20898         if(this.inputType == 'radio'){
20899             return this.getGroupValue();
20900         }
20901         
20902         return this.hiddenEl().dom.value;
20903         
20904     },
20905     
20906     getGroupValue : function()
20907     {
20908         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20909             return '';
20910         }
20911         
20912         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20913     },
20914     
20915     setValue : function(v,suppressEvent)
20916     {
20917         if(this.inputType == 'radio'){
20918             this.setGroupValue(v, suppressEvent);
20919             return;
20920         }
20921         
20922         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20923         
20924         this.validate();
20925     },
20926     
20927     setGroupValue : function(v, suppressEvent)
20928     {
20929         this.startValue = this.getValue();
20930         
20931         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20932             e.dom.checked = false;
20933             
20934             if(e.dom.value == v){
20935                 e.dom.checked = true;
20936             }
20937         });
20938         
20939         if(suppressEvent !== true){
20940             this.fireEvent('check', this, true);
20941         }
20942
20943         this.validate();
20944         
20945         return;
20946     },
20947     
20948     validate : function()
20949     {
20950         if(this.getVisibilityEl().hasClass('hidden')){
20951             return true;
20952         }
20953         
20954         if(
20955                 this.disabled || 
20956                 (this.inputType == 'radio' && this.validateRadio()) ||
20957                 (this.inputType == 'checkbox' && this.validateCheckbox())
20958         ){
20959             this.markValid();
20960             return true;
20961         }
20962         
20963         this.markInvalid();
20964         return false;
20965     },
20966     
20967     validateRadio : function()
20968     {
20969         if(this.getVisibilityEl().hasClass('hidden')){
20970             return true;
20971         }
20972         
20973         if(this.allowBlank){
20974             return true;
20975         }
20976         
20977         var valid = false;
20978         
20979         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20980             if(!e.dom.checked){
20981                 return;
20982             }
20983             
20984             valid = true;
20985             
20986             return false;
20987         });
20988         
20989         return valid;
20990     },
20991     
20992     validateCheckbox : function()
20993     {
20994         if(!this.groupId){
20995             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20996             //return (this.getValue() == this.inputValue) ? true : false;
20997         }
20998         
20999         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21000         
21001         if(!group){
21002             return false;
21003         }
21004         
21005         var r = false;
21006         
21007         for(var i in group){
21008             if(group[i].el.isVisible(true)){
21009                 r = false;
21010                 break;
21011             }
21012             
21013             r = true;
21014         }
21015         
21016         for(var i in group){
21017             if(r){
21018                 break;
21019             }
21020             
21021             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21022         }
21023         
21024         return r;
21025     },
21026     
21027     /**
21028      * Mark this field as valid
21029      */
21030     markValid : function()
21031     {
21032         var _this = this;
21033         
21034         this.fireEvent('valid', this);
21035         
21036         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21037         
21038         if(this.groupId){
21039             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21040         }
21041         
21042         if(label){
21043             label.markValid();
21044         }
21045
21046         if(this.inputType == 'radio'){
21047             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21048                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21049                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21050             });
21051             
21052             return;
21053         }
21054
21055         if(!this.groupId){
21056             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21057             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21058             return;
21059         }
21060         
21061         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21062         
21063         if(!group){
21064             return;
21065         }
21066         
21067         for(var i in group){
21068             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21069             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21070         }
21071     },
21072     
21073      /**
21074      * Mark this field as invalid
21075      * @param {String} msg The validation message
21076      */
21077     markInvalid : function(msg)
21078     {
21079         if(this.allowBlank){
21080             return;
21081         }
21082         
21083         var _this = this;
21084         
21085         this.fireEvent('invalid', this, msg);
21086         
21087         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21088         
21089         if(this.groupId){
21090             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21091         }
21092         
21093         if(label){
21094             label.markInvalid();
21095         }
21096             
21097         if(this.inputType == 'radio'){
21098             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21099                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21100                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21101             });
21102             
21103             return;
21104         }
21105         
21106         if(!this.groupId){
21107             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21108             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21109             return;
21110         }
21111         
21112         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21113         
21114         if(!group){
21115             return;
21116         }
21117         
21118         for(var i in group){
21119             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21120             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21121         }
21122         
21123     },
21124     
21125     clearInvalid : function()
21126     {
21127         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21128         
21129         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21130         
21131         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21132         
21133         if (label && label.iconEl) {
21134             label.iconEl.removeClass(label.validClass);
21135             label.iconEl.removeClass(label.invalidClass);
21136         }
21137     },
21138     
21139     disable : function()
21140     {
21141         if(this.inputType != 'radio'){
21142             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21143             return;
21144         }
21145         
21146         var _this = this;
21147         
21148         if(this.rendered){
21149             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21150                 _this.getActionEl().addClass(this.disabledClass);
21151                 e.dom.disabled = true;
21152             });
21153         }
21154         
21155         this.disabled = true;
21156         this.fireEvent("disable", this);
21157         return this;
21158     },
21159
21160     enable : function()
21161     {
21162         if(this.inputType != 'radio'){
21163             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21164             return;
21165         }
21166         
21167         var _this = this;
21168         
21169         if(this.rendered){
21170             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21171                 _this.getActionEl().removeClass(this.disabledClass);
21172                 e.dom.disabled = false;
21173             });
21174         }
21175         
21176         this.disabled = false;
21177         this.fireEvent("enable", this);
21178         return this;
21179     },
21180     
21181     setBoxLabel : function(v)
21182     {
21183         this.boxLabel = v;
21184         
21185         if(this.rendered){
21186             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21187         }
21188     }
21189
21190 });
21191
21192 Roo.apply(Roo.bootstrap.CheckBox, {
21193     
21194     groups: {},
21195     
21196      /**
21197     * register a CheckBox Group
21198     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21199     */
21200     register : function(checkbox)
21201     {
21202         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21203             this.groups[checkbox.groupId] = {};
21204         }
21205         
21206         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21207             return;
21208         }
21209         
21210         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21211         
21212     },
21213     /**
21214     * fetch a CheckBox Group based on the group ID
21215     * @param {string} the group ID
21216     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21217     */
21218     get: function(groupId) {
21219         if (typeof(this.groups[groupId]) == 'undefined') {
21220             return false;
21221         }
21222         
21223         return this.groups[groupId] ;
21224     }
21225     
21226     
21227 });
21228 /*
21229  * - LGPL
21230  *
21231  * RadioItem
21232  * 
21233  */
21234
21235 /**
21236  * @class Roo.bootstrap.Radio
21237  * @extends Roo.bootstrap.Component
21238  * Bootstrap Radio class
21239  * @cfg {String} boxLabel - the label associated
21240  * @cfg {String} value - the value of radio
21241  * 
21242  * @constructor
21243  * Create a new Radio
21244  * @param {Object} config The config object
21245  */
21246 Roo.bootstrap.Radio = function(config){
21247     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21248     
21249 };
21250
21251 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21252     
21253     boxLabel : '',
21254     
21255     value : '',
21256     
21257     getAutoCreate : function()
21258     {
21259         var cfg = {
21260             tag : 'div',
21261             cls : 'form-group radio',
21262             cn : [
21263                 {
21264                     tag : 'label',
21265                     cls : 'box-label',
21266                     html : this.boxLabel
21267                 }
21268             ]
21269         };
21270         
21271         return cfg;
21272     },
21273     
21274     initEvents : function() 
21275     {
21276         this.parent().register(this);
21277         
21278         this.el.on('click', this.onClick, this);
21279         
21280     },
21281     
21282     onClick : function(e)
21283     {
21284         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21285             this.setChecked(true);
21286         }
21287     },
21288     
21289     setChecked : function(state, suppressEvent)
21290     {
21291         this.parent().setValue(this.value, suppressEvent);
21292         
21293     },
21294     
21295     setBoxLabel : function(v)
21296     {
21297         this.boxLabel = v;
21298         
21299         if(this.rendered){
21300             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21301         }
21302     }
21303     
21304 });
21305  
21306
21307  /*
21308  * - LGPL
21309  *
21310  * Input
21311  * 
21312  */
21313
21314 /**
21315  * @class Roo.bootstrap.SecurePass
21316  * @extends Roo.bootstrap.Input
21317  * Bootstrap SecurePass class
21318  *
21319  * 
21320  * @constructor
21321  * Create a new SecurePass
21322  * @param {Object} config The config object
21323  */
21324  
21325 Roo.bootstrap.SecurePass = function (config) {
21326     // these go here, so the translation tool can replace them..
21327     this.errors = {
21328         PwdEmpty: "Please type a password, and then retype it to confirm.",
21329         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21330         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21331         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21332         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21333         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21334         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21335         TooWeak: "Your password is Too Weak."
21336     },
21337     this.meterLabel = "Password strength:";
21338     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21339     this.meterClass = [
21340         "roo-password-meter-tooweak", 
21341         "roo-password-meter-weak", 
21342         "roo-password-meter-medium", 
21343         "roo-password-meter-strong", 
21344         "roo-password-meter-grey"
21345     ];
21346     
21347     this.errors = {};
21348     
21349     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21350 }
21351
21352 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21353     /**
21354      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21355      * {
21356      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21357      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21358      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21359      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21360      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21361      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21362      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21363      * })
21364      */
21365     // private
21366     
21367     meterWidth: 300,
21368     errorMsg :'',    
21369     errors: false,
21370     imageRoot: '/',
21371     /**
21372      * @cfg {String/Object} Label for the strength meter (defaults to
21373      * 'Password strength:')
21374      */
21375     // private
21376     meterLabel: '',
21377     /**
21378      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21379      * ['Weak', 'Medium', 'Strong'])
21380      */
21381     // private    
21382     pwdStrengths: false,    
21383     // private
21384     strength: 0,
21385     // private
21386     _lastPwd: null,
21387     // private
21388     kCapitalLetter: 0,
21389     kSmallLetter: 1,
21390     kDigit: 2,
21391     kPunctuation: 3,
21392     
21393     insecure: false,
21394     // private
21395     initEvents: function ()
21396     {
21397         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21398
21399         if (this.el.is('input[type=password]') && Roo.isSafari) {
21400             this.el.on('keydown', this.SafariOnKeyDown, this);
21401         }
21402
21403         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21404     },
21405     // private
21406     onRender: function (ct, position)
21407     {
21408         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21409         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21410         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21411
21412         this.trigger.createChild({
21413                    cn: [
21414                     {
21415                     //id: 'PwdMeter',
21416                     tag: 'div',
21417                     cls: 'roo-password-meter-grey col-xs-12',
21418                     style: {
21419                         //width: 0,
21420                         //width: this.meterWidth + 'px'                                                
21421                         }
21422                     },
21423                     {                            
21424                          cls: 'roo-password-meter-text'                          
21425                     }
21426                 ]            
21427         });
21428
21429          
21430         if (this.hideTrigger) {
21431             this.trigger.setDisplayed(false);
21432         }
21433         this.setSize(this.width || '', this.height || '');
21434     },
21435     // private
21436     onDestroy: function ()
21437     {
21438         if (this.trigger) {
21439             this.trigger.removeAllListeners();
21440             this.trigger.remove();
21441         }
21442         if (this.wrap) {
21443             this.wrap.remove();
21444         }
21445         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21446     },
21447     // private
21448     checkStrength: function ()
21449     {
21450         var pwd = this.inputEl().getValue();
21451         if (pwd == this._lastPwd) {
21452             return;
21453         }
21454
21455         var strength;
21456         if (this.ClientSideStrongPassword(pwd)) {
21457             strength = 3;
21458         } else if (this.ClientSideMediumPassword(pwd)) {
21459             strength = 2;
21460         } else if (this.ClientSideWeakPassword(pwd)) {
21461             strength = 1;
21462         } else {
21463             strength = 0;
21464         }
21465         
21466         Roo.log('strength1: ' + strength);
21467         
21468         //var pm = this.trigger.child('div/div/div').dom;
21469         var pm = this.trigger.child('div/div');
21470         pm.removeClass(this.meterClass);
21471         pm.addClass(this.meterClass[strength]);
21472                 
21473         
21474         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21475                 
21476         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21477         
21478         this._lastPwd = pwd;
21479     },
21480     reset: function ()
21481     {
21482         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21483         
21484         this._lastPwd = '';
21485         
21486         var pm = this.trigger.child('div/div');
21487         pm.removeClass(this.meterClass);
21488         pm.addClass('roo-password-meter-grey');        
21489         
21490         
21491         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21492         
21493         pt.innerHTML = '';
21494         this.inputEl().dom.type='password';
21495     },
21496     // private
21497     validateValue: function (value)
21498     {
21499         
21500         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21501             return false;
21502         }
21503         if (value.length == 0) {
21504             if (this.allowBlank) {
21505                 this.clearInvalid();
21506                 return true;
21507             }
21508
21509             this.markInvalid(this.errors.PwdEmpty);
21510             this.errorMsg = this.errors.PwdEmpty;
21511             return false;
21512         }
21513         
21514         if(this.insecure){
21515             return true;
21516         }
21517         
21518         if ('[\x21-\x7e]*'.match(value)) {
21519             this.markInvalid(this.errors.PwdBadChar);
21520             this.errorMsg = this.errors.PwdBadChar;
21521             return false;
21522         }
21523         if (value.length < 6) {
21524             this.markInvalid(this.errors.PwdShort);
21525             this.errorMsg = this.errors.PwdShort;
21526             return false;
21527         }
21528         if (value.length > 16) {
21529             this.markInvalid(this.errors.PwdLong);
21530             this.errorMsg = this.errors.PwdLong;
21531             return false;
21532         }
21533         var strength;
21534         if (this.ClientSideStrongPassword(value)) {
21535             strength = 3;
21536         } else if (this.ClientSideMediumPassword(value)) {
21537             strength = 2;
21538         } else if (this.ClientSideWeakPassword(value)) {
21539             strength = 1;
21540         } else {
21541             strength = 0;
21542         }
21543
21544         
21545         if (strength < 2) {
21546             //this.markInvalid(this.errors.TooWeak);
21547             this.errorMsg = this.errors.TooWeak;
21548             //return false;
21549         }
21550         
21551         
21552         console.log('strength2: ' + strength);
21553         
21554         //var pm = this.trigger.child('div/div/div').dom;
21555         
21556         var pm = this.trigger.child('div/div');
21557         pm.removeClass(this.meterClass);
21558         pm.addClass(this.meterClass[strength]);
21559                 
21560         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21561                 
21562         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21563         
21564         this.errorMsg = ''; 
21565         return true;
21566     },
21567     // private
21568     CharacterSetChecks: function (type)
21569     {
21570         this.type = type;
21571         this.fResult = false;
21572     },
21573     // private
21574     isctype: function (character, type)
21575     {
21576         switch (type) {  
21577             case this.kCapitalLetter:
21578                 if (character >= 'A' && character <= 'Z') {
21579                     return true;
21580                 }
21581                 break;
21582             
21583             case this.kSmallLetter:
21584                 if (character >= 'a' && character <= 'z') {
21585                     return true;
21586                 }
21587                 break;
21588             
21589             case this.kDigit:
21590                 if (character >= '0' && character <= '9') {
21591                     return true;
21592                 }
21593                 break;
21594             
21595             case this.kPunctuation:
21596                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21597                     return true;
21598                 }
21599                 break;
21600             
21601             default:
21602                 return false;
21603         }
21604
21605     },
21606     // private
21607     IsLongEnough: function (pwd, size)
21608     {
21609         return !(pwd == null || isNaN(size) || pwd.length < size);
21610     },
21611     // private
21612     SpansEnoughCharacterSets: function (word, nb)
21613     {
21614         if (!this.IsLongEnough(word, nb))
21615         {
21616             return false;
21617         }
21618
21619         var characterSetChecks = new Array(
21620             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21621             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21622         );
21623         
21624         for (var index = 0; index < word.length; ++index) {
21625             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21626                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21627                     characterSetChecks[nCharSet].fResult = true;
21628                     break;
21629                 }
21630             }
21631         }
21632
21633         var nCharSets = 0;
21634         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21635             if (characterSetChecks[nCharSet].fResult) {
21636                 ++nCharSets;
21637             }
21638         }
21639
21640         if (nCharSets < nb) {
21641             return false;
21642         }
21643         return true;
21644     },
21645     // private
21646     ClientSideStrongPassword: function (pwd)
21647     {
21648         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21649     },
21650     // private
21651     ClientSideMediumPassword: function (pwd)
21652     {
21653         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21654     },
21655     // private
21656     ClientSideWeakPassword: function (pwd)
21657     {
21658         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21659     }
21660           
21661 })//<script type="text/javascript">
21662
21663 /*
21664  * Based  Ext JS Library 1.1.1
21665  * Copyright(c) 2006-2007, Ext JS, LLC.
21666  * LGPL
21667  *
21668  */
21669  
21670 /**
21671  * @class Roo.HtmlEditorCore
21672  * @extends Roo.Component
21673  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21674  *
21675  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21676  */
21677
21678 Roo.HtmlEditorCore = function(config){
21679     
21680     
21681     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21682     
21683     
21684     this.addEvents({
21685         /**
21686          * @event initialize
21687          * Fires when the editor is fully initialized (including the iframe)
21688          * @param {Roo.HtmlEditorCore} this
21689          */
21690         initialize: true,
21691         /**
21692          * @event activate
21693          * Fires when the editor is first receives the focus. Any insertion must wait
21694          * until after this event.
21695          * @param {Roo.HtmlEditorCore} this
21696          */
21697         activate: true,
21698          /**
21699          * @event beforesync
21700          * Fires before the textarea is updated with content from the editor iframe. Return false
21701          * to cancel the sync.
21702          * @param {Roo.HtmlEditorCore} this
21703          * @param {String} html
21704          */
21705         beforesync: true,
21706          /**
21707          * @event beforepush
21708          * Fires before the iframe editor is updated with content from the textarea. Return false
21709          * to cancel the push.
21710          * @param {Roo.HtmlEditorCore} this
21711          * @param {String} html
21712          */
21713         beforepush: true,
21714          /**
21715          * @event sync
21716          * Fires when the textarea is updated with content from the editor iframe.
21717          * @param {Roo.HtmlEditorCore} this
21718          * @param {String} html
21719          */
21720         sync: true,
21721          /**
21722          * @event push
21723          * Fires when the iframe editor is updated with content from the textarea.
21724          * @param {Roo.HtmlEditorCore} this
21725          * @param {String} html
21726          */
21727         push: true,
21728         
21729         /**
21730          * @event editorevent
21731          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21732          * @param {Roo.HtmlEditorCore} this
21733          */
21734         editorevent: true
21735         
21736     });
21737     
21738     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21739     
21740     // defaults : white / black...
21741     this.applyBlacklists();
21742     
21743     
21744     
21745 };
21746
21747
21748 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21749
21750
21751      /**
21752      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21753      */
21754     
21755     owner : false,
21756     
21757      /**
21758      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21759      *                        Roo.resizable.
21760      */
21761     resizable : false,
21762      /**
21763      * @cfg {Number} height (in pixels)
21764      */   
21765     height: 300,
21766    /**
21767      * @cfg {Number} width (in pixels)
21768      */   
21769     width: 500,
21770     
21771     /**
21772      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21773      * 
21774      */
21775     stylesheets: false,
21776     
21777     // id of frame..
21778     frameId: false,
21779     
21780     // private properties
21781     validationEvent : false,
21782     deferHeight: true,
21783     initialized : false,
21784     activated : false,
21785     sourceEditMode : false,
21786     onFocus : Roo.emptyFn,
21787     iframePad:3,
21788     hideMode:'offsets',
21789     
21790     clearUp: true,
21791     
21792     // blacklist + whitelisted elements..
21793     black: false,
21794     white: false,
21795      
21796     bodyCls : '',
21797
21798     /**
21799      * Protected method that will not generally be called directly. It
21800      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21801      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21802      */
21803     getDocMarkup : function(){
21804         // body styles..
21805         var st = '';
21806         
21807         // inherit styels from page...?? 
21808         if (this.stylesheets === false) {
21809             
21810             Roo.get(document.head).select('style').each(function(node) {
21811                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21812             });
21813             
21814             Roo.get(document.head).select('link').each(function(node) { 
21815                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21816             });
21817             
21818         } else if (!this.stylesheets.length) {
21819                 // simple..
21820                 st = '<style type="text/css">' +
21821                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21822                    '</style>';
21823         } else { 
21824             st = '<style type="text/css">' +
21825                     this.stylesheets +
21826                 '</style>';
21827         }
21828         
21829         st +=  '<style type="text/css">' +
21830             'IMG { cursor: pointer } ' +
21831         '</style>';
21832
21833         var cls = 'roo-htmleditor-body';
21834         
21835         if(this.bodyCls.length){
21836             cls += ' ' + this.bodyCls;
21837         }
21838         
21839         return '<html><head>' + st  +
21840             //<style type="text/css">' +
21841             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21842             //'</style>' +
21843             ' </head><body class="' +  cls + '"></body></html>';
21844     },
21845
21846     // private
21847     onRender : function(ct, position)
21848     {
21849         var _t = this;
21850         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21851         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21852         
21853         
21854         this.el.dom.style.border = '0 none';
21855         this.el.dom.setAttribute('tabIndex', -1);
21856         this.el.addClass('x-hidden hide');
21857         
21858         
21859         
21860         if(Roo.isIE){ // fix IE 1px bogus margin
21861             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21862         }
21863        
21864         
21865         this.frameId = Roo.id();
21866         
21867          
21868         
21869         var iframe = this.owner.wrap.createChild({
21870             tag: 'iframe',
21871             cls: 'form-control', // bootstrap..
21872             id: this.frameId,
21873             name: this.frameId,
21874             frameBorder : 'no',
21875             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21876         }, this.el
21877         );
21878         
21879         
21880         this.iframe = iframe.dom;
21881
21882          this.assignDocWin();
21883         
21884         this.doc.designMode = 'on';
21885        
21886         this.doc.open();
21887         this.doc.write(this.getDocMarkup());
21888         this.doc.close();
21889
21890         
21891         var task = { // must defer to wait for browser to be ready
21892             run : function(){
21893                 //console.log("run task?" + this.doc.readyState);
21894                 this.assignDocWin();
21895                 if(this.doc.body || this.doc.readyState == 'complete'){
21896                     try {
21897                         this.doc.designMode="on";
21898                     } catch (e) {
21899                         return;
21900                     }
21901                     Roo.TaskMgr.stop(task);
21902                     this.initEditor.defer(10, this);
21903                 }
21904             },
21905             interval : 10,
21906             duration: 10000,
21907             scope: this
21908         };
21909         Roo.TaskMgr.start(task);
21910
21911     },
21912
21913     // private
21914     onResize : function(w, h)
21915     {
21916          Roo.log('resize: ' +w + ',' + h );
21917         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21918         if(!this.iframe){
21919             return;
21920         }
21921         if(typeof w == 'number'){
21922             
21923             this.iframe.style.width = w + 'px';
21924         }
21925         if(typeof h == 'number'){
21926             
21927             this.iframe.style.height = h + 'px';
21928             if(this.doc){
21929                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21930             }
21931         }
21932         
21933     },
21934
21935     /**
21936      * Toggles the editor between standard and source edit mode.
21937      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21938      */
21939     toggleSourceEdit : function(sourceEditMode){
21940         
21941         this.sourceEditMode = sourceEditMode === true;
21942         
21943         if(this.sourceEditMode){
21944  
21945             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21946             
21947         }else{
21948             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21949             //this.iframe.className = '';
21950             this.deferFocus();
21951         }
21952         //this.setSize(this.owner.wrap.getSize());
21953         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21954     },
21955
21956     
21957   
21958
21959     /**
21960      * Protected method that will not generally be called directly. If you need/want
21961      * custom HTML cleanup, this is the method you should override.
21962      * @param {String} html The HTML to be cleaned
21963      * return {String} The cleaned HTML
21964      */
21965     cleanHtml : function(html){
21966         html = String(html);
21967         if(html.length > 5){
21968             if(Roo.isSafari){ // strip safari nonsense
21969                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21970             }
21971         }
21972         if(html == '&nbsp;'){
21973             html = '';
21974         }
21975         return html;
21976     },
21977
21978     /**
21979      * HTML Editor -> Textarea
21980      * Protected method that will not generally be called directly. Syncs the contents
21981      * of the editor iframe with the textarea.
21982      */
21983     syncValue : function(){
21984         if(this.initialized){
21985             var bd = (this.doc.body || this.doc.documentElement);
21986             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21987             var html = bd.innerHTML;
21988             if(Roo.isSafari){
21989                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21990                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21991                 if(m && m[1]){
21992                     html = '<div style="'+m[0]+'">' + html + '</div>';
21993                 }
21994             }
21995             html = this.cleanHtml(html);
21996             // fix up the special chars.. normaly like back quotes in word...
21997             // however we do not want to do this with chinese..
21998             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21999                 var cc = b.charCodeAt();
22000                 if (
22001                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22002                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22003                     (cc >= 0xf900 && cc < 0xfb00 )
22004                 ) {
22005                         return b;
22006                 }
22007                 return "&#"+cc+";" 
22008             });
22009             if(this.owner.fireEvent('beforesync', this, html) !== false){
22010                 this.el.dom.value = html;
22011                 this.owner.fireEvent('sync', this, html);
22012             }
22013         }
22014     },
22015
22016     /**
22017      * Protected method that will not generally be called directly. Pushes the value of the textarea
22018      * into the iframe editor.
22019      */
22020     pushValue : function(){
22021         if(this.initialized){
22022             var v = this.el.dom.value.trim();
22023             
22024 //            if(v.length < 1){
22025 //                v = '&#160;';
22026 //            }
22027             
22028             if(this.owner.fireEvent('beforepush', this, v) !== false){
22029                 var d = (this.doc.body || this.doc.documentElement);
22030                 d.innerHTML = v;
22031                 this.cleanUpPaste();
22032                 this.el.dom.value = d.innerHTML;
22033                 this.owner.fireEvent('push', this, v);
22034             }
22035         }
22036     },
22037
22038     // private
22039     deferFocus : function(){
22040         this.focus.defer(10, this);
22041     },
22042
22043     // doc'ed in Field
22044     focus : function(){
22045         if(this.win && !this.sourceEditMode){
22046             this.win.focus();
22047         }else{
22048             this.el.focus();
22049         }
22050     },
22051     
22052     assignDocWin: function()
22053     {
22054         var iframe = this.iframe;
22055         
22056          if(Roo.isIE){
22057             this.doc = iframe.contentWindow.document;
22058             this.win = iframe.contentWindow;
22059         } else {
22060 //            if (!Roo.get(this.frameId)) {
22061 //                return;
22062 //            }
22063 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22064 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22065             
22066             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22067                 return;
22068             }
22069             
22070             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22071             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22072         }
22073     },
22074     
22075     // private
22076     initEditor : function(){
22077         //console.log("INIT EDITOR");
22078         this.assignDocWin();
22079         
22080         
22081         
22082         this.doc.designMode="on";
22083         this.doc.open();
22084         this.doc.write(this.getDocMarkup());
22085         this.doc.close();
22086         
22087         var dbody = (this.doc.body || this.doc.documentElement);
22088         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22089         // this copies styles from the containing element into thsi one..
22090         // not sure why we need all of this..
22091         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22092         
22093         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22094         //ss['background-attachment'] = 'fixed'; // w3c
22095         dbody.bgProperties = 'fixed'; // ie
22096         //Roo.DomHelper.applyStyles(dbody, ss);
22097         Roo.EventManager.on(this.doc, {
22098             //'mousedown': this.onEditorEvent,
22099             'mouseup': this.onEditorEvent,
22100             'dblclick': this.onEditorEvent,
22101             'click': this.onEditorEvent,
22102             'keyup': this.onEditorEvent,
22103             buffer:100,
22104             scope: this
22105         });
22106         if(Roo.isGecko){
22107             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22108         }
22109         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22110             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22111         }
22112         this.initialized = true;
22113
22114         this.owner.fireEvent('initialize', this);
22115         this.pushValue();
22116     },
22117
22118     // private
22119     onDestroy : function(){
22120         
22121         
22122         
22123         if(this.rendered){
22124             
22125             //for (var i =0; i < this.toolbars.length;i++) {
22126             //    // fixme - ask toolbars for heights?
22127             //    this.toolbars[i].onDestroy();
22128            // }
22129             
22130             //this.wrap.dom.innerHTML = '';
22131             //this.wrap.remove();
22132         }
22133     },
22134
22135     // private
22136     onFirstFocus : function(){
22137         
22138         this.assignDocWin();
22139         
22140         
22141         this.activated = true;
22142          
22143     
22144         if(Roo.isGecko){ // prevent silly gecko errors
22145             this.win.focus();
22146             var s = this.win.getSelection();
22147             if(!s.focusNode || s.focusNode.nodeType != 3){
22148                 var r = s.getRangeAt(0);
22149                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22150                 r.collapse(true);
22151                 this.deferFocus();
22152             }
22153             try{
22154                 this.execCmd('useCSS', true);
22155                 this.execCmd('styleWithCSS', false);
22156             }catch(e){}
22157         }
22158         this.owner.fireEvent('activate', this);
22159     },
22160
22161     // private
22162     adjustFont: function(btn){
22163         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22164         //if(Roo.isSafari){ // safari
22165         //    adjust *= 2;
22166        // }
22167         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22168         if(Roo.isSafari){ // safari
22169             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22170             v =  (v < 10) ? 10 : v;
22171             v =  (v > 48) ? 48 : v;
22172             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22173             
22174         }
22175         
22176         
22177         v = Math.max(1, v+adjust);
22178         
22179         this.execCmd('FontSize', v  );
22180     },
22181
22182     onEditorEvent : function(e)
22183     {
22184         this.owner.fireEvent('editorevent', this, e);
22185       //  this.updateToolbar();
22186         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22187     },
22188
22189     insertTag : function(tg)
22190     {
22191         // could be a bit smarter... -> wrap the current selected tRoo..
22192         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22193             
22194             range = this.createRange(this.getSelection());
22195             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22196             wrappingNode.appendChild(range.extractContents());
22197             range.insertNode(wrappingNode);
22198
22199             return;
22200             
22201             
22202             
22203         }
22204         this.execCmd("formatblock",   tg);
22205         
22206     },
22207     
22208     insertText : function(txt)
22209     {
22210         
22211         
22212         var range = this.createRange();
22213         range.deleteContents();
22214                //alert(Sender.getAttribute('label'));
22215                
22216         range.insertNode(this.doc.createTextNode(txt));
22217     } ,
22218     
22219      
22220
22221     /**
22222      * Executes a Midas editor command on the editor document and performs necessary focus and
22223      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22224      * @param {String} cmd The Midas command
22225      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22226      */
22227     relayCmd : function(cmd, value){
22228         this.win.focus();
22229         this.execCmd(cmd, value);
22230         this.owner.fireEvent('editorevent', this);
22231         //this.updateToolbar();
22232         this.owner.deferFocus();
22233     },
22234
22235     /**
22236      * Executes a Midas editor command directly on the editor document.
22237      * For visual commands, you should use {@link #relayCmd} instead.
22238      * <b>This should only be called after the editor is initialized.</b>
22239      * @param {String} cmd The Midas command
22240      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22241      */
22242     execCmd : function(cmd, value){
22243         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22244         this.syncValue();
22245     },
22246  
22247  
22248    
22249     /**
22250      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22251      * to insert tRoo.
22252      * @param {String} text | dom node.. 
22253      */
22254     insertAtCursor : function(text)
22255     {
22256         
22257         if(!this.activated){
22258             return;
22259         }
22260         /*
22261         if(Roo.isIE){
22262             this.win.focus();
22263             var r = this.doc.selection.createRange();
22264             if(r){
22265                 r.collapse(true);
22266                 r.pasteHTML(text);
22267                 this.syncValue();
22268                 this.deferFocus();
22269             
22270             }
22271             return;
22272         }
22273         */
22274         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22275             this.win.focus();
22276             
22277             
22278             // from jquery ui (MIT licenced)
22279             var range, node;
22280             var win = this.win;
22281             
22282             if (win.getSelection && win.getSelection().getRangeAt) {
22283                 range = win.getSelection().getRangeAt(0);
22284                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22285                 range.insertNode(node);
22286             } else if (win.document.selection && win.document.selection.createRange) {
22287                 // no firefox support
22288                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22289                 win.document.selection.createRange().pasteHTML(txt);
22290             } else {
22291                 // no firefox support
22292                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22293                 this.execCmd('InsertHTML', txt);
22294             } 
22295             
22296             this.syncValue();
22297             
22298             this.deferFocus();
22299         }
22300     },
22301  // private
22302     mozKeyPress : function(e){
22303         if(e.ctrlKey){
22304             var c = e.getCharCode(), cmd;
22305           
22306             if(c > 0){
22307                 c = String.fromCharCode(c).toLowerCase();
22308                 switch(c){
22309                     case 'b':
22310                         cmd = 'bold';
22311                         break;
22312                     case 'i':
22313                         cmd = 'italic';
22314                         break;
22315                     
22316                     case 'u':
22317                         cmd = 'underline';
22318                         break;
22319                     
22320                     case 'v':
22321                         this.cleanUpPaste.defer(100, this);
22322                         return;
22323                         
22324                 }
22325                 if(cmd){
22326                     this.win.focus();
22327                     this.execCmd(cmd);
22328                     this.deferFocus();
22329                     e.preventDefault();
22330                 }
22331                 
22332             }
22333         }
22334     },
22335
22336     // private
22337     fixKeys : function(){ // load time branching for fastest keydown performance
22338         if(Roo.isIE){
22339             return function(e){
22340                 var k = e.getKey(), r;
22341                 if(k == e.TAB){
22342                     e.stopEvent();
22343                     r = this.doc.selection.createRange();
22344                     if(r){
22345                         r.collapse(true);
22346                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22347                         this.deferFocus();
22348                     }
22349                     return;
22350                 }
22351                 
22352                 if(k == e.ENTER){
22353                     r = this.doc.selection.createRange();
22354                     if(r){
22355                         var target = r.parentElement();
22356                         if(!target || target.tagName.toLowerCase() != 'li'){
22357                             e.stopEvent();
22358                             r.pasteHTML('<br />');
22359                             r.collapse(false);
22360                             r.select();
22361                         }
22362                     }
22363                 }
22364                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22365                     this.cleanUpPaste.defer(100, this);
22366                     return;
22367                 }
22368                 
22369                 
22370             };
22371         }else if(Roo.isOpera){
22372             return function(e){
22373                 var k = e.getKey();
22374                 if(k == e.TAB){
22375                     e.stopEvent();
22376                     this.win.focus();
22377                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22378                     this.deferFocus();
22379                 }
22380                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22381                     this.cleanUpPaste.defer(100, this);
22382                     return;
22383                 }
22384                 
22385             };
22386         }else if(Roo.isSafari){
22387             return function(e){
22388                 var k = e.getKey();
22389                 
22390                 if(k == e.TAB){
22391                     e.stopEvent();
22392                     this.execCmd('InsertText','\t');
22393                     this.deferFocus();
22394                     return;
22395                 }
22396                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22397                     this.cleanUpPaste.defer(100, this);
22398                     return;
22399                 }
22400                 
22401              };
22402         }
22403     }(),
22404     
22405     getAllAncestors: function()
22406     {
22407         var p = this.getSelectedNode();
22408         var a = [];
22409         if (!p) {
22410             a.push(p); // push blank onto stack..
22411             p = this.getParentElement();
22412         }
22413         
22414         
22415         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22416             a.push(p);
22417             p = p.parentNode;
22418         }
22419         a.push(this.doc.body);
22420         return a;
22421     },
22422     lastSel : false,
22423     lastSelNode : false,
22424     
22425     
22426     getSelection : function() 
22427     {
22428         this.assignDocWin();
22429         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22430     },
22431     
22432     getSelectedNode: function() 
22433     {
22434         // this may only work on Gecko!!!
22435         
22436         // should we cache this!!!!
22437         
22438         
22439         
22440          
22441         var range = this.createRange(this.getSelection()).cloneRange();
22442         
22443         if (Roo.isIE) {
22444             var parent = range.parentElement();
22445             while (true) {
22446                 var testRange = range.duplicate();
22447                 testRange.moveToElementText(parent);
22448                 if (testRange.inRange(range)) {
22449                     break;
22450                 }
22451                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22452                     break;
22453                 }
22454                 parent = parent.parentElement;
22455             }
22456             return parent;
22457         }
22458         
22459         // is ancestor a text element.
22460         var ac =  range.commonAncestorContainer;
22461         if (ac.nodeType == 3) {
22462             ac = ac.parentNode;
22463         }
22464         
22465         var ar = ac.childNodes;
22466          
22467         var nodes = [];
22468         var other_nodes = [];
22469         var has_other_nodes = false;
22470         for (var i=0;i<ar.length;i++) {
22471             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22472                 continue;
22473             }
22474             // fullly contained node.
22475             
22476             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22477                 nodes.push(ar[i]);
22478                 continue;
22479             }
22480             
22481             // probably selected..
22482             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22483                 other_nodes.push(ar[i]);
22484                 continue;
22485             }
22486             // outer..
22487             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22488                 continue;
22489             }
22490             
22491             
22492             has_other_nodes = true;
22493         }
22494         if (!nodes.length && other_nodes.length) {
22495             nodes= other_nodes;
22496         }
22497         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22498             return false;
22499         }
22500         
22501         return nodes[0];
22502     },
22503     createRange: function(sel)
22504     {
22505         // this has strange effects when using with 
22506         // top toolbar - not sure if it's a great idea.
22507         //this.editor.contentWindow.focus();
22508         if (typeof sel != "undefined") {
22509             try {
22510                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22511             } catch(e) {
22512                 return this.doc.createRange();
22513             }
22514         } else {
22515             return this.doc.createRange();
22516         }
22517     },
22518     getParentElement: function()
22519     {
22520         
22521         this.assignDocWin();
22522         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22523         
22524         var range = this.createRange(sel);
22525          
22526         try {
22527             var p = range.commonAncestorContainer;
22528             while (p.nodeType == 3) { // text node
22529                 p = p.parentNode;
22530             }
22531             return p;
22532         } catch (e) {
22533             return null;
22534         }
22535     
22536     },
22537     /***
22538      *
22539      * Range intersection.. the hard stuff...
22540      *  '-1' = before
22541      *  '0' = hits..
22542      *  '1' = after.
22543      *         [ -- selected range --- ]
22544      *   [fail]                        [fail]
22545      *
22546      *    basically..
22547      *      if end is before start or  hits it. fail.
22548      *      if start is after end or hits it fail.
22549      *
22550      *   if either hits (but other is outside. - then it's not 
22551      *   
22552      *    
22553      **/
22554     
22555     
22556     // @see http://www.thismuchiknow.co.uk/?p=64.
22557     rangeIntersectsNode : function(range, node)
22558     {
22559         var nodeRange = node.ownerDocument.createRange();
22560         try {
22561             nodeRange.selectNode(node);
22562         } catch (e) {
22563             nodeRange.selectNodeContents(node);
22564         }
22565     
22566         var rangeStartRange = range.cloneRange();
22567         rangeStartRange.collapse(true);
22568     
22569         var rangeEndRange = range.cloneRange();
22570         rangeEndRange.collapse(false);
22571     
22572         var nodeStartRange = nodeRange.cloneRange();
22573         nodeStartRange.collapse(true);
22574     
22575         var nodeEndRange = nodeRange.cloneRange();
22576         nodeEndRange.collapse(false);
22577     
22578         return rangeStartRange.compareBoundaryPoints(
22579                  Range.START_TO_START, nodeEndRange) == -1 &&
22580                rangeEndRange.compareBoundaryPoints(
22581                  Range.START_TO_START, nodeStartRange) == 1;
22582         
22583          
22584     },
22585     rangeCompareNode : function(range, node)
22586     {
22587         var nodeRange = node.ownerDocument.createRange();
22588         try {
22589             nodeRange.selectNode(node);
22590         } catch (e) {
22591             nodeRange.selectNodeContents(node);
22592         }
22593         
22594         
22595         range.collapse(true);
22596     
22597         nodeRange.collapse(true);
22598      
22599         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22600         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22601          
22602         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22603         
22604         var nodeIsBefore   =  ss == 1;
22605         var nodeIsAfter    = ee == -1;
22606         
22607         if (nodeIsBefore && nodeIsAfter) {
22608             return 0; // outer
22609         }
22610         if (!nodeIsBefore && nodeIsAfter) {
22611             return 1; //right trailed.
22612         }
22613         
22614         if (nodeIsBefore && !nodeIsAfter) {
22615             return 2;  // left trailed.
22616         }
22617         // fully contined.
22618         return 3;
22619     },
22620
22621     // private? - in a new class?
22622     cleanUpPaste :  function()
22623     {
22624         // cleans up the whole document..
22625         Roo.log('cleanuppaste');
22626         
22627         this.cleanUpChildren(this.doc.body);
22628         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22629         if (clean != this.doc.body.innerHTML) {
22630             this.doc.body.innerHTML = clean;
22631         }
22632         
22633     },
22634     
22635     cleanWordChars : function(input) {// change the chars to hex code
22636         var he = Roo.HtmlEditorCore;
22637         
22638         var output = input;
22639         Roo.each(he.swapCodes, function(sw) { 
22640             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22641             
22642             output = output.replace(swapper, sw[1]);
22643         });
22644         
22645         return output;
22646     },
22647     
22648     
22649     cleanUpChildren : function (n)
22650     {
22651         if (!n.childNodes.length) {
22652             return;
22653         }
22654         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22655            this.cleanUpChild(n.childNodes[i]);
22656         }
22657     },
22658     
22659     
22660         
22661     
22662     cleanUpChild : function (node)
22663     {
22664         var ed = this;
22665         //console.log(node);
22666         if (node.nodeName == "#text") {
22667             // clean up silly Windows -- stuff?
22668             return; 
22669         }
22670         if (node.nodeName == "#comment") {
22671             node.parentNode.removeChild(node);
22672             // clean up silly Windows -- stuff?
22673             return; 
22674         }
22675         var lcname = node.tagName.toLowerCase();
22676         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22677         // whitelist of tags..
22678         
22679         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22680             // remove node.
22681             node.parentNode.removeChild(node);
22682             return;
22683             
22684         }
22685         
22686         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22687         
22688         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22689         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22690         
22691         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22692         //    remove_keep_children = true;
22693         //}
22694         
22695         if (remove_keep_children) {
22696             this.cleanUpChildren(node);
22697             // inserts everything just before this node...
22698             while (node.childNodes.length) {
22699                 var cn = node.childNodes[0];
22700                 node.removeChild(cn);
22701                 node.parentNode.insertBefore(cn, node);
22702             }
22703             node.parentNode.removeChild(node);
22704             return;
22705         }
22706         
22707         if (!node.attributes || !node.attributes.length) {
22708             this.cleanUpChildren(node);
22709             return;
22710         }
22711         
22712         function cleanAttr(n,v)
22713         {
22714             
22715             if (v.match(/^\./) || v.match(/^\//)) {
22716                 return;
22717             }
22718             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22719                 return;
22720             }
22721             if (v.match(/^#/)) {
22722                 return;
22723             }
22724 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22725             node.removeAttribute(n);
22726             
22727         }
22728         
22729         var cwhite = this.cwhite;
22730         var cblack = this.cblack;
22731             
22732         function cleanStyle(n,v)
22733         {
22734             if (v.match(/expression/)) { //XSS?? should we even bother..
22735                 node.removeAttribute(n);
22736                 return;
22737             }
22738             
22739             var parts = v.split(/;/);
22740             var clean = [];
22741             
22742             Roo.each(parts, function(p) {
22743                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22744                 if (!p.length) {
22745                     return true;
22746                 }
22747                 var l = p.split(':').shift().replace(/\s+/g,'');
22748                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22749                 
22750                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22751 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22752                     //node.removeAttribute(n);
22753                     return true;
22754                 }
22755                 //Roo.log()
22756                 // only allow 'c whitelisted system attributes'
22757                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22758 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22759                     //node.removeAttribute(n);
22760                     return true;
22761                 }
22762                 
22763                 
22764                  
22765                 
22766                 clean.push(p);
22767                 return true;
22768             });
22769             if (clean.length) { 
22770                 node.setAttribute(n, clean.join(';'));
22771             } else {
22772                 node.removeAttribute(n);
22773             }
22774             
22775         }
22776         
22777         
22778         for (var i = node.attributes.length-1; i > -1 ; i--) {
22779             var a = node.attributes[i];
22780             //console.log(a);
22781             
22782             if (a.name.toLowerCase().substr(0,2)=='on')  {
22783                 node.removeAttribute(a.name);
22784                 continue;
22785             }
22786             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22787                 node.removeAttribute(a.name);
22788                 continue;
22789             }
22790             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22791                 cleanAttr(a.name,a.value); // fixme..
22792                 continue;
22793             }
22794             if (a.name == 'style') {
22795                 cleanStyle(a.name,a.value);
22796                 continue;
22797             }
22798             /// clean up MS crap..
22799             // tecnically this should be a list of valid class'es..
22800             
22801             
22802             if (a.name == 'class') {
22803                 if (a.value.match(/^Mso/)) {
22804                     node.className = '';
22805                 }
22806                 
22807                 if (a.value.match(/^body$/)) {
22808                     node.className = '';
22809                 }
22810                 continue;
22811             }
22812             
22813             // style cleanup!?
22814             // class cleanup?
22815             
22816         }
22817         
22818         
22819         this.cleanUpChildren(node);
22820         
22821         
22822     },
22823     
22824     /**
22825      * Clean up MS wordisms...
22826      */
22827     cleanWord : function(node)
22828     {
22829         
22830         
22831         if (!node) {
22832             this.cleanWord(this.doc.body);
22833             return;
22834         }
22835         if (node.nodeName == "#text") {
22836             // clean up silly Windows -- stuff?
22837             return; 
22838         }
22839         if (node.nodeName == "#comment") {
22840             node.parentNode.removeChild(node);
22841             // clean up silly Windows -- stuff?
22842             return; 
22843         }
22844         
22845         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22846             node.parentNode.removeChild(node);
22847             return;
22848         }
22849         
22850         // remove - but keep children..
22851         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22852             while (node.childNodes.length) {
22853                 var cn = node.childNodes[0];
22854                 node.removeChild(cn);
22855                 node.parentNode.insertBefore(cn, node);
22856             }
22857             node.parentNode.removeChild(node);
22858             this.iterateChildren(node, this.cleanWord);
22859             return;
22860         }
22861         // clean styles
22862         if (node.className.length) {
22863             
22864             var cn = node.className.split(/\W+/);
22865             var cna = [];
22866             Roo.each(cn, function(cls) {
22867                 if (cls.match(/Mso[a-zA-Z]+/)) {
22868                     return;
22869                 }
22870                 cna.push(cls);
22871             });
22872             node.className = cna.length ? cna.join(' ') : '';
22873             if (!cna.length) {
22874                 node.removeAttribute("class");
22875             }
22876         }
22877         
22878         if (node.hasAttribute("lang")) {
22879             node.removeAttribute("lang");
22880         }
22881         
22882         if (node.hasAttribute("style")) {
22883             
22884             var styles = node.getAttribute("style").split(";");
22885             var nstyle = [];
22886             Roo.each(styles, function(s) {
22887                 if (!s.match(/:/)) {
22888                     return;
22889                 }
22890                 var kv = s.split(":");
22891                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22892                     return;
22893                 }
22894                 // what ever is left... we allow.
22895                 nstyle.push(s);
22896             });
22897             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22898             if (!nstyle.length) {
22899                 node.removeAttribute('style');
22900             }
22901         }
22902         this.iterateChildren(node, this.cleanWord);
22903         
22904         
22905         
22906     },
22907     /**
22908      * iterateChildren of a Node, calling fn each time, using this as the scole..
22909      * @param {DomNode} node node to iterate children of.
22910      * @param {Function} fn method of this class to call on each item.
22911      */
22912     iterateChildren : function(node, fn)
22913     {
22914         if (!node.childNodes.length) {
22915                 return;
22916         }
22917         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22918            fn.call(this, node.childNodes[i])
22919         }
22920     },
22921     
22922     
22923     /**
22924      * cleanTableWidths.
22925      *
22926      * Quite often pasting from word etc.. results in tables with column and widths.
22927      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22928      *
22929      */
22930     cleanTableWidths : function(node)
22931     {
22932          
22933          
22934         if (!node) {
22935             this.cleanTableWidths(this.doc.body);
22936             return;
22937         }
22938         
22939         // ignore list...
22940         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22941             return; 
22942         }
22943         Roo.log(node.tagName);
22944         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22945             this.iterateChildren(node, this.cleanTableWidths);
22946             return;
22947         }
22948         if (node.hasAttribute('width')) {
22949             node.removeAttribute('width');
22950         }
22951         
22952          
22953         if (node.hasAttribute("style")) {
22954             // pretty basic...
22955             
22956             var styles = node.getAttribute("style").split(";");
22957             var nstyle = [];
22958             Roo.each(styles, function(s) {
22959                 if (!s.match(/:/)) {
22960                     return;
22961                 }
22962                 var kv = s.split(":");
22963                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22964                     return;
22965                 }
22966                 // what ever is left... we allow.
22967                 nstyle.push(s);
22968             });
22969             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22970             if (!nstyle.length) {
22971                 node.removeAttribute('style');
22972             }
22973         }
22974         
22975         this.iterateChildren(node, this.cleanTableWidths);
22976         
22977         
22978     },
22979     
22980     
22981     
22982     
22983     domToHTML : function(currentElement, depth, nopadtext) {
22984         
22985         depth = depth || 0;
22986         nopadtext = nopadtext || false;
22987     
22988         if (!currentElement) {
22989             return this.domToHTML(this.doc.body);
22990         }
22991         
22992         //Roo.log(currentElement);
22993         var j;
22994         var allText = false;
22995         var nodeName = currentElement.nodeName;
22996         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22997         
22998         if  (nodeName == '#text') {
22999             
23000             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23001         }
23002         
23003         
23004         var ret = '';
23005         if (nodeName != 'BODY') {
23006              
23007             var i = 0;
23008             // Prints the node tagName, such as <A>, <IMG>, etc
23009             if (tagName) {
23010                 var attr = [];
23011                 for(i = 0; i < currentElement.attributes.length;i++) {
23012                     // quoting?
23013                     var aname = currentElement.attributes.item(i).name;
23014                     if (!currentElement.attributes.item(i).value.length) {
23015                         continue;
23016                     }
23017                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23018                 }
23019                 
23020                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23021             } 
23022             else {
23023                 
23024                 // eack
23025             }
23026         } else {
23027             tagName = false;
23028         }
23029         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23030             return ret;
23031         }
23032         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23033             nopadtext = true;
23034         }
23035         
23036         
23037         // Traverse the tree
23038         i = 0;
23039         var currentElementChild = currentElement.childNodes.item(i);
23040         var allText = true;
23041         var innerHTML  = '';
23042         lastnode = '';
23043         while (currentElementChild) {
23044             // Formatting code (indent the tree so it looks nice on the screen)
23045             var nopad = nopadtext;
23046             if (lastnode == 'SPAN') {
23047                 nopad  = true;
23048             }
23049             // text
23050             if  (currentElementChild.nodeName == '#text') {
23051                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23052                 toadd = nopadtext ? toadd : toadd.trim();
23053                 if (!nopad && toadd.length > 80) {
23054                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23055                 }
23056                 innerHTML  += toadd;
23057                 
23058                 i++;
23059                 currentElementChild = currentElement.childNodes.item(i);
23060                 lastNode = '';
23061                 continue;
23062             }
23063             allText = false;
23064             
23065             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23066                 
23067             // Recursively traverse the tree structure of the child node
23068             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23069             lastnode = currentElementChild.nodeName;
23070             i++;
23071             currentElementChild=currentElement.childNodes.item(i);
23072         }
23073         
23074         ret += innerHTML;
23075         
23076         if (!allText) {
23077                 // The remaining code is mostly for formatting the tree
23078             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23079         }
23080         
23081         
23082         if (tagName) {
23083             ret+= "</"+tagName+">";
23084         }
23085         return ret;
23086         
23087     },
23088         
23089     applyBlacklists : function()
23090     {
23091         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23092         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23093         
23094         this.white = [];
23095         this.black = [];
23096         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23097             if (b.indexOf(tag) > -1) {
23098                 return;
23099             }
23100             this.white.push(tag);
23101             
23102         }, this);
23103         
23104         Roo.each(w, function(tag) {
23105             if (b.indexOf(tag) > -1) {
23106                 return;
23107             }
23108             if (this.white.indexOf(tag) > -1) {
23109                 return;
23110             }
23111             this.white.push(tag);
23112             
23113         }, this);
23114         
23115         
23116         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23117             if (w.indexOf(tag) > -1) {
23118                 return;
23119             }
23120             this.black.push(tag);
23121             
23122         }, this);
23123         
23124         Roo.each(b, function(tag) {
23125             if (w.indexOf(tag) > -1) {
23126                 return;
23127             }
23128             if (this.black.indexOf(tag) > -1) {
23129                 return;
23130             }
23131             this.black.push(tag);
23132             
23133         }, this);
23134         
23135         
23136         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23137         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23138         
23139         this.cwhite = [];
23140         this.cblack = [];
23141         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23142             if (b.indexOf(tag) > -1) {
23143                 return;
23144             }
23145             this.cwhite.push(tag);
23146             
23147         }, this);
23148         
23149         Roo.each(w, function(tag) {
23150             if (b.indexOf(tag) > -1) {
23151                 return;
23152             }
23153             if (this.cwhite.indexOf(tag) > -1) {
23154                 return;
23155             }
23156             this.cwhite.push(tag);
23157             
23158         }, this);
23159         
23160         
23161         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23162             if (w.indexOf(tag) > -1) {
23163                 return;
23164             }
23165             this.cblack.push(tag);
23166             
23167         }, this);
23168         
23169         Roo.each(b, function(tag) {
23170             if (w.indexOf(tag) > -1) {
23171                 return;
23172             }
23173             if (this.cblack.indexOf(tag) > -1) {
23174                 return;
23175             }
23176             this.cblack.push(tag);
23177             
23178         }, this);
23179     },
23180     
23181     setStylesheets : function(stylesheets)
23182     {
23183         if(typeof(stylesheets) == 'string'){
23184             Roo.get(this.iframe.contentDocument.head).createChild({
23185                 tag : 'link',
23186                 rel : 'stylesheet',
23187                 type : 'text/css',
23188                 href : stylesheets
23189             });
23190             
23191             return;
23192         }
23193         var _this = this;
23194      
23195         Roo.each(stylesheets, function(s) {
23196             if(!s.length){
23197                 return;
23198             }
23199             
23200             Roo.get(_this.iframe.contentDocument.head).createChild({
23201                 tag : 'link',
23202                 rel : 'stylesheet',
23203                 type : 'text/css',
23204                 href : s
23205             });
23206         });
23207
23208         
23209     },
23210     
23211     removeStylesheets : function()
23212     {
23213         var _this = this;
23214         
23215         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23216             s.remove();
23217         });
23218     },
23219     
23220     setStyle : function(style)
23221     {
23222         Roo.get(this.iframe.contentDocument.head).createChild({
23223             tag : 'style',
23224             type : 'text/css',
23225             html : style
23226         });
23227
23228         return;
23229     }
23230     
23231     // hide stuff that is not compatible
23232     /**
23233      * @event blur
23234      * @hide
23235      */
23236     /**
23237      * @event change
23238      * @hide
23239      */
23240     /**
23241      * @event focus
23242      * @hide
23243      */
23244     /**
23245      * @event specialkey
23246      * @hide
23247      */
23248     /**
23249      * @cfg {String} fieldClass @hide
23250      */
23251     /**
23252      * @cfg {String} focusClass @hide
23253      */
23254     /**
23255      * @cfg {String} autoCreate @hide
23256      */
23257     /**
23258      * @cfg {String} inputType @hide
23259      */
23260     /**
23261      * @cfg {String} invalidClass @hide
23262      */
23263     /**
23264      * @cfg {String} invalidText @hide
23265      */
23266     /**
23267      * @cfg {String} msgFx @hide
23268      */
23269     /**
23270      * @cfg {String} validateOnBlur @hide
23271      */
23272 });
23273
23274 Roo.HtmlEditorCore.white = [
23275         'area', 'br', 'img', 'input', 'hr', 'wbr',
23276         
23277        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23278        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23279        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23280        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23281        'table',   'ul',         'xmp', 
23282        
23283        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23284       'thead',   'tr', 
23285      
23286       'dir', 'menu', 'ol', 'ul', 'dl',
23287        
23288       'embed',  'object'
23289 ];
23290
23291
23292 Roo.HtmlEditorCore.black = [
23293     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23294         'applet', // 
23295         'base',   'basefont', 'bgsound', 'blink',  'body', 
23296         'frame',  'frameset', 'head',    'html',   'ilayer', 
23297         'iframe', 'layer',  'link',     'meta',    'object',   
23298         'script', 'style' ,'title',  'xml' // clean later..
23299 ];
23300 Roo.HtmlEditorCore.clean = [
23301     'script', 'style', 'title', 'xml'
23302 ];
23303 Roo.HtmlEditorCore.remove = [
23304     'font'
23305 ];
23306 // attributes..
23307
23308 Roo.HtmlEditorCore.ablack = [
23309     'on'
23310 ];
23311     
23312 Roo.HtmlEditorCore.aclean = [ 
23313     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23314 ];
23315
23316 // protocols..
23317 Roo.HtmlEditorCore.pwhite= [
23318         'http',  'https',  'mailto'
23319 ];
23320
23321 // white listed style attributes.
23322 Roo.HtmlEditorCore.cwhite= [
23323       //  'text-align', /// default is to allow most things..
23324       
23325          
23326 //        'font-size'//??
23327 ];
23328
23329 // black listed style attributes.
23330 Roo.HtmlEditorCore.cblack= [
23331       //  'font-size' -- this can be set by the project 
23332 ];
23333
23334
23335 Roo.HtmlEditorCore.swapCodes   =[ 
23336     [    8211, "--" ], 
23337     [    8212, "--" ], 
23338     [    8216,  "'" ],  
23339     [    8217, "'" ],  
23340     [    8220, '"' ],  
23341     [    8221, '"' ],  
23342     [    8226, "*" ],  
23343     [    8230, "..." ]
23344 ]; 
23345
23346     /*
23347  * - LGPL
23348  *
23349  * HtmlEditor
23350  * 
23351  */
23352
23353 /**
23354  * @class Roo.bootstrap.HtmlEditor
23355  * @extends Roo.bootstrap.TextArea
23356  * Bootstrap HtmlEditor class
23357
23358  * @constructor
23359  * Create a new HtmlEditor
23360  * @param {Object} config The config object
23361  */
23362
23363 Roo.bootstrap.HtmlEditor = function(config){
23364     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23365     if (!this.toolbars) {
23366         this.toolbars = [];
23367     }
23368     
23369     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23370     this.addEvents({
23371             /**
23372              * @event initialize
23373              * Fires when the editor is fully initialized (including the iframe)
23374              * @param {HtmlEditor} this
23375              */
23376             initialize: true,
23377             /**
23378              * @event activate
23379              * Fires when the editor is first receives the focus. Any insertion must wait
23380              * until after this event.
23381              * @param {HtmlEditor} this
23382              */
23383             activate: true,
23384              /**
23385              * @event beforesync
23386              * Fires before the textarea is updated with content from the editor iframe. Return false
23387              * to cancel the sync.
23388              * @param {HtmlEditor} this
23389              * @param {String} html
23390              */
23391             beforesync: true,
23392              /**
23393              * @event beforepush
23394              * Fires before the iframe editor is updated with content from the textarea. Return false
23395              * to cancel the push.
23396              * @param {HtmlEditor} this
23397              * @param {String} html
23398              */
23399             beforepush: true,
23400              /**
23401              * @event sync
23402              * Fires when the textarea is updated with content from the editor iframe.
23403              * @param {HtmlEditor} this
23404              * @param {String} html
23405              */
23406             sync: true,
23407              /**
23408              * @event push
23409              * Fires when the iframe editor is updated with content from the textarea.
23410              * @param {HtmlEditor} this
23411              * @param {String} html
23412              */
23413             push: true,
23414              /**
23415              * @event editmodechange
23416              * Fires when the editor switches edit modes
23417              * @param {HtmlEditor} this
23418              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23419              */
23420             editmodechange: true,
23421             /**
23422              * @event editorevent
23423              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23424              * @param {HtmlEditor} this
23425              */
23426             editorevent: true,
23427             /**
23428              * @event firstfocus
23429              * Fires when on first focus - needed by toolbars..
23430              * @param {HtmlEditor} this
23431              */
23432             firstfocus: true,
23433             /**
23434              * @event autosave
23435              * Auto save the htmlEditor value as a file into Events
23436              * @param {HtmlEditor} this
23437              */
23438             autosave: true,
23439             /**
23440              * @event savedpreview
23441              * preview the saved version of htmlEditor
23442              * @param {HtmlEditor} this
23443              */
23444             savedpreview: true
23445         });
23446 };
23447
23448
23449 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23450     
23451     
23452       /**
23453      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23454      */
23455     toolbars : false,
23456     
23457      /**
23458     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23459     */
23460     btns : [],
23461    
23462      /**
23463      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23464      *                        Roo.resizable.
23465      */
23466     resizable : false,
23467      /**
23468      * @cfg {Number} height (in pixels)
23469      */   
23470     height: 300,
23471    /**
23472      * @cfg {Number} width (in pixels)
23473      */   
23474     width: false,
23475     
23476     /**
23477      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23478      * 
23479      */
23480     stylesheets: false,
23481     
23482     // id of frame..
23483     frameId: false,
23484     
23485     // private properties
23486     validationEvent : false,
23487     deferHeight: true,
23488     initialized : false,
23489     activated : false,
23490     
23491     onFocus : Roo.emptyFn,
23492     iframePad:3,
23493     hideMode:'offsets',
23494     
23495     tbContainer : false,
23496     
23497     bodyCls : '',
23498     
23499     toolbarContainer :function() {
23500         return this.wrap.select('.x-html-editor-tb',true).first();
23501     },
23502
23503     /**
23504      * Protected method that will not generally be called directly. It
23505      * is called when the editor creates its toolbar. Override this method if you need to
23506      * add custom toolbar buttons.
23507      * @param {HtmlEditor} editor
23508      */
23509     createToolbar : function(){
23510         Roo.log('renewing');
23511         Roo.log("create toolbars");
23512         
23513         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23514         this.toolbars[0].render(this.toolbarContainer());
23515         
23516         return;
23517         
23518 //        if (!editor.toolbars || !editor.toolbars.length) {
23519 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23520 //        }
23521 //        
23522 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23523 //            editor.toolbars[i] = Roo.factory(
23524 //                    typeof(editor.toolbars[i]) == 'string' ?
23525 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23526 //                Roo.bootstrap.HtmlEditor);
23527 //            editor.toolbars[i].init(editor);
23528 //        }
23529     },
23530
23531      
23532     // private
23533     onRender : function(ct, position)
23534     {
23535        // Roo.log("Call onRender: " + this.xtype);
23536         var _t = this;
23537         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23538       
23539         this.wrap = this.inputEl().wrap({
23540             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23541         });
23542         
23543         this.editorcore.onRender(ct, position);
23544          
23545         if (this.resizable) {
23546             this.resizeEl = new Roo.Resizable(this.wrap, {
23547                 pinned : true,
23548                 wrap: true,
23549                 dynamic : true,
23550                 minHeight : this.height,
23551                 height: this.height,
23552                 handles : this.resizable,
23553                 width: this.width,
23554                 listeners : {
23555                     resize : function(r, w, h) {
23556                         _t.onResize(w,h); // -something
23557                     }
23558                 }
23559             });
23560             
23561         }
23562         this.createToolbar(this);
23563        
23564         
23565         if(!this.width && this.resizable){
23566             this.setSize(this.wrap.getSize());
23567         }
23568         if (this.resizeEl) {
23569             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23570             // should trigger onReize..
23571         }
23572         
23573     },
23574
23575     // private
23576     onResize : function(w, h)
23577     {
23578         Roo.log('resize: ' +w + ',' + h );
23579         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23580         var ew = false;
23581         var eh = false;
23582         
23583         if(this.inputEl() ){
23584             if(typeof w == 'number'){
23585                 var aw = w - this.wrap.getFrameWidth('lr');
23586                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23587                 ew = aw;
23588             }
23589             if(typeof h == 'number'){
23590                  var tbh = -11;  // fixme it needs to tool bar size!
23591                 for (var i =0; i < this.toolbars.length;i++) {
23592                     // fixme - ask toolbars for heights?
23593                     tbh += this.toolbars[i].el.getHeight();
23594                     //if (this.toolbars[i].footer) {
23595                     //    tbh += this.toolbars[i].footer.el.getHeight();
23596                     //}
23597                 }
23598               
23599                 
23600                 
23601                 
23602                 
23603                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23604                 ah -= 5; // knock a few pixes off for look..
23605                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23606                 var eh = ah;
23607             }
23608         }
23609         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23610         this.editorcore.onResize(ew,eh);
23611         
23612     },
23613
23614     /**
23615      * Toggles the editor between standard and source edit mode.
23616      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23617      */
23618     toggleSourceEdit : function(sourceEditMode)
23619     {
23620         this.editorcore.toggleSourceEdit(sourceEditMode);
23621         
23622         if(this.editorcore.sourceEditMode){
23623             Roo.log('editor - showing textarea');
23624             
23625 //            Roo.log('in');
23626 //            Roo.log(this.syncValue());
23627             this.syncValue();
23628             this.inputEl().removeClass(['hide', 'x-hidden']);
23629             this.inputEl().dom.removeAttribute('tabIndex');
23630             this.inputEl().focus();
23631         }else{
23632             Roo.log('editor - hiding textarea');
23633 //            Roo.log('out')
23634 //            Roo.log(this.pushValue()); 
23635             this.pushValue();
23636             
23637             this.inputEl().addClass(['hide', 'x-hidden']);
23638             this.inputEl().dom.setAttribute('tabIndex', -1);
23639             //this.deferFocus();
23640         }
23641          
23642         if(this.resizable){
23643             this.setSize(this.wrap.getSize());
23644         }
23645         
23646         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23647     },
23648  
23649     // private (for BoxComponent)
23650     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23651
23652     // private (for BoxComponent)
23653     getResizeEl : function(){
23654         return this.wrap;
23655     },
23656
23657     // private (for BoxComponent)
23658     getPositionEl : function(){
23659         return this.wrap;
23660     },
23661
23662     // private
23663     initEvents : function(){
23664         this.originalValue = this.getValue();
23665     },
23666
23667 //    /**
23668 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23669 //     * @method
23670 //     */
23671 //    markInvalid : Roo.emptyFn,
23672 //    /**
23673 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23674 //     * @method
23675 //     */
23676 //    clearInvalid : Roo.emptyFn,
23677
23678     setValue : function(v){
23679         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23680         this.editorcore.pushValue();
23681     },
23682
23683      
23684     // private
23685     deferFocus : function(){
23686         this.focus.defer(10, this);
23687     },
23688
23689     // doc'ed in Field
23690     focus : function(){
23691         this.editorcore.focus();
23692         
23693     },
23694       
23695
23696     // private
23697     onDestroy : function(){
23698         
23699         
23700         
23701         if(this.rendered){
23702             
23703             for (var i =0; i < this.toolbars.length;i++) {
23704                 // fixme - ask toolbars for heights?
23705                 this.toolbars[i].onDestroy();
23706             }
23707             
23708             this.wrap.dom.innerHTML = '';
23709             this.wrap.remove();
23710         }
23711     },
23712
23713     // private
23714     onFirstFocus : function(){
23715         //Roo.log("onFirstFocus");
23716         this.editorcore.onFirstFocus();
23717          for (var i =0; i < this.toolbars.length;i++) {
23718             this.toolbars[i].onFirstFocus();
23719         }
23720         
23721     },
23722     
23723     // private
23724     syncValue : function()
23725     {   
23726         this.editorcore.syncValue();
23727     },
23728     
23729     pushValue : function()
23730     {   
23731         this.editorcore.pushValue();
23732     }
23733      
23734     
23735     // hide stuff that is not compatible
23736     /**
23737      * @event blur
23738      * @hide
23739      */
23740     /**
23741      * @event change
23742      * @hide
23743      */
23744     /**
23745      * @event focus
23746      * @hide
23747      */
23748     /**
23749      * @event specialkey
23750      * @hide
23751      */
23752     /**
23753      * @cfg {String} fieldClass @hide
23754      */
23755     /**
23756      * @cfg {String} focusClass @hide
23757      */
23758     /**
23759      * @cfg {String} autoCreate @hide
23760      */
23761     /**
23762      * @cfg {String} inputType @hide
23763      */
23764     /**
23765      * @cfg {String} invalidClass @hide
23766      */
23767     /**
23768      * @cfg {String} invalidText @hide
23769      */
23770     /**
23771      * @cfg {String} msgFx @hide
23772      */
23773     /**
23774      * @cfg {String} validateOnBlur @hide
23775      */
23776 });
23777  
23778     
23779    
23780    
23781    
23782       
23783 Roo.namespace('Roo.bootstrap.htmleditor');
23784 /**
23785  * @class Roo.bootstrap.HtmlEditorToolbar1
23786  * Basic Toolbar
23787  * 
23788  * Usage:
23789  *
23790  new Roo.bootstrap.HtmlEditor({
23791     ....
23792     toolbars : [
23793         new Roo.bootstrap.HtmlEditorToolbar1({
23794             disable : { fonts: 1 , format: 1, ..., ... , ...],
23795             btns : [ .... ]
23796         })
23797     }
23798      
23799  * 
23800  * @cfg {Object} disable List of elements to disable..
23801  * @cfg {Array} btns List of additional buttons.
23802  * 
23803  * 
23804  * NEEDS Extra CSS? 
23805  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23806  */
23807  
23808 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23809 {
23810     
23811     Roo.apply(this, config);
23812     
23813     // default disabled, based on 'good practice'..
23814     this.disable = this.disable || {};
23815     Roo.applyIf(this.disable, {
23816         fontSize : true,
23817         colors : true,
23818         specialElements : true
23819     });
23820     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23821     
23822     this.editor = config.editor;
23823     this.editorcore = config.editor.editorcore;
23824     
23825     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23826     
23827     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23828     // dont call parent... till later.
23829 }
23830 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23831      
23832     bar : true,
23833     
23834     editor : false,
23835     editorcore : false,
23836     
23837     
23838     formats : [
23839         "p" ,  
23840         "h1","h2","h3","h4","h5","h6", 
23841         "pre", "code", 
23842         "abbr", "acronym", "address", "cite", "samp", "var",
23843         'div','span'
23844     ],
23845     
23846     onRender : function(ct, position)
23847     {
23848        // Roo.log("Call onRender: " + this.xtype);
23849         
23850        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23851        Roo.log(this.el);
23852        this.el.dom.style.marginBottom = '0';
23853        var _this = this;
23854        var editorcore = this.editorcore;
23855        var editor= this.editor;
23856        
23857        var children = [];
23858        var btn = function(id,cmd , toggle, handler, html){
23859        
23860             var  event = toggle ? 'toggle' : 'click';
23861        
23862             var a = {
23863                 size : 'sm',
23864                 xtype: 'Button',
23865                 xns: Roo.bootstrap,
23866                 glyphicon : id,
23867                 cmd : id || cmd,
23868                 enableToggle:toggle !== false,
23869                 html : html || '',
23870                 pressed : toggle ? false : null,
23871                 listeners : {}
23872             };
23873             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23874                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23875             };
23876             children.push(a);
23877             return a;
23878        }
23879        
23880     //    var cb_box = function...
23881         
23882         var style = {
23883                 xtype: 'Button',
23884                 size : 'sm',
23885                 xns: Roo.bootstrap,
23886                 glyphicon : 'font',
23887                 //html : 'submit'
23888                 menu : {
23889                     xtype: 'Menu',
23890                     xns: Roo.bootstrap,
23891                     items:  []
23892                 }
23893         };
23894         Roo.each(this.formats, function(f) {
23895             style.menu.items.push({
23896                 xtype :'MenuItem',
23897                 xns: Roo.bootstrap,
23898                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23899                 tagname : f,
23900                 listeners : {
23901                     click : function()
23902                     {
23903                         editorcore.insertTag(this.tagname);
23904                         editor.focus();
23905                     }
23906                 }
23907                 
23908             });
23909         });
23910         children.push(style);   
23911         
23912         btn('bold',false,true);
23913         btn('italic',false,true);
23914         btn('align-left', 'justifyleft',true);
23915         btn('align-center', 'justifycenter',true);
23916         btn('align-right' , 'justifyright',true);
23917         btn('link', false, false, function(btn) {
23918             //Roo.log("create link?");
23919             var url = prompt(this.createLinkText, this.defaultLinkValue);
23920             if(url && url != 'http:/'+'/'){
23921                 this.editorcore.relayCmd('createlink', url);
23922             }
23923         }),
23924         btn('list','insertunorderedlist',true);
23925         btn('pencil', false,true, function(btn){
23926                 Roo.log(this);
23927                 this.toggleSourceEdit(btn.pressed);
23928         });
23929         
23930         if (this.editor.btns.length > 0) {
23931             for (var i = 0; i<this.editor.btns.length; i++) {
23932                 children.push(this.editor.btns[i]);
23933             }
23934         }
23935         
23936         /*
23937         var cog = {
23938                 xtype: 'Button',
23939                 size : 'sm',
23940                 xns: Roo.bootstrap,
23941                 glyphicon : 'cog',
23942                 //html : 'submit'
23943                 menu : {
23944                     xtype: 'Menu',
23945                     xns: Roo.bootstrap,
23946                     items:  []
23947                 }
23948         };
23949         
23950         cog.menu.items.push({
23951             xtype :'MenuItem',
23952             xns: Roo.bootstrap,
23953             html : Clean styles,
23954             tagname : f,
23955             listeners : {
23956                 click : function()
23957                 {
23958                     editorcore.insertTag(this.tagname);
23959                     editor.focus();
23960                 }
23961             }
23962             
23963         });
23964        */
23965         
23966          
23967        this.xtype = 'NavSimplebar';
23968         
23969         for(var i=0;i< children.length;i++) {
23970             
23971             this.buttons.add(this.addxtypeChild(children[i]));
23972             
23973         }
23974         
23975         editor.on('editorevent', this.updateToolbar, this);
23976     },
23977     onBtnClick : function(id)
23978     {
23979        this.editorcore.relayCmd(id);
23980        this.editorcore.focus();
23981     },
23982     
23983     /**
23984      * Protected method that will not generally be called directly. It triggers
23985      * a toolbar update by reading the markup state of the current selection in the editor.
23986      */
23987     updateToolbar: function(){
23988
23989         if(!this.editorcore.activated){
23990             this.editor.onFirstFocus(); // is this neeed?
23991             return;
23992         }
23993
23994         var btns = this.buttons; 
23995         var doc = this.editorcore.doc;
23996         btns.get('bold').setActive(doc.queryCommandState('bold'));
23997         btns.get('italic').setActive(doc.queryCommandState('italic'));
23998         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23999         
24000         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24001         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24002         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24003         
24004         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24005         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24006          /*
24007         
24008         var ans = this.editorcore.getAllAncestors();
24009         if (this.formatCombo) {
24010             
24011             
24012             var store = this.formatCombo.store;
24013             this.formatCombo.setValue("");
24014             for (var i =0; i < ans.length;i++) {
24015                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24016                     // select it..
24017                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24018                     break;
24019                 }
24020             }
24021         }
24022         
24023         
24024         
24025         // hides menus... - so this cant be on a menu...
24026         Roo.bootstrap.MenuMgr.hideAll();
24027         */
24028         Roo.bootstrap.MenuMgr.hideAll();
24029         //this.editorsyncValue();
24030     },
24031     onFirstFocus: function() {
24032         this.buttons.each(function(item){
24033            item.enable();
24034         });
24035     },
24036     toggleSourceEdit : function(sourceEditMode){
24037         
24038           
24039         if(sourceEditMode){
24040             Roo.log("disabling buttons");
24041            this.buttons.each( function(item){
24042                 if(item.cmd != 'pencil'){
24043                     item.disable();
24044                 }
24045             });
24046           
24047         }else{
24048             Roo.log("enabling buttons");
24049             if(this.editorcore.initialized){
24050                 this.buttons.each( function(item){
24051                     item.enable();
24052                 });
24053             }
24054             
24055         }
24056         Roo.log("calling toggole on editor");
24057         // tell the editor that it's been pressed..
24058         this.editor.toggleSourceEdit(sourceEditMode);
24059        
24060     }
24061 });
24062
24063
24064
24065
24066
24067 /**
24068  * @class Roo.bootstrap.Table.AbstractSelectionModel
24069  * @extends Roo.util.Observable
24070  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24071  * implemented by descendant classes.  This class should not be directly instantiated.
24072  * @constructor
24073  */
24074 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24075     this.locked = false;
24076     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24077 };
24078
24079
24080 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24081     /** @ignore Called by the grid automatically. Do not call directly. */
24082     init : function(grid){
24083         this.grid = grid;
24084         this.initEvents();
24085     },
24086
24087     /**
24088      * Locks the selections.
24089      */
24090     lock : function(){
24091         this.locked = true;
24092     },
24093
24094     /**
24095      * Unlocks the selections.
24096      */
24097     unlock : function(){
24098         this.locked = false;
24099     },
24100
24101     /**
24102      * Returns true if the selections are locked.
24103      * @return {Boolean}
24104      */
24105     isLocked : function(){
24106         return this.locked;
24107     }
24108 });
24109 /**
24110  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24111  * @class Roo.bootstrap.Table.RowSelectionModel
24112  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24113  * It supports multiple selections and keyboard selection/navigation. 
24114  * @constructor
24115  * @param {Object} config
24116  */
24117
24118 Roo.bootstrap.Table.RowSelectionModel = function(config){
24119     Roo.apply(this, config);
24120     this.selections = new Roo.util.MixedCollection(false, function(o){
24121         return o.id;
24122     });
24123
24124     this.last = false;
24125     this.lastActive = false;
24126
24127     this.addEvents({
24128         /**
24129              * @event selectionchange
24130              * Fires when the selection changes
24131              * @param {SelectionModel} this
24132              */
24133             "selectionchange" : true,
24134         /**
24135              * @event afterselectionchange
24136              * Fires after the selection changes (eg. by key press or clicking)
24137              * @param {SelectionModel} this
24138              */
24139             "afterselectionchange" : true,
24140         /**
24141              * @event beforerowselect
24142              * Fires when a row is selected being selected, return false to cancel.
24143              * @param {SelectionModel} this
24144              * @param {Number} rowIndex The selected index
24145              * @param {Boolean} keepExisting False if other selections will be cleared
24146              */
24147             "beforerowselect" : true,
24148         /**
24149              * @event rowselect
24150              * Fires when a row is selected.
24151              * @param {SelectionModel} this
24152              * @param {Number} rowIndex The selected index
24153              * @param {Roo.data.Record} r The record
24154              */
24155             "rowselect" : true,
24156         /**
24157              * @event rowdeselect
24158              * Fires when a row is deselected.
24159              * @param {SelectionModel} this
24160              * @param {Number} rowIndex The selected index
24161              */
24162         "rowdeselect" : true
24163     });
24164     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24165     this.locked = false;
24166  };
24167
24168 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24169     /**
24170      * @cfg {Boolean} singleSelect
24171      * True to allow selection of only one row at a time (defaults to false)
24172      */
24173     singleSelect : false,
24174
24175     // private
24176     initEvents : function()
24177     {
24178
24179         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24180         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24181         //}else{ // allow click to work like normal
24182          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24183         //}
24184         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24185         this.grid.on("rowclick", this.handleMouseDown, this);
24186         
24187         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24188             "up" : function(e){
24189                 if(!e.shiftKey){
24190                     this.selectPrevious(e.shiftKey);
24191                 }else if(this.last !== false && this.lastActive !== false){
24192                     var last = this.last;
24193                     this.selectRange(this.last,  this.lastActive-1);
24194                     this.grid.getView().focusRow(this.lastActive);
24195                     if(last !== false){
24196                         this.last = last;
24197                     }
24198                 }else{
24199                     this.selectFirstRow();
24200                 }
24201                 this.fireEvent("afterselectionchange", this);
24202             },
24203             "down" : function(e){
24204                 if(!e.shiftKey){
24205                     this.selectNext(e.shiftKey);
24206                 }else if(this.last !== false && this.lastActive !== false){
24207                     var last = this.last;
24208                     this.selectRange(this.last,  this.lastActive+1);
24209                     this.grid.getView().focusRow(this.lastActive);
24210                     if(last !== false){
24211                         this.last = last;
24212                     }
24213                 }else{
24214                     this.selectFirstRow();
24215                 }
24216                 this.fireEvent("afterselectionchange", this);
24217             },
24218             scope: this
24219         });
24220         this.grid.store.on('load', function(){
24221             this.selections.clear();
24222         },this);
24223         /*
24224         var view = this.grid.view;
24225         view.on("refresh", this.onRefresh, this);
24226         view.on("rowupdated", this.onRowUpdated, this);
24227         view.on("rowremoved", this.onRemove, this);
24228         */
24229     },
24230
24231     // private
24232     onRefresh : function()
24233     {
24234         var ds = this.grid.store, i, v = this.grid.view;
24235         var s = this.selections;
24236         s.each(function(r){
24237             if((i = ds.indexOfId(r.id)) != -1){
24238                 v.onRowSelect(i);
24239             }else{
24240                 s.remove(r);
24241             }
24242         });
24243     },
24244
24245     // private
24246     onRemove : function(v, index, r){
24247         this.selections.remove(r);
24248     },
24249
24250     // private
24251     onRowUpdated : function(v, index, r){
24252         if(this.isSelected(r)){
24253             v.onRowSelect(index);
24254         }
24255     },
24256
24257     /**
24258      * Select records.
24259      * @param {Array} records The records to select
24260      * @param {Boolean} keepExisting (optional) True to keep existing selections
24261      */
24262     selectRecords : function(records, keepExisting)
24263     {
24264         if(!keepExisting){
24265             this.clearSelections();
24266         }
24267             var ds = this.grid.store;
24268         for(var i = 0, len = records.length; i < len; i++){
24269             this.selectRow(ds.indexOf(records[i]), true);
24270         }
24271     },
24272
24273     /**
24274      * Gets the number of selected rows.
24275      * @return {Number}
24276      */
24277     getCount : function(){
24278         return this.selections.length;
24279     },
24280
24281     /**
24282      * Selects the first row in the grid.
24283      */
24284     selectFirstRow : function(){
24285         this.selectRow(0);
24286     },
24287
24288     /**
24289      * Select the last row.
24290      * @param {Boolean} keepExisting (optional) True to keep existing selections
24291      */
24292     selectLastRow : function(keepExisting){
24293         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24294         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24295     },
24296
24297     /**
24298      * Selects the row immediately following the last selected row.
24299      * @param {Boolean} keepExisting (optional) True to keep existing selections
24300      */
24301     selectNext : function(keepExisting)
24302     {
24303             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24304             this.selectRow(this.last+1, keepExisting);
24305             this.grid.getView().focusRow(this.last);
24306         }
24307     },
24308
24309     /**
24310      * Selects the row that precedes the last selected row.
24311      * @param {Boolean} keepExisting (optional) True to keep existing selections
24312      */
24313     selectPrevious : function(keepExisting){
24314         if(this.last){
24315             this.selectRow(this.last-1, keepExisting);
24316             this.grid.getView().focusRow(this.last);
24317         }
24318     },
24319
24320     /**
24321      * Returns the selected records
24322      * @return {Array} Array of selected records
24323      */
24324     getSelections : function(){
24325         return [].concat(this.selections.items);
24326     },
24327
24328     /**
24329      * Returns the first selected record.
24330      * @return {Record}
24331      */
24332     getSelected : function(){
24333         return this.selections.itemAt(0);
24334     },
24335
24336
24337     /**
24338      * Clears all selections.
24339      */
24340     clearSelections : function(fast)
24341     {
24342         if(this.locked) {
24343             return;
24344         }
24345         if(fast !== true){
24346                 var ds = this.grid.store;
24347             var s = this.selections;
24348             s.each(function(r){
24349                 this.deselectRow(ds.indexOfId(r.id));
24350             }, this);
24351             s.clear();
24352         }else{
24353             this.selections.clear();
24354         }
24355         this.last = false;
24356     },
24357
24358
24359     /**
24360      * Selects all rows.
24361      */
24362     selectAll : function(){
24363         if(this.locked) {
24364             return;
24365         }
24366         this.selections.clear();
24367         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24368             this.selectRow(i, true);
24369         }
24370     },
24371
24372     /**
24373      * Returns True if there is a selection.
24374      * @return {Boolean}
24375      */
24376     hasSelection : function(){
24377         return this.selections.length > 0;
24378     },
24379
24380     /**
24381      * Returns True if the specified row is selected.
24382      * @param {Number/Record} record The record or index of the record to check
24383      * @return {Boolean}
24384      */
24385     isSelected : function(index){
24386             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24387         return (r && this.selections.key(r.id) ? true : false);
24388     },
24389
24390     /**
24391      * Returns True if the specified record id is selected.
24392      * @param {String} id The id of record to check
24393      * @return {Boolean}
24394      */
24395     isIdSelected : function(id){
24396         return (this.selections.key(id) ? true : false);
24397     },
24398
24399
24400     // private
24401     handleMouseDBClick : function(e, t){
24402         
24403     },
24404     // private
24405     handleMouseDown : function(e, t)
24406     {
24407             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24408         if(this.isLocked() || rowIndex < 0 ){
24409             return;
24410         };
24411         if(e.shiftKey && this.last !== false){
24412             var last = this.last;
24413             this.selectRange(last, rowIndex, e.ctrlKey);
24414             this.last = last; // reset the last
24415             t.focus();
24416     
24417         }else{
24418             var isSelected = this.isSelected(rowIndex);
24419             //Roo.log("select row:" + rowIndex);
24420             if(isSelected){
24421                 this.deselectRow(rowIndex);
24422             } else {
24423                         this.selectRow(rowIndex, true);
24424             }
24425     
24426             /*
24427                 if(e.button !== 0 && isSelected){
24428                 alert('rowIndex 2: ' + rowIndex);
24429                     view.focusRow(rowIndex);
24430                 }else if(e.ctrlKey && isSelected){
24431                     this.deselectRow(rowIndex);
24432                 }else if(!isSelected){
24433                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24434                     view.focusRow(rowIndex);
24435                 }
24436             */
24437         }
24438         this.fireEvent("afterselectionchange", this);
24439     },
24440     // private
24441     handleDragableRowClick :  function(grid, rowIndex, e) 
24442     {
24443         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24444             this.selectRow(rowIndex, false);
24445             grid.view.focusRow(rowIndex);
24446              this.fireEvent("afterselectionchange", this);
24447         }
24448     },
24449     
24450     /**
24451      * Selects multiple rows.
24452      * @param {Array} rows Array of the indexes of the row to select
24453      * @param {Boolean} keepExisting (optional) True to keep existing selections
24454      */
24455     selectRows : function(rows, keepExisting){
24456         if(!keepExisting){
24457             this.clearSelections();
24458         }
24459         for(var i = 0, len = rows.length; i < len; i++){
24460             this.selectRow(rows[i], true);
24461         }
24462     },
24463
24464     /**
24465      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24466      * @param {Number} startRow The index of the first row in the range
24467      * @param {Number} endRow The index of the last row in the range
24468      * @param {Boolean} keepExisting (optional) True to retain existing selections
24469      */
24470     selectRange : function(startRow, endRow, keepExisting){
24471         if(this.locked) {
24472             return;
24473         }
24474         if(!keepExisting){
24475             this.clearSelections();
24476         }
24477         if(startRow <= endRow){
24478             for(var i = startRow; i <= endRow; i++){
24479                 this.selectRow(i, true);
24480             }
24481         }else{
24482             for(var i = startRow; i >= endRow; i--){
24483                 this.selectRow(i, true);
24484             }
24485         }
24486     },
24487
24488     /**
24489      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24490      * @param {Number} startRow The index of the first row in the range
24491      * @param {Number} endRow The index of the last row in the range
24492      */
24493     deselectRange : function(startRow, endRow, preventViewNotify){
24494         if(this.locked) {
24495             return;
24496         }
24497         for(var i = startRow; i <= endRow; i++){
24498             this.deselectRow(i, preventViewNotify);
24499         }
24500     },
24501
24502     /**
24503      * Selects a row.
24504      * @param {Number} row The index of the row to select
24505      * @param {Boolean} keepExisting (optional) True to keep existing selections
24506      */
24507     selectRow : function(index, keepExisting, preventViewNotify)
24508     {
24509             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24510             return;
24511         }
24512         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24513             if(!keepExisting || this.singleSelect){
24514                 this.clearSelections();
24515             }
24516             
24517             var r = this.grid.store.getAt(index);
24518             //console.log('selectRow - record id :' + r.id);
24519             
24520             this.selections.add(r);
24521             this.last = this.lastActive = index;
24522             if(!preventViewNotify){
24523                 var proxy = new Roo.Element(
24524                                 this.grid.getRowDom(index)
24525                 );
24526                 proxy.addClass('bg-info info');
24527             }
24528             this.fireEvent("rowselect", this, index, r);
24529             this.fireEvent("selectionchange", this);
24530         }
24531     },
24532
24533     /**
24534      * Deselects a row.
24535      * @param {Number} row The index of the row to deselect
24536      */
24537     deselectRow : function(index, preventViewNotify)
24538     {
24539         if(this.locked) {
24540             return;
24541         }
24542         if(this.last == index){
24543             this.last = false;
24544         }
24545         if(this.lastActive == index){
24546             this.lastActive = false;
24547         }
24548         
24549         var r = this.grid.store.getAt(index);
24550         if (!r) {
24551             return;
24552         }
24553         
24554         this.selections.remove(r);
24555         //.console.log('deselectRow - record id :' + r.id);
24556         if(!preventViewNotify){
24557         
24558             var proxy = new Roo.Element(
24559                 this.grid.getRowDom(index)
24560             );
24561             proxy.removeClass('bg-info info');
24562         }
24563         this.fireEvent("rowdeselect", this, index);
24564         this.fireEvent("selectionchange", this);
24565     },
24566
24567     // private
24568     restoreLast : function(){
24569         if(this._last){
24570             this.last = this._last;
24571         }
24572     },
24573
24574     // private
24575     acceptsNav : function(row, col, cm){
24576         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24577     },
24578
24579     // private
24580     onEditorKey : function(field, e){
24581         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24582         if(k == e.TAB){
24583             e.stopEvent();
24584             ed.completeEdit();
24585             if(e.shiftKey){
24586                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24587             }else{
24588                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24589             }
24590         }else if(k == e.ENTER && !e.ctrlKey){
24591             e.stopEvent();
24592             ed.completeEdit();
24593             if(e.shiftKey){
24594                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24595             }else{
24596                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24597             }
24598         }else if(k == e.ESC){
24599             ed.cancelEdit();
24600         }
24601         if(newCell){
24602             g.startEditing(newCell[0], newCell[1]);
24603         }
24604     }
24605 });
24606 /*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616  
24617 /**
24618  * @class Roo.bootstrap.PagingToolbar
24619  * @extends Roo.bootstrap.NavSimplebar
24620  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24621  * @constructor
24622  * Create a new PagingToolbar
24623  * @param {Object} config The config object
24624  * @param {Roo.data.Store} store
24625  */
24626 Roo.bootstrap.PagingToolbar = function(config)
24627 {
24628     // old args format still supported... - xtype is prefered..
24629         // created from xtype...
24630     
24631     this.ds = config.dataSource;
24632     
24633     if (config.store && !this.ds) {
24634         this.store= Roo.factory(config.store, Roo.data);
24635         this.ds = this.store;
24636         this.ds.xmodule = this.xmodule || false;
24637     }
24638     
24639     this.toolbarItems = [];
24640     if (config.items) {
24641         this.toolbarItems = config.items;
24642     }
24643     
24644     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24645     
24646     this.cursor = 0;
24647     
24648     if (this.ds) { 
24649         this.bind(this.ds);
24650     }
24651     
24652     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24653     
24654 };
24655
24656 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24657     /**
24658      * @cfg {Roo.data.Store} dataSource
24659      * The underlying data store providing the paged data
24660      */
24661     /**
24662      * @cfg {String/HTMLElement/Element} container
24663      * container The id or element that will contain the toolbar
24664      */
24665     /**
24666      * @cfg {Boolean} displayInfo
24667      * True to display the displayMsg (defaults to false)
24668      */
24669     /**
24670      * @cfg {Number} pageSize
24671      * The number of records to display per page (defaults to 20)
24672      */
24673     pageSize: 20,
24674     /**
24675      * @cfg {String} displayMsg
24676      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24677      */
24678     displayMsg : 'Displaying {0} - {1} of {2}',
24679     /**
24680      * @cfg {String} emptyMsg
24681      * The message to display when no records are found (defaults to "No data to display")
24682      */
24683     emptyMsg : 'No data to display',
24684     /**
24685      * Customizable piece of the default paging text (defaults to "Page")
24686      * @type String
24687      */
24688     beforePageText : "Page",
24689     /**
24690      * Customizable piece of the default paging text (defaults to "of %0")
24691      * @type String
24692      */
24693     afterPageText : "of {0}",
24694     /**
24695      * Customizable piece of the default paging text (defaults to "First Page")
24696      * @type String
24697      */
24698     firstText : "First Page",
24699     /**
24700      * Customizable piece of the default paging text (defaults to "Previous Page")
24701      * @type String
24702      */
24703     prevText : "Previous Page",
24704     /**
24705      * Customizable piece of the default paging text (defaults to "Next Page")
24706      * @type String
24707      */
24708     nextText : "Next Page",
24709     /**
24710      * Customizable piece of the default paging text (defaults to "Last Page")
24711      * @type String
24712      */
24713     lastText : "Last Page",
24714     /**
24715      * Customizable piece of the default paging text (defaults to "Refresh")
24716      * @type String
24717      */
24718     refreshText : "Refresh",
24719
24720     buttons : false,
24721     // private
24722     onRender : function(ct, position) 
24723     {
24724         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24725         this.navgroup.parentId = this.id;
24726         this.navgroup.onRender(this.el, null);
24727         // add the buttons to the navgroup
24728         
24729         if(this.displayInfo){
24730             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24731             this.displayEl = this.el.select('.x-paging-info', true).first();
24732 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24733 //            this.displayEl = navel.el.select('span',true).first();
24734         }
24735         
24736         var _this = this;
24737         
24738         if(this.buttons){
24739             Roo.each(_this.buttons, function(e){ // this might need to use render????
24740                Roo.factory(e).render(_this.el);
24741             });
24742         }
24743             
24744         Roo.each(_this.toolbarItems, function(e) {
24745             _this.navgroup.addItem(e);
24746         });
24747         
24748         
24749         this.first = this.navgroup.addItem({
24750             tooltip: this.firstText,
24751             cls: "prev",
24752             icon : 'fa fa-step-backward',
24753             disabled: true,
24754             preventDefault: true,
24755             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24756         });
24757         
24758         this.prev =  this.navgroup.addItem({
24759             tooltip: this.prevText,
24760             cls: "prev",
24761             icon : 'fa fa-backward',
24762             disabled: true,
24763             preventDefault: true,
24764             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24765         });
24766     //this.addSeparator();
24767         
24768         
24769         var field = this.navgroup.addItem( {
24770             tagtype : 'span',
24771             cls : 'x-paging-position',
24772             
24773             html : this.beforePageText  +
24774                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24775                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24776          } ); //?? escaped?
24777         
24778         this.field = field.el.select('input', true).first();
24779         this.field.on("keydown", this.onPagingKeydown, this);
24780         this.field.on("focus", function(){this.dom.select();});
24781     
24782     
24783         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24784         //this.field.setHeight(18);
24785         //this.addSeparator();
24786         this.next = this.navgroup.addItem({
24787             tooltip: this.nextText,
24788             cls: "next",
24789             html : ' <i class="fa fa-forward">',
24790             disabled: true,
24791             preventDefault: true,
24792             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24793         });
24794         this.last = this.navgroup.addItem({
24795             tooltip: this.lastText,
24796             icon : 'fa fa-step-forward',
24797             cls: "next",
24798             disabled: true,
24799             preventDefault: true,
24800             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24801         });
24802     //this.addSeparator();
24803         this.loading = this.navgroup.addItem({
24804             tooltip: this.refreshText,
24805             icon: 'fa fa-refresh',
24806             preventDefault: true,
24807             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24808         });
24809         
24810     },
24811
24812     // private
24813     updateInfo : function(){
24814         if(this.displayEl){
24815             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24816             var msg = count == 0 ?
24817                 this.emptyMsg :
24818                 String.format(
24819                     this.displayMsg,
24820                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24821                 );
24822             this.displayEl.update(msg);
24823         }
24824     },
24825
24826     // private
24827     onLoad : function(ds, r, o)
24828     {
24829         this.cursor = o.params.start ? o.params.start : 0;
24830         
24831         var d = this.getPageData(),
24832             ap = d.activePage,
24833             ps = d.pages;
24834         
24835         
24836         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24837         this.field.dom.value = ap;
24838         this.first.setDisabled(ap == 1);
24839         this.prev.setDisabled(ap == 1);
24840         this.next.setDisabled(ap == ps);
24841         this.last.setDisabled(ap == ps);
24842         this.loading.enable();
24843         this.updateInfo();
24844     },
24845
24846     // private
24847     getPageData : function(){
24848         var total = this.ds.getTotalCount();
24849         return {
24850             total : total,
24851             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24852             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24853         };
24854     },
24855
24856     // private
24857     onLoadError : function(){
24858         this.loading.enable();
24859     },
24860
24861     // private
24862     onPagingKeydown : function(e){
24863         var k = e.getKey();
24864         var d = this.getPageData();
24865         if(k == e.RETURN){
24866             var v = this.field.dom.value, pageNum;
24867             if(!v || isNaN(pageNum = parseInt(v, 10))){
24868                 this.field.dom.value = d.activePage;
24869                 return;
24870             }
24871             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24872             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24873             e.stopEvent();
24874         }
24875         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))
24876         {
24877           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24878           this.field.dom.value = pageNum;
24879           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24880           e.stopEvent();
24881         }
24882         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24883         {
24884           var v = this.field.dom.value, pageNum; 
24885           var increment = (e.shiftKey) ? 10 : 1;
24886           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24887                 increment *= -1;
24888           }
24889           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24890             this.field.dom.value = d.activePage;
24891             return;
24892           }
24893           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24894           {
24895             this.field.dom.value = parseInt(v, 10) + increment;
24896             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24897             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24898           }
24899           e.stopEvent();
24900         }
24901     },
24902
24903     // private
24904     beforeLoad : function(){
24905         if(this.loading){
24906             this.loading.disable();
24907         }
24908     },
24909
24910     // private
24911     onClick : function(which){
24912         
24913         var ds = this.ds;
24914         if (!ds) {
24915             return;
24916         }
24917         
24918         switch(which){
24919             case "first":
24920                 ds.load({params:{start: 0, limit: this.pageSize}});
24921             break;
24922             case "prev":
24923                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24924             break;
24925             case "next":
24926                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24927             break;
24928             case "last":
24929                 var total = ds.getTotalCount();
24930                 var extra = total % this.pageSize;
24931                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24932                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24933             break;
24934             case "refresh":
24935                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24936             break;
24937         }
24938     },
24939
24940     /**
24941      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24942      * @param {Roo.data.Store} store The data store to unbind
24943      */
24944     unbind : function(ds){
24945         ds.un("beforeload", this.beforeLoad, this);
24946         ds.un("load", this.onLoad, this);
24947         ds.un("loadexception", this.onLoadError, this);
24948         ds.un("remove", this.updateInfo, this);
24949         ds.un("add", this.updateInfo, this);
24950         this.ds = undefined;
24951     },
24952
24953     /**
24954      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24955      * @param {Roo.data.Store} store The data store to bind
24956      */
24957     bind : function(ds){
24958         ds.on("beforeload", this.beforeLoad, this);
24959         ds.on("load", this.onLoad, this);
24960         ds.on("loadexception", this.onLoadError, this);
24961         ds.on("remove", this.updateInfo, this);
24962         ds.on("add", this.updateInfo, this);
24963         this.ds = ds;
24964     }
24965 });/*
24966  * - LGPL
24967  *
24968  * element
24969  * 
24970  */
24971
24972 /**
24973  * @class Roo.bootstrap.MessageBar
24974  * @extends Roo.bootstrap.Component
24975  * Bootstrap MessageBar class
24976  * @cfg {String} html contents of the MessageBar
24977  * @cfg {String} weight (info | success | warning | danger) default info
24978  * @cfg {String} beforeClass insert the bar before the given class
24979  * @cfg {Boolean} closable (true | false) default false
24980  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24981  * 
24982  * @constructor
24983  * Create a new Element
24984  * @param {Object} config The config object
24985  */
24986
24987 Roo.bootstrap.MessageBar = function(config){
24988     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24989 };
24990
24991 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24992     
24993     html: '',
24994     weight: 'info',
24995     closable: false,
24996     fixed: false,
24997     beforeClass: 'bootstrap-sticky-wrap',
24998     
24999     getAutoCreate : function(){
25000         
25001         var cfg = {
25002             tag: 'div',
25003             cls: 'alert alert-dismissable alert-' + this.weight,
25004             cn: [
25005                 {
25006                     tag: 'span',
25007                     cls: 'message',
25008                     html: this.html || ''
25009                 }
25010             ]
25011         };
25012         
25013         if(this.fixed){
25014             cfg.cls += ' alert-messages-fixed';
25015         }
25016         
25017         if(this.closable){
25018             cfg.cn.push({
25019                 tag: 'button',
25020                 cls: 'close',
25021                 html: 'x'
25022             });
25023         }
25024         
25025         return cfg;
25026     },
25027     
25028     onRender : function(ct, position)
25029     {
25030         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25031         
25032         if(!this.el){
25033             var cfg = Roo.apply({},  this.getAutoCreate());
25034             cfg.id = Roo.id();
25035             
25036             if (this.cls) {
25037                 cfg.cls += ' ' + this.cls;
25038             }
25039             if (this.style) {
25040                 cfg.style = this.style;
25041             }
25042             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25043             
25044             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25045         }
25046         
25047         this.el.select('>button.close').on('click', this.hide, this);
25048         
25049     },
25050     
25051     show : function()
25052     {
25053         if (!this.rendered) {
25054             this.render();
25055         }
25056         
25057         this.el.show();
25058         
25059         this.fireEvent('show', this);
25060         
25061     },
25062     
25063     hide : function()
25064     {
25065         if (!this.rendered) {
25066             this.render();
25067         }
25068         
25069         this.el.hide();
25070         
25071         this.fireEvent('hide', this);
25072     },
25073     
25074     update : function()
25075     {
25076 //        var e = this.el.dom.firstChild;
25077 //        
25078 //        if(this.closable){
25079 //            e = e.nextSibling;
25080 //        }
25081 //        
25082 //        e.data = this.html || '';
25083
25084         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25085     }
25086    
25087 });
25088
25089  
25090
25091      /*
25092  * - LGPL
25093  *
25094  * Graph
25095  * 
25096  */
25097
25098
25099 /**
25100  * @class Roo.bootstrap.Graph
25101  * @extends Roo.bootstrap.Component
25102  * Bootstrap Graph class
25103 > Prameters
25104  -sm {number} sm 4
25105  -md {number} md 5
25106  @cfg {String} graphtype  bar | vbar | pie
25107  @cfg {number} g_x coodinator | centre x (pie)
25108  @cfg {number} g_y coodinator | centre y (pie)
25109  @cfg {number} g_r radius (pie)
25110  @cfg {number} g_height height of the chart (respected by all elements in the set)
25111  @cfg {number} g_width width of the chart (respected by all elements in the set)
25112  @cfg {Object} title The title of the chart
25113     
25114  -{Array}  values
25115  -opts (object) options for the chart 
25116      o {
25117      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25118      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25119      o vgutter (number)
25120      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.
25121      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25122      o to
25123      o stretch (boolean)
25124      o }
25125  -opts (object) options for the pie
25126      o{
25127      o cut
25128      o startAngle (number)
25129      o endAngle (number)
25130      } 
25131  *
25132  * @constructor
25133  * Create a new Input
25134  * @param {Object} config The config object
25135  */
25136
25137 Roo.bootstrap.Graph = function(config){
25138     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25139     
25140     this.addEvents({
25141         // img events
25142         /**
25143          * @event click
25144          * The img click event for the img.
25145          * @param {Roo.EventObject} e
25146          */
25147         "click" : true
25148     });
25149 };
25150
25151 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25152     
25153     sm: 4,
25154     md: 5,
25155     graphtype: 'bar',
25156     g_height: 250,
25157     g_width: 400,
25158     g_x: 50,
25159     g_y: 50,
25160     g_r: 30,
25161     opts:{
25162         //g_colors: this.colors,
25163         g_type: 'soft',
25164         g_gutter: '20%'
25165
25166     },
25167     title : false,
25168
25169     getAutoCreate : function(){
25170         
25171         var cfg = {
25172             tag: 'div',
25173             html : null
25174         };
25175         
25176         
25177         return  cfg;
25178     },
25179
25180     onRender : function(ct,position){
25181         
25182         
25183         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25184         
25185         if (typeof(Raphael) == 'undefined') {
25186             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25187             return;
25188         }
25189         
25190         this.raphael = Raphael(this.el.dom);
25191         
25192                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25193                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25194                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25195                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25196                 /*
25197                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25198                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25199                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25200                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25201                 
25202                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25203                 r.barchart(330, 10, 300, 220, data1);
25204                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25205                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25206                 */
25207                 
25208                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25209                 // r.barchart(30, 30, 560, 250,  xdata, {
25210                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25211                 //     axis : "0 0 1 1",
25212                 //     axisxlabels :  xdata
25213                 //     //yvalues : cols,
25214                    
25215                 // });
25216 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25217 //        
25218 //        this.load(null,xdata,{
25219 //                axis : "0 0 1 1",
25220 //                axisxlabels :  xdata
25221 //                });
25222
25223     },
25224
25225     load : function(graphtype,xdata,opts)
25226     {
25227         this.raphael.clear();
25228         if(!graphtype) {
25229             graphtype = this.graphtype;
25230         }
25231         if(!opts){
25232             opts = this.opts;
25233         }
25234         var r = this.raphael,
25235             fin = function () {
25236                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25237             },
25238             fout = function () {
25239                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25240             },
25241             pfin = function() {
25242                 this.sector.stop();
25243                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25244
25245                 if (this.label) {
25246                     this.label[0].stop();
25247                     this.label[0].attr({ r: 7.5 });
25248                     this.label[1].attr({ "font-weight": 800 });
25249                 }
25250             },
25251             pfout = function() {
25252                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25253
25254                 if (this.label) {
25255                     this.label[0].animate({ r: 5 }, 500, "bounce");
25256                     this.label[1].attr({ "font-weight": 400 });
25257                 }
25258             };
25259
25260         switch(graphtype){
25261             case 'bar':
25262                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25263                 break;
25264             case 'hbar':
25265                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25266                 break;
25267             case 'pie':
25268 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25269 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25270 //            
25271                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25272                 
25273                 break;
25274
25275         }
25276         
25277         if(this.title){
25278             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25279         }
25280         
25281     },
25282     
25283     setTitle: function(o)
25284     {
25285         this.title = o;
25286     },
25287     
25288     initEvents: function() {
25289         
25290         if(!this.href){
25291             this.el.on('click', this.onClick, this);
25292         }
25293     },
25294     
25295     onClick : function(e)
25296     {
25297         Roo.log('img onclick');
25298         this.fireEvent('click', this, e);
25299     }
25300    
25301 });
25302
25303  
25304 /*
25305  * - LGPL
25306  *
25307  * numberBox
25308  * 
25309  */
25310 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25311
25312 /**
25313  * @class Roo.bootstrap.dash.NumberBox
25314  * @extends Roo.bootstrap.Component
25315  * Bootstrap NumberBox class
25316  * @cfg {String} headline Box headline
25317  * @cfg {String} content Box content
25318  * @cfg {String} icon Box icon
25319  * @cfg {String} footer Footer text
25320  * @cfg {String} fhref Footer href
25321  * 
25322  * @constructor
25323  * Create a new NumberBox
25324  * @param {Object} config The config object
25325  */
25326
25327
25328 Roo.bootstrap.dash.NumberBox = function(config){
25329     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25330     
25331 };
25332
25333 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25334     
25335     headline : '',
25336     content : '',
25337     icon : '',
25338     footer : '',
25339     fhref : '',
25340     ficon : '',
25341     
25342     getAutoCreate : function(){
25343         
25344         var cfg = {
25345             tag : 'div',
25346             cls : 'small-box ',
25347             cn : [
25348                 {
25349                     tag : 'div',
25350                     cls : 'inner',
25351                     cn :[
25352                         {
25353                             tag : 'h3',
25354                             cls : 'roo-headline',
25355                             html : this.headline
25356                         },
25357                         {
25358                             tag : 'p',
25359                             cls : 'roo-content',
25360                             html : this.content
25361                         }
25362                     ]
25363                 }
25364             ]
25365         };
25366         
25367         if(this.icon){
25368             cfg.cn.push({
25369                 tag : 'div',
25370                 cls : 'icon',
25371                 cn :[
25372                     {
25373                         tag : 'i',
25374                         cls : 'ion ' + this.icon
25375                     }
25376                 ]
25377             });
25378         }
25379         
25380         if(this.footer){
25381             var footer = {
25382                 tag : 'a',
25383                 cls : 'small-box-footer',
25384                 href : this.fhref || '#',
25385                 html : this.footer
25386             };
25387             
25388             cfg.cn.push(footer);
25389             
25390         }
25391         
25392         return  cfg;
25393     },
25394
25395     onRender : function(ct,position){
25396         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25397
25398
25399        
25400                 
25401     },
25402
25403     setHeadline: function (value)
25404     {
25405         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25406     },
25407     
25408     setFooter: function (value, href)
25409     {
25410         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25411         
25412         if(href){
25413             this.el.select('a.small-box-footer',true).first().attr('href', href);
25414         }
25415         
25416     },
25417
25418     setContent: function (value)
25419     {
25420         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25421     },
25422
25423     initEvents: function() 
25424     {   
25425         
25426     }
25427     
25428 });
25429
25430  
25431 /*
25432  * - LGPL
25433  *
25434  * TabBox
25435  * 
25436  */
25437 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25438
25439 /**
25440  * @class Roo.bootstrap.dash.TabBox
25441  * @extends Roo.bootstrap.Component
25442  * Bootstrap TabBox class
25443  * @cfg {String} title Title of the TabBox
25444  * @cfg {String} icon Icon of the TabBox
25445  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25446  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25447  * 
25448  * @constructor
25449  * Create a new TabBox
25450  * @param {Object} config The config object
25451  */
25452
25453
25454 Roo.bootstrap.dash.TabBox = function(config){
25455     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25456     this.addEvents({
25457         // raw events
25458         /**
25459          * @event addpane
25460          * When a pane is added
25461          * @param {Roo.bootstrap.dash.TabPane} pane
25462          */
25463         "addpane" : true,
25464         /**
25465          * @event activatepane
25466          * When a pane is activated
25467          * @param {Roo.bootstrap.dash.TabPane} pane
25468          */
25469         "activatepane" : true
25470         
25471          
25472     });
25473     
25474     this.panes = [];
25475 };
25476
25477 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25478
25479     title : '',
25480     icon : false,
25481     showtabs : true,
25482     tabScrollable : false,
25483     
25484     getChildContainer : function()
25485     {
25486         return this.el.select('.tab-content', true).first();
25487     },
25488     
25489     getAutoCreate : function(){
25490         
25491         var header = {
25492             tag: 'li',
25493             cls: 'pull-left header',
25494             html: this.title,
25495             cn : []
25496         };
25497         
25498         if(this.icon){
25499             header.cn.push({
25500                 tag: 'i',
25501                 cls: 'fa ' + this.icon
25502             });
25503         }
25504         
25505         var h = {
25506             tag: 'ul',
25507             cls: 'nav nav-tabs pull-right',
25508             cn: [
25509                 header
25510             ]
25511         };
25512         
25513         if(this.tabScrollable){
25514             h = {
25515                 tag: 'div',
25516                 cls: 'tab-header',
25517                 cn: [
25518                     {
25519                         tag: 'ul',
25520                         cls: 'nav nav-tabs pull-right',
25521                         cn: [
25522                             header
25523                         ]
25524                     }
25525                 ]
25526             };
25527         }
25528         
25529         var cfg = {
25530             tag: 'div',
25531             cls: 'nav-tabs-custom',
25532             cn: [
25533                 h,
25534                 {
25535                     tag: 'div',
25536                     cls: 'tab-content no-padding',
25537                     cn: []
25538                 }
25539             ]
25540         };
25541
25542         return  cfg;
25543     },
25544     initEvents : function()
25545     {
25546         //Roo.log('add add pane handler');
25547         this.on('addpane', this.onAddPane, this);
25548     },
25549      /**
25550      * Updates the box title
25551      * @param {String} html to set the title to.
25552      */
25553     setTitle : function(value)
25554     {
25555         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25556     },
25557     onAddPane : function(pane)
25558     {
25559         this.panes.push(pane);
25560         //Roo.log('addpane');
25561         //Roo.log(pane);
25562         // tabs are rendere left to right..
25563         if(!this.showtabs){
25564             return;
25565         }
25566         
25567         var ctr = this.el.select('.nav-tabs', true).first();
25568          
25569          
25570         var existing = ctr.select('.nav-tab',true);
25571         var qty = existing.getCount();;
25572         
25573         
25574         var tab = ctr.createChild({
25575             tag : 'li',
25576             cls : 'nav-tab' + (qty ? '' : ' active'),
25577             cn : [
25578                 {
25579                     tag : 'a',
25580                     href:'#',
25581                     html : pane.title
25582                 }
25583             ]
25584         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25585         pane.tab = tab;
25586         
25587         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25588         if (!qty) {
25589             pane.el.addClass('active');
25590         }
25591         
25592                 
25593     },
25594     onTabClick : function(ev,un,ob,pane)
25595     {
25596         //Roo.log('tab - prev default');
25597         ev.preventDefault();
25598         
25599         
25600         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25601         pane.tab.addClass('active');
25602         //Roo.log(pane.title);
25603         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25604         // technically we should have a deactivate event.. but maybe add later.
25605         // and it should not de-activate the selected tab...
25606         this.fireEvent('activatepane', pane);
25607         pane.el.addClass('active');
25608         pane.fireEvent('activate');
25609         
25610         
25611     },
25612     
25613     getActivePane : function()
25614     {
25615         var r = false;
25616         Roo.each(this.panes, function(p) {
25617             if(p.el.hasClass('active')){
25618                 r = p;
25619                 return false;
25620             }
25621             
25622             return;
25623         });
25624         
25625         return r;
25626     }
25627     
25628     
25629 });
25630
25631  
25632 /*
25633  * - LGPL
25634  *
25635  * Tab pane
25636  * 
25637  */
25638 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25639 /**
25640  * @class Roo.bootstrap.TabPane
25641  * @extends Roo.bootstrap.Component
25642  * Bootstrap TabPane class
25643  * @cfg {Boolean} active (false | true) Default false
25644  * @cfg {String} title title of panel
25645
25646  * 
25647  * @constructor
25648  * Create a new TabPane
25649  * @param {Object} config The config object
25650  */
25651
25652 Roo.bootstrap.dash.TabPane = function(config){
25653     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25654     
25655     this.addEvents({
25656         // raw events
25657         /**
25658          * @event activate
25659          * When a pane is activated
25660          * @param {Roo.bootstrap.dash.TabPane} pane
25661          */
25662         "activate" : true
25663          
25664     });
25665 };
25666
25667 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25668     
25669     active : false,
25670     title : '',
25671     
25672     // the tabBox that this is attached to.
25673     tab : false,
25674      
25675     getAutoCreate : function() 
25676     {
25677         var cfg = {
25678             tag: 'div',
25679             cls: 'tab-pane'
25680         };
25681         
25682         if(this.active){
25683             cfg.cls += ' active';
25684         }
25685         
25686         return cfg;
25687     },
25688     initEvents  : function()
25689     {
25690         //Roo.log('trigger add pane handler');
25691         this.parent().fireEvent('addpane', this)
25692     },
25693     
25694      /**
25695      * Updates the tab title 
25696      * @param {String} html to set the title to.
25697      */
25698     setTitle: function(str)
25699     {
25700         if (!this.tab) {
25701             return;
25702         }
25703         this.title = str;
25704         this.tab.select('a', true).first().dom.innerHTML = str;
25705         
25706     }
25707     
25708     
25709     
25710 });
25711
25712  
25713
25714
25715  /*
25716  * - LGPL
25717  *
25718  * menu
25719  * 
25720  */
25721 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25722
25723 /**
25724  * @class Roo.bootstrap.menu.Menu
25725  * @extends Roo.bootstrap.Component
25726  * Bootstrap Menu class - container for Menu
25727  * @cfg {String} html Text of the menu
25728  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25729  * @cfg {String} icon Font awesome icon
25730  * @cfg {String} pos Menu align to (top | bottom) default bottom
25731  * 
25732  * 
25733  * @constructor
25734  * Create a new Menu
25735  * @param {Object} config The config object
25736  */
25737
25738
25739 Roo.bootstrap.menu.Menu = function(config){
25740     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25741     
25742     this.addEvents({
25743         /**
25744          * @event beforeshow
25745          * Fires before this menu is displayed
25746          * @param {Roo.bootstrap.menu.Menu} this
25747          */
25748         beforeshow : true,
25749         /**
25750          * @event beforehide
25751          * Fires before this menu is hidden
25752          * @param {Roo.bootstrap.menu.Menu} this
25753          */
25754         beforehide : true,
25755         /**
25756          * @event show
25757          * Fires after this menu is displayed
25758          * @param {Roo.bootstrap.menu.Menu} this
25759          */
25760         show : true,
25761         /**
25762          * @event hide
25763          * Fires after this menu is hidden
25764          * @param {Roo.bootstrap.menu.Menu} this
25765          */
25766         hide : true,
25767         /**
25768          * @event click
25769          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25770          * @param {Roo.bootstrap.menu.Menu} this
25771          * @param {Roo.EventObject} e
25772          */
25773         click : true
25774     });
25775     
25776 };
25777
25778 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25779     
25780     submenu : false,
25781     html : '',
25782     weight : 'default',
25783     icon : false,
25784     pos : 'bottom',
25785     
25786     
25787     getChildContainer : function() {
25788         if(this.isSubMenu){
25789             return this.el;
25790         }
25791         
25792         return this.el.select('ul.dropdown-menu', true).first();  
25793     },
25794     
25795     getAutoCreate : function()
25796     {
25797         var text = [
25798             {
25799                 tag : 'span',
25800                 cls : 'roo-menu-text',
25801                 html : this.html
25802             }
25803         ];
25804         
25805         if(this.icon){
25806             text.unshift({
25807                 tag : 'i',
25808                 cls : 'fa ' + this.icon
25809             })
25810         }
25811         
25812         
25813         var cfg = {
25814             tag : 'div',
25815             cls : 'btn-group',
25816             cn : [
25817                 {
25818                     tag : 'button',
25819                     cls : 'dropdown-button btn btn-' + this.weight,
25820                     cn : text
25821                 },
25822                 {
25823                     tag : 'button',
25824                     cls : 'dropdown-toggle btn btn-' + this.weight,
25825                     cn : [
25826                         {
25827                             tag : 'span',
25828                             cls : 'caret'
25829                         }
25830                     ]
25831                 },
25832                 {
25833                     tag : 'ul',
25834                     cls : 'dropdown-menu'
25835                 }
25836             ]
25837             
25838         };
25839         
25840         if(this.pos == 'top'){
25841             cfg.cls += ' dropup';
25842         }
25843         
25844         if(this.isSubMenu){
25845             cfg = {
25846                 tag : 'ul',
25847                 cls : 'dropdown-menu'
25848             }
25849         }
25850         
25851         return cfg;
25852     },
25853     
25854     onRender : function(ct, position)
25855     {
25856         this.isSubMenu = ct.hasClass('dropdown-submenu');
25857         
25858         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25859     },
25860     
25861     initEvents : function() 
25862     {
25863         if(this.isSubMenu){
25864             return;
25865         }
25866         
25867         this.hidden = true;
25868         
25869         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25870         this.triggerEl.on('click', this.onTriggerPress, this);
25871         
25872         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25873         this.buttonEl.on('click', this.onClick, this);
25874         
25875     },
25876     
25877     list : function()
25878     {
25879         if(this.isSubMenu){
25880             return this.el;
25881         }
25882         
25883         return this.el.select('ul.dropdown-menu', true).first();
25884     },
25885     
25886     onClick : function(e)
25887     {
25888         this.fireEvent("click", this, e);
25889     },
25890     
25891     onTriggerPress  : function(e)
25892     {   
25893         if (this.isVisible()) {
25894             this.hide();
25895         } else {
25896             this.show();
25897         }
25898     },
25899     
25900     isVisible : function(){
25901         return !this.hidden;
25902     },
25903     
25904     show : function()
25905     {
25906         this.fireEvent("beforeshow", this);
25907         
25908         this.hidden = false;
25909         this.el.addClass('open');
25910         
25911         Roo.get(document).on("mouseup", this.onMouseUp, this);
25912         
25913         this.fireEvent("show", this);
25914         
25915         
25916     },
25917     
25918     hide : function()
25919     {
25920         this.fireEvent("beforehide", this);
25921         
25922         this.hidden = true;
25923         this.el.removeClass('open');
25924         
25925         Roo.get(document).un("mouseup", this.onMouseUp);
25926         
25927         this.fireEvent("hide", this);
25928     },
25929     
25930     onMouseUp : function()
25931     {
25932         this.hide();
25933     }
25934     
25935 });
25936
25937  
25938  /*
25939  * - LGPL
25940  *
25941  * menu item
25942  * 
25943  */
25944 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25945
25946 /**
25947  * @class Roo.bootstrap.menu.Item
25948  * @extends Roo.bootstrap.Component
25949  * Bootstrap MenuItem class
25950  * @cfg {Boolean} submenu (true | false) default false
25951  * @cfg {String} html text of the item
25952  * @cfg {String} href the link
25953  * @cfg {Boolean} disable (true | false) default false
25954  * @cfg {Boolean} preventDefault (true | false) default true
25955  * @cfg {String} icon Font awesome icon
25956  * @cfg {String} pos Submenu align to (left | right) default right 
25957  * 
25958  * 
25959  * @constructor
25960  * Create a new Item
25961  * @param {Object} config The config object
25962  */
25963
25964
25965 Roo.bootstrap.menu.Item = function(config){
25966     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25967     this.addEvents({
25968         /**
25969          * @event mouseover
25970          * Fires when the mouse is hovering over this menu
25971          * @param {Roo.bootstrap.menu.Item} this
25972          * @param {Roo.EventObject} e
25973          */
25974         mouseover : true,
25975         /**
25976          * @event mouseout
25977          * Fires when the mouse exits this menu
25978          * @param {Roo.bootstrap.menu.Item} this
25979          * @param {Roo.EventObject} e
25980          */
25981         mouseout : true,
25982         // raw events
25983         /**
25984          * @event click
25985          * The raw click event for the entire grid.
25986          * @param {Roo.EventObject} e
25987          */
25988         click : true
25989     });
25990 };
25991
25992 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25993     
25994     submenu : false,
25995     href : '',
25996     html : '',
25997     preventDefault: true,
25998     disable : false,
25999     icon : false,
26000     pos : 'right',
26001     
26002     getAutoCreate : function()
26003     {
26004         var text = [
26005             {
26006                 tag : 'span',
26007                 cls : 'roo-menu-item-text',
26008                 html : this.html
26009             }
26010         ];
26011         
26012         if(this.icon){
26013             text.unshift({
26014                 tag : 'i',
26015                 cls : 'fa ' + this.icon
26016             })
26017         }
26018         
26019         var cfg = {
26020             tag : 'li',
26021             cn : [
26022                 {
26023                     tag : 'a',
26024                     href : this.href || '#',
26025                     cn : text
26026                 }
26027             ]
26028         };
26029         
26030         if(this.disable){
26031             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26032         }
26033         
26034         if(this.submenu){
26035             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26036             
26037             if(this.pos == 'left'){
26038                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26039             }
26040         }
26041         
26042         return cfg;
26043     },
26044     
26045     initEvents : function() 
26046     {
26047         this.el.on('mouseover', this.onMouseOver, this);
26048         this.el.on('mouseout', this.onMouseOut, this);
26049         
26050         this.el.select('a', true).first().on('click', this.onClick, this);
26051         
26052     },
26053     
26054     onClick : function(e)
26055     {
26056         if(this.preventDefault){
26057             e.preventDefault();
26058         }
26059         
26060         this.fireEvent("click", this, e);
26061     },
26062     
26063     onMouseOver : function(e)
26064     {
26065         if(this.submenu && this.pos == 'left'){
26066             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26067         }
26068         
26069         this.fireEvent("mouseover", this, e);
26070     },
26071     
26072     onMouseOut : function(e)
26073     {
26074         this.fireEvent("mouseout", this, e);
26075     }
26076 });
26077
26078  
26079
26080  /*
26081  * - LGPL
26082  *
26083  * menu separator
26084  * 
26085  */
26086 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26087
26088 /**
26089  * @class Roo.bootstrap.menu.Separator
26090  * @extends Roo.bootstrap.Component
26091  * Bootstrap Separator class
26092  * 
26093  * @constructor
26094  * Create a new Separator
26095  * @param {Object} config The config object
26096  */
26097
26098
26099 Roo.bootstrap.menu.Separator = function(config){
26100     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26101 };
26102
26103 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26104     
26105     getAutoCreate : function(){
26106         var cfg = {
26107             tag : 'li',
26108             cls: 'divider'
26109         };
26110         
26111         return cfg;
26112     }
26113    
26114 });
26115
26116  
26117
26118  /*
26119  * - LGPL
26120  *
26121  * Tooltip
26122  * 
26123  */
26124
26125 /**
26126  * @class Roo.bootstrap.Tooltip
26127  * Bootstrap Tooltip class
26128  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26129  * to determine which dom element triggers the tooltip.
26130  * 
26131  * It needs to add support for additional attributes like tooltip-position
26132  * 
26133  * @constructor
26134  * Create a new Toolti
26135  * @param {Object} config The config object
26136  */
26137
26138 Roo.bootstrap.Tooltip = function(config){
26139     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26140     
26141     this.alignment = Roo.bootstrap.Tooltip.alignment;
26142     
26143     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26144         this.alignment = config.alignment;
26145     }
26146     
26147 };
26148
26149 Roo.apply(Roo.bootstrap.Tooltip, {
26150     /**
26151      * @function init initialize tooltip monitoring.
26152      * @static
26153      */
26154     currentEl : false,
26155     currentTip : false,
26156     currentRegion : false,
26157     
26158     //  init : delay?
26159     
26160     init : function()
26161     {
26162         Roo.get(document).on('mouseover', this.enter ,this);
26163         Roo.get(document).on('mouseout', this.leave, this);
26164          
26165         
26166         this.currentTip = new Roo.bootstrap.Tooltip();
26167     },
26168     
26169     enter : function(ev)
26170     {
26171         var dom = ev.getTarget();
26172         
26173         //Roo.log(['enter',dom]);
26174         var el = Roo.fly(dom);
26175         if (this.currentEl) {
26176             //Roo.log(dom);
26177             //Roo.log(this.currentEl);
26178             //Roo.log(this.currentEl.contains(dom));
26179             if (this.currentEl == el) {
26180                 return;
26181             }
26182             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26183                 return;
26184             }
26185
26186         }
26187         
26188         if (this.currentTip.el) {
26189             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26190         }    
26191         //Roo.log(ev);
26192         
26193         if(!el || el.dom == document){
26194             return;
26195         }
26196         
26197         var bindEl = el;
26198         
26199         // you can not look for children, as if el is the body.. then everythign is the child..
26200         if (!el.attr('tooltip')) { //
26201             if (!el.select("[tooltip]").elements.length) {
26202                 return;
26203             }
26204             // is the mouse over this child...?
26205             bindEl = el.select("[tooltip]").first();
26206             var xy = ev.getXY();
26207             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26208                 //Roo.log("not in region.");
26209                 return;
26210             }
26211             //Roo.log("child element over..");
26212             
26213         }
26214         this.currentEl = bindEl;
26215         this.currentTip.bind(bindEl);
26216         this.currentRegion = Roo.lib.Region.getRegion(dom);
26217         this.currentTip.enter();
26218         
26219     },
26220     leave : function(ev)
26221     {
26222         var dom = ev.getTarget();
26223         //Roo.log(['leave',dom]);
26224         if (!this.currentEl) {
26225             return;
26226         }
26227         
26228         
26229         if (dom != this.currentEl.dom) {
26230             return;
26231         }
26232         var xy = ev.getXY();
26233         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26234             return;
26235         }
26236         // only activate leave if mouse cursor is outside... bounding box..
26237         
26238         
26239         
26240         
26241         if (this.currentTip) {
26242             this.currentTip.leave();
26243         }
26244         //Roo.log('clear currentEl');
26245         this.currentEl = false;
26246         
26247         
26248     },
26249     alignment : {
26250         'left' : ['r-l', [-2,0], 'right'],
26251         'right' : ['l-r', [2,0], 'left'],
26252         'bottom' : ['t-b', [0,2], 'top'],
26253         'top' : [ 'b-t', [0,-2], 'bottom']
26254     }
26255     
26256 });
26257
26258
26259 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26260     
26261     
26262     bindEl : false,
26263     
26264     delay : null, // can be { show : 300 , hide: 500}
26265     
26266     timeout : null,
26267     
26268     hoverState : null, //???
26269     
26270     placement : 'bottom', 
26271     
26272     alignment : false,
26273     
26274     getAutoCreate : function(){
26275     
26276         var cfg = {
26277            cls : 'tooltip',
26278            role : 'tooltip',
26279            cn : [
26280                 {
26281                     cls : 'tooltip-arrow'
26282                 },
26283                 {
26284                     cls : 'tooltip-inner'
26285                 }
26286            ]
26287         };
26288         
26289         return cfg;
26290     },
26291     bind : function(el)
26292     {
26293         this.bindEl = el;
26294     },
26295       
26296     
26297     enter : function () {
26298        
26299         if (this.timeout != null) {
26300             clearTimeout(this.timeout);
26301         }
26302         
26303         this.hoverState = 'in';
26304          //Roo.log("enter - show");
26305         if (!this.delay || !this.delay.show) {
26306             this.show();
26307             return;
26308         }
26309         var _t = this;
26310         this.timeout = setTimeout(function () {
26311             if (_t.hoverState == 'in') {
26312                 _t.show();
26313             }
26314         }, this.delay.show);
26315     },
26316     leave : function()
26317     {
26318         clearTimeout(this.timeout);
26319     
26320         this.hoverState = 'out';
26321          if (!this.delay || !this.delay.hide) {
26322             this.hide();
26323             return;
26324         }
26325        
26326         var _t = this;
26327         this.timeout = setTimeout(function () {
26328             //Roo.log("leave - timeout");
26329             
26330             if (_t.hoverState == 'out') {
26331                 _t.hide();
26332                 Roo.bootstrap.Tooltip.currentEl = false;
26333             }
26334         }, delay);
26335     },
26336     
26337     show : function (msg)
26338     {
26339         if (!this.el) {
26340             this.render(document.body);
26341         }
26342         // set content.
26343         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26344         
26345         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26346         
26347         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26348         
26349         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26350         
26351         var placement = typeof this.placement == 'function' ?
26352             this.placement.call(this, this.el, on_el) :
26353             this.placement;
26354             
26355         var autoToken = /\s?auto?\s?/i;
26356         var autoPlace = autoToken.test(placement);
26357         if (autoPlace) {
26358             placement = placement.replace(autoToken, '') || 'top';
26359         }
26360         
26361         //this.el.detach()
26362         //this.el.setXY([0,0]);
26363         this.el.show();
26364         //this.el.dom.style.display='block';
26365         
26366         //this.el.appendTo(on_el);
26367         
26368         var p = this.getPosition();
26369         var box = this.el.getBox();
26370         
26371         if (autoPlace) {
26372             // fixme..
26373         }
26374         
26375         var align = this.alignment[placement];
26376         
26377         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26378         
26379         if(placement == 'top' || placement == 'bottom'){
26380             if(xy[0] < 0){
26381                 placement = 'right';
26382             }
26383             
26384             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26385                 placement = 'left';
26386             }
26387             
26388             var scroll = Roo.select('body', true).first().getScroll();
26389             
26390             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26391                 placement = 'top';
26392             }
26393             
26394             align = this.alignment[placement];
26395         }
26396         
26397         this.el.alignTo(this.bindEl, align[0],align[1]);
26398         //var arrow = this.el.select('.arrow',true).first();
26399         //arrow.set(align[2], 
26400         
26401         this.el.addClass(placement);
26402         
26403         this.el.addClass('in fade');
26404         
26405         this.hoverState = null;
26406         
26407         if (this.el.hasClass('fade')) {
26408             // fade it?
26409         }
26410         
26411     },
26412     hide : function()
26413     {
26414          
26415         if (!this.el) {
26416             return;
26417         }
26418         //this.el.setXY([0,0]);
26419         this.el.removeClass('in');
26420         //this.el.hide();
26421         
26422     }
26423     
26424 });
26425  
26426
26427  /*
26428  * - LGPL
26429  *
26430  * Location Picker
26431  * 
26432  */
26433
26434 /**
26435  * @class Roo.bootstrap.LocationPicker
26436  * @extends Roo.bootstrap.Component
26437  * Bootstrap LocationPicker class
26438  * @cfg {Number} latitude Position when init default 0
26439  * @cfg {Number} longitude Position when init default 0
26440  * @cfg {Number} zoom default 15
26441  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26442  * @cfg {Boolean} mapTypeControl default false
26443  * @cfg {Boolean} disableDoubleClickZoom default false
26444  * @cfg {Boolean} scrollwheel default true
26445  * @cfg {Boolean} streetViewControl default false
26446  * @cfg {Number} radius default 0
26447  * @cfg {String} locationName
26448  * @cfg {Boolean} draggable default true
26449  * @cfg {Boolean} enableAutocomplete default false
26450  * @cfg {Boolean} enableReverseGeocode default true
26451  * @cfg {String} markerTitle
26452  * 
26453  * @constructor
26454  * Create a new LocationPicker
26455  * @param {Object} config The config object
26456  */
26457
26458
26459 Roo.bootstrap.LocationPicker = function(config){
26460     
26461     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26462     
26463     this.addEvents({
26464         /**
26465          * @event initial
26466          * Fires when the picker initialized.
26467          * @param {Roo.bootstrap.LocationPicker} this
26468          * @param {Google Location} location
26469          */
26470         initial : true,
26471         /**
26472          * @event positionchanged
26473          * Fires when the picker position changed.
26474          * @param {Roo.bootstrap.LocationPicker} this
26475          * @param {Google Location} location
26476          */
26477         positionchanged : true,
26478         /**
26479          * @event resize
26480          * Fires when the map resize.
26481          * @param {Roo.bootstrap.LocationPicker} this
26482          */
26483         resize : true,
26484         /**
26485          * @event show
26486          * Fires when the map show.
26487          * @param {Roo.bootstrap.LocationPicker} this
26488          */
26489         show : true,
26490         /**
26491          * @event hide
26492          * Fires when the map hide.
26493          * @param {Roo.bootstrap.LocationPicker} this
26494          */
26495         hide : true,
26496         /**
26497          * @event mapClick
26498          * Fires when click the map.
26499          * @param {Roo.bootstrap.LocationPicker} this
26500          * @param {Map event} e
26501          */
26502         mapClick : true,
26503         /**
26504          * @event mapRightClick
26505          * Fires when right click the map.
26506          * @param {Roo.bootstrap.LocationPicker} this
26507          * @param {Map event} e
26508          */
26509         mapRightClick : true,
26510         /**
26511          * @event markerClick
26512          * Fires when click the marker.
26513          * @param {Roo.bootstrap.LocationPicker} this
26514          * @param {Map event} e
26515          */
26516         markerClick : true,
26517         /**
26518          * @event markerRightClick
26519          * Fires when right click the marker.
26520          * @param {Roo.bootstrap.LocationPicker} this
26521          * @param {Map event} e
26522          */
26523         markerRightClick : true,
26524         /**
26525          * @event OverlayViewDraw
26526          * Fires when OverlayView Draw
26527          * @param {Roo.bootstrap.LocationPicker} this
26528          */
26529         OverlayViewDraw : true,
26530         /**
26531          * @event OverlayViewOnAdd
26532          * Fires when OverlayView Draw
26533          * @param {Roo.bootstrap.LocationPicker} this
26534          */
26535         OverlayViewOnAdd : true,
26536         /**
26537          * @event OverlayViewOnRemove
26538          * Fires when OverlayView Draw
26539          * @param {Roo.bootstrap.LocationPicker} this
26540          */
26541         OverlayViewOnRemove : true,
26542         /**
26543          * @event OverlayViewShow
26544          * Fires when OverlayView Draw
26545          * @param {Roo.bootstrap.LocationPicker} this
26546          * @param {Pixel} cpx
26547          */
26548         OverlayViewShow : true,
26549         /**
26550          * @event OverlayViewHide
26551          * Fires when OverlayView Draw
26552          * @param {Roo.bootstrap.LocationPicker} this
26553          */
26554         OverlayViewHide : true,
26555         /**
26556          * @event loadexception
26557          * Fires when load google lib failed.
26558          * @param {Roo.bootstrap.LocationPicker} this
26559          */
26560         loadexception : true
26561     });
26562         
26563 };
26564
26565 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26566     
26567     gMapContext: false,
26568     
26569     latitude: 0,
26570     longitude: 0,
26571     zoom: 15,
26572     mapTypeId: false,
26573     mapTypeControl: false,
26574     disableDoubleClickZoom: false,
26575     scrollwheel: true,
26576     streetViewControl: false,
26577     radius: 0,
26578     locationName: '',
26579     draggable: true,
26580     enableAutocomplete: false,
26581     enableReverseGeocode: true,
26582     markerTitle: '',
26583     
26584     getAutoCreate: function()
26585     {
26586
26587         var cfg = {
26588             tag: 'div',
26589             cls: 'roo-location-picker'
26590         };
26591         
26592         return cfg
26593     },
26594     
26595     initEvents: function(ct, position)
26596     {       
26597         if(!this.el.getWidth() || this.isApplied()){
26598             return;
26599         }
26600         
26601         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26602         
26603         this.initial();
26604     },
26605     
26606     initial: function()
26607     {
26608         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26609             this.fireEvent('loadexception', this);
26610             return;
26611         }
26612         
26613         if(!this.mapTypeId){
26614             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26615         }
26616         
26617         this.gMapContext = this.GMapContext();
26618         
26619         this.initOverlayView();
26620         
26621         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26622         
26623         var _this = this;
26624                 
26625         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26626             _this.setPosition(_this.gMapContext.marker.position);
26627         });
26628         
26629         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26630             _this.fireEvent('mapClick', this, event);
26631             
26632         });
26633
26634         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26635             _this.fireEvent('mapRightClick', this, event);
26636             
26637         });
26638         
26639         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26640             _this.fireEvent('markerClick', this, event);
26641             
26642         });
26643
26644         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26645             _this.fireEvent('markerRightClick', this, event);
26646             
26647         });
26648         
26649         this.setPosition(this.gMapContext.location);
26650         
26651         this.fireEvent('initial', this, this.gMapContext.location);
26652     },
26653     
26654     initOverlayView: function()
26655     {
26656         var _this = this;
26657         
26658         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26659             
26660             draw: function()
26661             {
26662                 _this.fireEvent('OverlayViewDraw', _this);
26663             },
26664             
26665             onAdd: function()
26666             {
26667                 _this.fireEvent('OverlayViewOnAdd', _this);
26668             },
26669             
26670             onRemove: function()
26671             {
26672                 _this.fireEvent('OverlayViewOnRemove', _this);
26673             },
26674             
26675             show: function(cpx)
26676             {
26677                 _this.fireEvent('OverlayViewShow', _this, cpx);
26678             },
26679             
26680             hide: function()
26681             {
26682                 _this.fireEvent('OverlayViewHide', _this);
26683             }
26684             
26685         });
26686     },
26687     
26688     fromLatLngToContainerPixel: function(event)
26689     {
26690         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26691     },
26692     
26693     isApplied: function() 
26694     {
26695         return this.getGmapContext() == false ? false : true;
26696     },
26697     
26698     getGmapContext: function() 
26699     {
26700         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26701     },
26702     
26703     GMapContext: function() 
26704     {
26705         var position = new google.maps.LatLng(this.latitude, this.longitude);
26706         
26707         var _map = new google.maps.Map(this.el.dom, {
26708             center: position,
26709             zoom: this.zoom,
26710             mapTypeId: this.mapTypeId,
26711             mapTypeControl: this.mapTypeControl,
26712             disableDoubleClickZoom: this.disableDoubleClickZoom,
26713             scrollwheel: this.scrollwheel,
26714             streetViewControl: this.streetViewControl,
26715             locationName: this.locationName,
26716             draggable: this.draggable,
26717             enableAutocomplete: this.enableAutocomplete,
26718             enableReverseGeocode: this.enableReverseGeocode
26719         });
26720         
26721         var _marker = new google.maps.Marker({
26722             position: position,
26723             map: _map,
26724             title: this.markerTitle,
26725             draggable: this.draggable
26726         });
26727         
26728         return {
26729             map: _map,
26730             marker: _marker,
26731             circle: null,
26732             location: position,
26733             radius: this.radius,
26734             locationName: this.locationName,
26735             addressComponents: {
26736                 formatted_address: null,
26737                 addressLine1: null,
26738                 addressLine2: null,
26739                 streetName: null,
26740                 streetNumber: null,
26741                 city: null,
26742                 district: null,
26743                 state: null,
26744                 stateOrProvince: null
26745             },
26746             settings: this,
26747             domContainer: this.el.dom,
26748             geodecoder: new google.maps.Geocoder()
26749         };
26750     },
26751     
26752     drawCircle: function(center, radius, options) 
26753     {
26754         if (this.gMapContext.circle != null) {
26755             this.gMapContext.circle.setMap(null);
26756         }
26757         if (radius > 0) {
26758             radius *= 1;
26759             options = Roo.apply({}, options, {
26760                 strokeColor: "#0000FF",
26761                 strokeOpacity: .35,
26762                 strokeWeight: 2,
26763                 fillColor: "#0000FF",
26764                 fillOpacity: .2
26765             });
26766             
26767             options.map = this.gMapContext.map;
26768             options.radius = radius;
26769             options.center = center;
26770             this.gMapContext.circle = new google.maps.Circle(options);
26771             return this.gMapContext.circle;
26772         }
26773         
26774         return null;
26775     },
26776     
26777     setPosition: function(location) 
26778     {
26779         this.gMapContext.location = location;
26780         this.gMapContext.marker.setPosition(location);
26781         this.gMapContext.map.panTo(location);
26782         this.drawCircle(location, this.gMapContext.radius, {});
26783         
26784         var _this = this;
26785         
26786         if (this.gMapContext.settings.enableReverseGeocode) {
26787             this.gMapContext.geodecoder.geocode({
26788                 latLng: this.gMapContext.location
26789             }, function(results, status) {
26790                 
26791                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26792                     _this.gMapContext.locationName = results[0].formatted_address;
26793                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26794                     
26795                     _this.fireEvent('positionchanged', this, location);
26796                 }
26797             });
26798             
26799             return;
26800         }
26801         
26802         this.fireEvent('positionchanged', this, location);
26803     },
26804     
26805     resize: function()
26806     {
26807         google.maps.event.trigger(this.gMapContext.map, "resize");
26808         
26809         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26810         
26811         this.fireEvent('resize', this);
26812     },
26813     
26814     setPositionByLatLng: function(latitude, longitude)
26815     {
26816         this.setPosition(new google.maps.LatLng(latitude, longitude));
26817     },
26818     
26819     getCurrentPosition: function() 
26820     {
26821         return {
26822             latitude: this.gMapContext.location.lat(),
26823             longitude: this.gMapContext.location.lng()
26824         };
26825     },
26826     
26827     getAddressName: function() 
26828     {
26829         return this.gMapContext.locationName;
26830     },
26831     
26832     getAddressComponents: function() 
26833     {
26834         return this.gMapContext.addressComponents;
26835     },
26836     
26837     address_component_from_google_geocode: function(address_components) 
26838     {
26839         var result = {};
26840         
26841         for (var i = 0; i < address_components.length; i++) {
26842             var component = address_components[i];
26843             if (component.types.indexOf("postal_code") >= 0) {
26844                 result.postalCode = component.short_name;
26845             } else if (component.types.indexOf("street_number") >= 0) {
26846                 result.streetNumber = component.short_name;
26847             } else if (component.types.indexOf("route") >= 0) {
26848                 result.streetName = component.short_name;
26849             } else if (component.types.indexOf("neighborhood") >= 0) {
26850                 result.city = component.short_name;
26851             } else if (component.types.indexOf("locality") >= 0) {
26852                 result.city = component.short_name;
26853             } else if (component.types.indexOf("sublocality") >= 0) {
26854                 result.district = component.short_name;
26855             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26856                 result.stateOrProvince = component.short_name;
26857             } else if (component.types.indexOf("country") >= 0) {
26858                 result.country = component.short_name;
26859             }
26860         }
26861         
26862         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26863         result.addressLine2 = "";
26864         return result;
26865     },
26866     
26867     setZoomLevel: function(zoom)
26868     {
26869         this.gMapContext.map.setZoom(zoom);
26870     },
26871     
26872     show: function()
26873     {
26874         if(!this.el){
26875             return;
26876         }
26877         
26878         this.el.show();
26879         
26880         this.resize();
26881         
26882         this.fireEvent('show', this);
26883     },
26884     
26885     hide: function()
26886     {
26887         if(!this.el){
26888             return;
26889         }
26890         
26891         this.el.hide();
26892         
26893         this.fireEvent('hide', this);
26894     }
26895     
26896 });
26897
26898 Roo.apply(Roo.bootstrap.LocationPicker, {
26899     
26900     OverlayView : function(map, options)
26901     {
26902         options = options || {};
26903         
26904         this.setMap(map);
26905     }
26906     
26907     
26908 });/*
26909  * - LGPL
26910  *
26911  * Alert
26912  * 
26913  */
26914
26915 /**
26916  * @class Roo.bootstrap.Alert
26917  * @extends Roo.bootstrap.Component
26918  * Bootstrap Alert class
26919  * @cfg {String} title The title of alert
26920  * @cfg {String} html The content of alert
26921  * @cfg {String} weight (  success | info | warning | danger )
26922  * @cfg {String} faicon font-awesomeicon
26923  * 
26924  * @constructor
26925  * Create a new alert
26926  * @param {Object} config The config object
26927  */
26928
26929
26930 Roo.bootstrap.Alert = function(config){
26931     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26932     
26933 };
26934
26935 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26936     
26937     title: '',
26938     html: '',
26939     weight: false,
26940     faicon: false,
26941     
26942     getAutoCreate : function()
26943     {
26944         
26945         var cfg = {
26946             tag : 'div',
26947             cls : 'alert',
26948             cn : [
26949                 {
26950                     tag : 'i',
26951                     cls : 'roo-alert-icon'
26952                     
26953                 },
26954                 {
26955                     tag : 'b',
26956                     cls : 'roo-alert-title',
26957                     html : this.title
26958                 },
26959                 {
26960                     tag : 'span',
26961                     cls : 'roo-alert-text',
26962                     html : this.html
26963                 }
26964             ]
26965         };
26966         
26967         if(this.faicon){
26968             cfg.cn[0].cls += ' fa ' + this.faicon;
26969         }
26970         
26971         if(this.weight){
26972             cfg.cls += ' alert-' + this.weight;
26973         }
26974         
26975         return cfg;
26976     },
26977     
26978     initEvents: function() 
26979     {
26980         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26981     },
26982     
26983     setTitle : function(str)
26984     {
26985         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26986     },
26987     
26988     setText : function(str)
26989     {
26990         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26991     },
26992     
26993     setWeight : function(weight)
26994     {
26995         if(this.weight){
26996             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26997         }
26998         
26999         this.weight = weight;
27000         
27001         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27002     },
27003     
27004     setIcon : function(icon)
27005     {
27006         if(this.faicon){
27007             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27008         }
27009         
27010         this.faicon = icon;
27011         
27012         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27013     },
27014     
27015     hide: function() 
27016     {
27017         this.el.hide();   
27018     },
27019     
27020     show: function() 
27021     {  
27022         this.el.show();   
27023     }
27024     
27025 });
27026
27027  
27028 /*
27029 * Licence: LGPL
27030 */
27031
27032 /**
27033  * @class Roo.bootstrap.UploadCropbox
27034  * @extends Roo.bootstrap.Component
27035  * Bootstrap UploadCropbox class
27036  * @cfg {String} emptyText show when image has been loaded
27037  * @cfg {String} rotateNotify show when image too small to rotate
27038  * @cfg {Number} errorTimeout default 3000
27039  * @cfg {Number} minWidth default 300
27040  * @cfg {Number} minHeight default 300
27041  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27042  * @cfg {Boolean} isDocument (true|false) default false
27043  * @cfg {String} url action url
27044  * @cfg {String} paramName default 'imageUpload'
27045  * @cfg {String} method default POST
27046  * @cfg {Boolean} loadMask (true|false) default true
27047  * @cfg {Boolean} loadingText default 'Loading...'
27048  * 
27049  * @constructor
27050  * Create a new UploadCropbox
27051  * @param {Object} config The config object
27052  */
27053
27054 Roo.bootstrap.UploadCropbox = function(config){
27055     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27056     
27057     this.addEvents({
27058         /**
27059          * @event beforeselectfile
27060          * Fire before select file
27061          * @param {Roo.bootstrap.UploadCropbox} this
27062          */
27063         "beforeselectfile" : true,
27064         /**
27065          * @event initial
27066          * Fire after initEvent
27067          * @param {Roo.bootstrap.UploadCropbox} this
27068          */
27069         "initial" : true,
27070         /**
27071          * @event crop
27072          * Fire after initEvent
27073          * @param {Roo.bootstrap.UploadCropbox} this
27074          * @param {String} data
27075          */
27076         "crop" : true,
27077         /**
27078          * @event prepare
27079          * Fire when preparing the file data
27080          * @param {Roo.bootstrap.UploadCropbox} this
27081          * @param {Object} file
27082          */
27083         "prepare" : true,
27084         /**
27085          * @event exception
27086          * Fire when get exception
27087          * @param {Roo.bootstrap.UploadCropbox} this
27088          * @param {XMLHttpRequest} xhr
27089          */
27090         "exception" : true,
27091         /**
27092          * @event beforeloadcanvas
27093          * Fire before load the canvas
27094          * @param {Roo.bootstrap.UploadCropbox} this
27095          * @param {String} src
27096          */
27097         "beforeloadcanvas" : true,
27098         /**
27099          * @event trash
27100          * Fire when trash image
27101          * @param {Roo.bootstrap.UploadCropbox} this
27102          */
27103         "trash" : true,
27104         /**
27105          * @event download
27106          * Fire when download the image
27107          * @param {Roo.bootstrap.UploadCropbox} this
27108          */
27109         "download" : true,
27110         /**
27111          * @event footerbuttonclick
27112          * Fire when footerbuttonclick
27113          * @param {Roo.bootstrap.UploadCropbox} this
27114          * @param {String} type
27115          */
27116         "footerbuttonclick" : true,
27117         /**
27118          * @event resize
27119          * Fire when resize
27120          * @param {Roo.bootstrap.UploadCropbox} this
27121          */
27122         "resize" : true,
27123         /**
27124          * @event rotate
27125          * Fire when rotate the image
27126          * @param {Roo.bootstrap.UploadCropbox} this
27127          * @param {String} pos
27128          */
27129         "rotate" : true,
27130         /**
27131          * @event inspect
27132          * Fire when inspect the file
27133          * @param {Roo.bootstrap.UploadCropbox} this
27134          * @param {Object} file
27135          */
27136         "inspect" : true,
27137         /**
27138          * @event upload
27139          * Fire when xhr upload the file
27140          * @param {Roo.bootstrap.UploadCropbox} this
27141          * @param {Object} data
27142          */
27143         "upload" : true,
27144         /**
27145          * @event arrange
27146          * Fire when arrange the file data
27147          * @param {Roo.bootstrap.UploadCropbox} this
27148          * @param {Object} formData
27149          */
27150         "arrange" : true
27151     });
27152     
27153     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27154 };
27155
27156 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27157     
27158     emptyText : 'Click to upload image',
27159     rotateNotify : 'Image is too small to rotate',
27160     errorTimeout : 3000,
27161     scale : 0,
27162     baseScale : 1,
27163     rotate : 0,
27164     dragable : false,
27165     pinching : false,
27166     mouseX : 0,
27167     mouseY : 0,
27168     cropData : false,
27169     minWidth : 300,
27170     minHeight : 300,
27171     file : false,
27172     exif : {},
27173     baseRotate : 1,
27174     cropType : 'image/jpeg',
27175     buttons : false,
27176     canvasLoaded : false,
27177     isDocument : false,
27178     method : 'POST',
27179     paramName : 'imageUpload',
27180     loadMask : true,
27181     loadingText : 'Loading...',
27182     maskEl : false,
27183     
27184     getAutoCreate : function()
27185     {
27186         var cfg = {
27187             tag : 'div',
27188             cls : 'roo-upload-cropbox',
27189             cn : [
27190                 {
27191                     tag : 'input',
27192                     cls : 'roo-upload-cropbox-selector',
27193                     type : 'file'
27194                 },
27195                 {
27196                     tag : 'div',
27197                     cls : 'roo-upload-cropbox-body',
27198                     style : 'cursor:pointer',
27199                     cn : [
27200                         {
27201                             tag : 'div',
27202                             cls : 'roo-upload-cropbox-preview'
27203                         },
27204                         {
27205                             tag : 'div',
27206                             cls : 'roo-upload-cropbox-thumb'
27207                         },
27208                         {
27209                             tag : 'div',
27210                             cls : 'roo-upload-cropbox-empty-notify',
27211                             html : this.emptyText
27212                         },
27213                         {
27214                             tag : 'div',
27215                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27216                             html : this.rotateNotify
27217                         }
27218                     ]
27219                 },
27220                 {
27221                     tag : 'div',
27222                     cls : 'roo-upload-cropbox-footer',
27223                     cn : {
27224                         tag : 'div',
27225                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27226                         cn : []
27227                     }
27228                 }
27229             ]
27230         };
27231         
27232         return cfg;
27233     },
27234     
27235     onRender : function(ct, position)
27236     {
27237         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27238         
27239         if (this.buttons.length) {
27240             
27241             Roo.each(this.buttons, function(bb) {
27242                 
27243                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27244                 
27245                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27246                 
27247             }, this);
27248         }
27249         
27250         if(this.loadMask){
27251             this.maskEl = this.el;
27252         }
27253     },
27254     
27255     initEvents : function()
27256     {
27257         this.urlAPI = (window.createObjectURL && window) || 
27258                                 (window.URL && URL.revokeObjectURL && URL) || 
27259                                 (window.webkitURL && webkitURL);
27260                         
27261         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27262         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27263         
27264         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27265         this.selectorEl.hide();
27266         
27267         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27268         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27269         
27270         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27271         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27272         this.thumbEl.hide();
27273         
27274         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27275         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27276         
27277         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27278         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27279         this.errorEl.hide();
27280         
27281         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27282         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27283         this.footerEl.hide();
27284         
27285         this.setThumbBoxSize();
27286         
27287         this.bind();
27288         
27289         this.resize();
27290         
27291         this.fireEvent('initial', this);
27292     },
27293
27294     bind : function()
27295     {
27296         var _this = this;
27297         
27298         window.addEventListener("resize", function() { _this.resize(); } );
27299         
27300         this.bodyEl.on('click', this.beforeSelectFile, this);
27301         
27302         if(Roo.isTouch){
27303             this.bodyEl.on('touchstart', this.onTouchStart, this);
27304             this.bodyEl.on('touchmove', this.onTouchMove, this);
27305             this.bodyEl.on('touchend', this.onTouchEnd, this);
27306         }
27307         
27308         if(!Roo.isTouch){
27309             this.bodyEl.on('mousedown', this.onMouseDown, this);
27310             this.bodyEl.on('mousemove', this.onMouseMove, this);
27311             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27312             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27313             Roo.get(document).on('mouseup', this.onMouseUp, this);
27314         }
27315         
27316         this.selectorEl.on('change', this.onFileSelected, this);
27317     },
27318     
27319     reset : function()
27320     {    
27321         this.scale = 0;
27322         this.baseScale = 1;
27323         this.rotate = 0;
27324         this.baseRotate = 1;
27325         this.dragable = false;
27326         this.pinching = false;
27327         this.mouseX = 0;
27328         this.mouseY = 0;
27329         this.cropData = false;
27330         this.notifyEl.dom.innerHTML = this.emptyText;
27331         
27332         this.selectorEl.dom.value = '';
27333         
27334     },
27335     
27336     resize : function()
27337     {
27338         if(this.fireEvent('resize', this) != false){
27339             this.setThumbBoxPosition();
27340             this.setCanvasPosition();
27341         }
27342     },
27343     
27344     onFooterButtonClick : function(e, el, o, type)
27345     {
27346         switch (type) {
27347             case 'rotate-left' :
27348                 this.onRotateLeft(e);
27349                 break;
27350             case 'rotate-right' :
27351                 this.onRotateRight(e);
27352                 break;
27353             case 'picture' :
27354                 this.beforeSelectFile(e);
27355                 break;
27356             case 'trash' :
27357                 this.trash(e);
27358                 break;
27359             case 'crop' :
27360                 this.crop(e);
27361                 break;
27362             case 'download' :
27363                 this.download(e);
27364                 break;
27365             default :
27366                 break;
27367         }
27368         
27369         this.fireEvent('footerbuttonclick', this, type);
27370     },
27371     
27372     beforeSelectFile : function(e)
27373     {
27374         e.preventDefault();
27375         
27376         if(this.fireEvent('beforeselectfile', this) != false){
27377             this.selectorEl.dom.click();
27378         }
27379     },
27380     
27381     onFileSelected : function(e)
27382     {
27383         e.preventDefault();
27384         
27385         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27386             return;
27387         }
27388         
27389         var file = this.selectorEl.dom.files[0];
27390         
27391         if(this.fireEvent('inspect', this, file) != false){
27392             this.prepare(file);
27393         }
27394         
27395     },
27396     
27397     trash : function(e)
27398     {
27399         this.fireEvent('trash', this);
27400     },
27401     
27402     download : function(e)
27403     {
27404         this.fireEvent('download', this);
27405     },
27406     
27407     loadCanvas : function(src)
27408     {   
27409         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27410             
27411             this.reset();
27412             
27413             this.imageEl = document.createElement('img');
27414             
27415             var _this = this;
27416             
27417             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27418             
27419             this.imageEl.src = src;
27420         }
27421     },
27422     
27423     onLoadCanvas : function()
27424     {   
27425         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27426         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27427         
27428         this.bodyEl.un('click', this.beforeSelectFile, this);
27429         
27430         this.notifyEl.hide();
27431         this.thumbEl.show();
27432         this.footerEl.show();
27433         
27434         this.baseRotateLevel();
27435         
27436         if(this.isDocument){
27437             this.setThumbBoxSize();
27438         }
27439         
27440         this.setThumbBoxPosition();
27441         
27442         this.baseScaleLevel();
27443         
27444         this.draw();
27445         
27446         this.resize();
27447         
27448         this.canvasLoaded = true;
27449         
27450         if(this.loadMask){
27451             this.maskEl.unmask();
27452         }
27453         
27454     },
27455     
27456     setCanvasPosition : function()
27457     {   
27458         if(!this.canvasEl){
27459             return;
27460         }
27461         
27462         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27463         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27464         
27465         this.previewEl.setLeft(pw);
27466         this.previewEl.setTop(ph);
27467         
27468     },
27469     
27470     onMouseDown : function(e)
27471     {   
27472         e.stopEvent();
27473         
27474         this.dragable = true;
27475         this.pinching = false;
27476         
27477         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27478             this.dragable = false;
27479             return;
27480         }
27481         
27482         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27483         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27484         
27485     },
27486     
27487     onMouseMove : function(e)
27488     {   
27489         e.stopEvent();
27490         
27491         if(!this.canvasLoaded){
27492             return;
27493         }
27494         
27495         if (!this.dragable){
27496             return;
27497         }
27498         
27499         var minX = Math.ceil(this.thumbEl.getLeft(true));
27500         var minY = Math.ceil(this.thumbEl.getTop(true));
27501         
27502         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27503         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27504         
27505         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27506         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27507         
27508         x = x - this.mouseX;
27509         y = y - this.mouseY;
27510         
27511         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27512         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27513         
27514         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27515         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27516         
27517         this.previewEl.setLeft(bgX);
27518         this.previewEl.setTop(bgY);
27519         
27520         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27521         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27522     },
27523     
27524     onMouseUp : function(e)
27525     {   
27526         e.stopEvent();
27527         
27528         this.dragable = false;
27529     },
27530     
27531     onMouseWheel : function(e)
27532     {   
27533         e.stopEvent();
27534         
27535         this.startScale = this.scale;
27536         
27537         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27538         
27539         if(!this.zoomable()){
27540             this.scale = this.startScale;
27541             return;
27542         }
27543         
27544         this.draw();
27545         
27546         return;
27547     },
27548     
27549     zoomable : function()
27550     {
27551         var minScale = this.thumbEl.getWidth() / this.minWidth;
27552         
27553         if(this.minWidth < this.minHeight){
27554             minScale = this.thumbEl.getHeight() / this.minHeight;
27555         }
27556         
27557         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27558         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27559         
27560         if(
27561                 this.isDocument &&
27562                 (this.rotate == 0 || this.rotate == 180) && 
27563                 (
27564                     width > this.imageEl.OriginWidth || 
27565                     height > this.imageEl.OriginHeight ||
27566                     (width < this.minWidth && height < this.minHeight)
27567                 )
27568         ){
27569             return false;
27570         }
27571         
27572         if(
27573                 this.isDocument &&
27574                 (this.rotate == 90 || this.rotate == 270) && 
27575                 (
27576                     width > this.imageEl.OriginWidth || 
27577                     height > this.imageEl.OriginHeight ||
27578                     (width < this.minHeight && height < this.minWidth)
27579                 )
27580         ){
27581             return false;
27582         }
27583         
27584         if(
27585                 !this.isDocument &&
27586                 (this.rotate == 0 || this.rotate == 180) && 
27587                 (
27588                     width < this.minWidth || 
27589                     width > this.imageEl.OriginWidth || 
27590                     height < this.minHeight || 
27591                     height > this.imageEl.OriginHeight
27592                 )
27593         ){
27594             return false;
27595         }
27596         
27597         if(
27598                 !this.isDocument &&
27599                 (this.rotate == 90 || this.rotate == 270) && 
27600                 (
27601                     width < this.minHeight || 
27602                     width > this.imageEl.OriginWidth || 
27603                     height < this.minWidth || 
27604                     height > this.imageEl.OriginHeight
27605                 )
27606         ){
27607             return false;
27608         }
27609         
27610         return true;
27611         
27612     },
27613     
27614     onRotateLeft : function(e)
27615     {   
27616         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27617             
27618             var minScale = this.thumbEl.getWidth() / this.minWidth;
27619             
27620             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27621             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27622             
27623             this.startScale = this.scale;
27624             
27625             while (this.getScaleLevel() < minScale){
27626             
27627                 this.scale = this.scale + 1;
27628                 
27629                 if(!this.zoomable()){
27630                     break;
27631                 }
27632                 
27633                 if(
27634                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27635                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27636                 ){
27637                     continue;
27638                 }
27639                 
27640                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27641
27642                 this.draw();
27643                 
27644                 return;
27645             }
27646             
27647             this.scale = this.startScale;
27648             
27649             this.onRotateFail();
27650             
27651             return false;
27652         }
27653         
27654         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27655
27656         if(this.isDocument){
27657             this.setThumbBoxSize();
27658             this.setThumbBoxPosition();
27659             this.setCanvasPosition();
27660         }
27661         
27662         this.draw();
27663         
27664         this.fireEvent('rotate', this, 'left');
27665         
27666     },
27667     
27668     onRotateRight : function(e)
27669     {
27670         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27671             
27672             var minScale = this.thumbEl.getWidth() / this.minWidth;
27673         
27674             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27675             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27676             
27677             this.startScale = this.scale;
27678             
27679             while (this.getScaleLevel() < minScale){
27680             
27681                 this.scale = this.scale + 1;
27682                 
27683                 if(!this.zoomable()){
27684                     break;
27685                 }
27686                 
27687                 if(
27688                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27689                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27690                 ){
27691                     continue;
27692                 }
27693                 
27694                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27695
27696                 this.draw();
27697                 
27698                 return;
27699             }
27700             
27701             this.scale = this.startScale;
27702             
27703             this.onRotateFail();
27704             
27705             return false;
27706         }
27707         
27708         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27709
27710         if(this.isDocument){
27711             this.setThumbBoxSize();
27712             this.setThumbBoxPosition();
27713             this.setCanvasPosition();
27714         }
27715         
27716         this.draw();
27717         
27718         this.fireEvent('rotate', this, 'right');
27719     },
27720     
27721     onRotateFail : function()
27722     {
27723         this.errorEl.show(true);
27724         
27725         var _this = this;
27726         
27727         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27728     },
27729     
27730     draw : function()
27731     {
27732         this.previewEl.dom.innerHTML = '';
27733         
27734         var canvasEl = document.createElement("canvas");
27735         
27736         var contextEl = canvasEl.getContext("2d");
27737         
27738         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27739         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27740         var center = this.imageEl.OriginWidth / 2;
27741         
27742         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27743             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27744             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27745             center = this.imageEl.OriginHeight / 2;
27746         }
27747         
27748         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27749         
27750         contextEl.translate(center, center);
27751         contextEl.rotate(this.rotate * Math.PI / 180);
27752
27753         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27754         
27755         this.canvasEl = document.createElement("canvas");
27756         
27757         this.contextEl = this.canvasEl.getContext("2d");
27758         
27759         switch (this.rotate) {
27760             case 0 :
27761                 
27762                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27763                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27764                 
27765                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27766                 
27767                 break;
27768             case 90 : 
27769                 
27770                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27771                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27772                 
27773                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27774                     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);
27775                     break;
27776                 }
27777                 
27778                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27779                 
27780                 break;
27781             case 180 :
27782                 
27783                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27784                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27785                 
27786                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27787                     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);
27788                     break;
27789                 }
27790                 
27791                 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);
27792                 
27793                 break;
27794             case 270 :
27795                 
27796                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27797                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27798         
27799                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27800                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27801                     break;
27802                 }
27803                 
27804                 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);
27805                 
27806                 break;
27807             default : 
27808                 break;
27809         }
27810         
27811         this.previewEl.appendChild(this.canvasEl);
27812         
27813         this.setCanvasPosition();
27814     },
27815     
27816     crop : function()
27817     {
27818         if(!this.canvasLoaded){
27819             return;
27820         }
27821         
27822         var imageCanvas = document.createElement("canvas");
27823         
27824         var imageContext = imageCanvas.getContext("2d");
27825         
27826         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27827         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27828         
27829         var center = imageCanvas.width / 2;
27830         
27831         imageContext.translate(center, center);
27832         
27833         imageContext.rotate(this.rotate * Math.PI / 180);
27834         
27835         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27836         
27837         var canvas = document.createElement("canvas");
27838         
27839         var context = canvas.getContext("2d");
27840                 
27841         canvas.width = this.minWidth;
27842         canvas.height = this.minHeight;
27843
27844         switch (this.rotate) {
27845             case 0 :
27846                 
27847                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27848                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27849                 
27850                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27851                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27852                 
27853                 var targetWidth = this.minWidth - 2 * x;
27854                 var targetHeight = this.minHeight - 2 * y;
27855                 
27856                 var scale = 1;
27857                 
27858                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27859                     scale = targetWidth / width;
27860                 }
27861                 
27862                 if(x > 0 && y == 0){
27863                     scale = targetHeight / height;
27864                 }
27865                 
27866                 if(x > 0 && y > 0){
27867                     scale = targetWidth / width;
27868                     
27869                     if(width < height){
27870                         scale = targetHeight / height;
27871                     }
27872                 }
27873                 
27874                 context.scale(scale, scale);
27875                 
27876                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27877                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27878
27879                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27880                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27881
27882                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27883                 
27884                 break;
27885             case 90 : 
27886                 
27887                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27888                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (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                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27923                 
27924                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27925                 
27926                 break;
27927             case 180 :
27928                 
27929                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27930                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27931                 
27932                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27933                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27934                 
27935                 var targetWidth = this.minWidth - 2 * x;
27936                 var targetHeight = this.minHeight - 2 * y;
27937                 
27938                 var scale = 1;
27939                 
27940                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27941                     scale = targetWidth / width;
27942                 }
27943                 
27944                 if(x > 0 && y == 0){
27945                     scale = targetHeight / height;
27946                 }
27947                 
27948                 if(x > 0 && y > 0){
27949                     scale = targetWidth / width;
27950                     
27951                     if(width < height){
27952                         scale = targetHeight / height;
27953                     }
27954                 }
27955                 
27956                 context.scale(scale, scale);
27957                 
27958                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27959                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27960
27961                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27962                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27963
27964                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27965                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27966                 
27967                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27968                 
27969                 break;
27970             case 270 :
27971                 
27972                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27973                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27974                 
27975                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27976                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27977                 
27978                 var targetWidth = this.minWidth - 2 * x;
27979                 var targetHeight = this.minHeight - 2 * y;
27980                 
27981                 var scale = 1;
27982                 
27983                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27984                     scale = targetWidth / width;
27985                 }
27986                 
27987                 if(x > 0 && y == 0){
27988                     scale = targetHeight / height;
27989                 }
27990                 
27991                 if(x > 0 && y > 0){
27992                     scale = targetWidth / width;
27993                     
27994                     if(width < height){
27995                         scale = targetHeight / height;
27996                     }
27997                 }
27998                 
27999                 context.scale(scale, scale);
28000                 
28001                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28002                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28003
28004                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28005                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28006                 
28007                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28008                 
28009                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28010                 
28011                 break;
28012             default : 
28013                 break;
28014         }
28015         
28016         this.cropData = canvas.toDataURL(this.cropType);
28017         
28018         if(this.fireEvent('crop', this, this.cropData) !== false){
28019             this.process(this.file, this.cropData);
28020         }
28021         
28022         return;
28023         
28024     },
28025     
28026     setThumbBoxSize : function()
28027     {
28028         var width, height;
28029         
28030         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28031             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28032             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28033             
28034             this.minWidth = width;
28035             this.minHeight = height;
28036             
28037             if(this.rotate == 90 || this.rotate == 270){
28038                 this.minWidth = height;
28039                 this.minHeight = width;
28040             }
28041         }
28042         
28043         height = 300;
28044         width = Math.ceil(this.minWidth * height / this.minHeight);
28045         
28046         if(this.minWidth > this.minHeight){
28047             width = 300;
28048             height = Math.ceil(this.minHeight * width / this.minWidth);
28049         }
28050         
28051         this.thumbEl.setStyle({
28052             width : width + 'px',
28053             height : height + 'px'
28054         });
28055
28056         return;
28057             
28058     },
28059     
28060     setThumbBoxPosition : function()
28061     {
28062         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28063         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28064         
28065         this.thumbEl.setLeft(x);
28066         this.thumbEl.setTop(y);
28067         
28068     },
28069     
28070     baseRotateLevel : function()
28071     {
28072         this.baseRotate = 1;
28073         
28074         if(
28075                 typeof(this.exif) != 'undefined' &&
28076                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28077                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28078         ){
28079             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28080         }
28081         
28082         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28083         
28084     },
28085     
28086     baseScaleLevel : function()
28087     {
28088         var width, height;
28089         
28090         if(this.isDocument){
28091             
28092             if(this.baseRotate == 6 || this.baseRotate == 8){
28093             
28094                 height = this.thumbEl.getHeight();
28095                 this.baseScale = height / this.imageEl.OriginWidth;
28096
28097                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28098                     width = this.thumbEl.getWidth();
28099                     this.baseScale = width / this.imageEl.OriginHeight;
28100                 }
28101
28102                 return;
28103             }
28104
28105             height = this.thumbEl.getHeight();
28106             this.baseScale = height / this.imageEl.OriginHeight;
28107
28108             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28109                 width = this.thumbEl.getWidth();
28110                 this.baseScale = width / this.imageEl.OriginWidth;
28111             }
28112
28113             return;
28114         }
28115         
28116         if(this.baseRotate == 6 || this.baseRotate == 8){
28117             
28118             width = this.thumbEl.getHeight();
28119             this.baseScale = width / this.imageEl.OriginHeight;
28120             
28121             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28122                 height = this.thumbEl.getWidth();
28123                 this.baseScale = height / this.imageEl.OriginHeight;
28124             }
28125             
28126             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28127                 height = this.thumbEl.getWidth();
28128                 this.baseScale = height / this.imageEl.OriginHeight;
28129                 
28130                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28131                     width = this.thumbEl.getHeight();
28132                     this.baseScale = width / this.imageEl.OriginWidth;
28133                 }
28134             }
28135             
28136             return;
28137         }
28138         
28139         width = this.thumbEl.getWidth();
28140         this.baseScale = width / this.imageEl.OriginWidth;
28141         
28142         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28143             height = this.thumbEl.getHeight();
28144             this.baseScale = height / this.imageEl.OriginHeight;
28145         }
28146         
28147         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28148             
28149             height = this.thumbEl.getHeight();
28150             this.baseScale = height / this.imageEl.OriginHeight;
28151             
28152             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28153                 width = this.thumbEl.getWidth();
28154                 this.baseScale = width / this.imageEl.OriginWidth;
28155             }
28156             
28157         }
28158         
28159         return;
28160     },
28161     
28162     getScaleLevel : function()
28163     {
28164         return this.baseScale * Math.pow(1.1, this.scale);
28165     },
28166     
28167     onTouchStart : function(e)
28168     {
28169         if(!this.canvasLoaded){
28170             this.beforeSelectFile(e);
28171             return;
28172         }
28173         
28174         var touches = e.browserEvent.touches;
28175         
28176         if(!touches){
28177             return;
28178         }
28179         
28180         if(touches.length == 1){
28181             this.onMouseDown(e);
28182             return;
28183         }
28184         
28185         if(touches.length != 2){
28186             return;
28187         }
28188         
28189         var coords = [];
28190         
28191         for(var i = 0, finger; finger = touches[i]; i++){
28192             coords.push(finger.pageX, finger.pageY);
28193         }
28194         
28195         var x = Math.pow(coords[0] - coords[2], 2);
28196         var y = Math.pow(coords[1] - coords[3], 2);
28197         
28198         this.startDistance = Math.sqrt(x + y);
28199         
28200         this.startScale = this.scale;
28201         
28202         this.pinching = true;
28203         this.dragable = false;
28204         
28205     },
28206     
28207     onTouchMove : function(e)
28208     {
28209         if(!this.pinching && !this.dragable){
28210             return;
28211         }
28212         
28213         var touches = e.browserEvent.touches;
28214         
28215         if(!touches){
28216             return;
28217         }
28218         
28219         if(this.dragable){
28220             this.onMouseMove(e);
28221             return;
28222         }
28223         
28224         var coords = [];
28225         
28226         for(var i = 0, finger; finger = touches[i]; i++){
28227             coords.push(finger.pageX, finger.pageY);
28228         }
28229         
28230         var x = Math.pow(coords[0] - coords[2], 2);
28231         var y = Math.pow(coords[1] - coords[3], 2);
28232         
28233         this.endDistance = Math.sqrt(x + y);
28234         
28235         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28236         
28237         if(!this.zoomable()){
28238             this.scale = this.startScale;
28239             return;
28240         }
28241         
28242         this.draw();
28243         
28244     },
28245     
28246     onTouchEnd : function(e)
28247     {
28248         this.pinching = false;
28249         this.dragable = false;
28250         
28251     },
28252     
28253     process : function(file, crop)
28254     {
28255         if(this.loadMask){
28256             this.maskEl.mask(this.loadingText);
28257         }
28258         
28259         this.xhr = new XMLHttpRequest();
28260         
28261         file.xhr = this.xhr;
28262
28263         this.xhr.open(this.method, this.url, true);
28264         
28265         var headers = {
28266             "Accept": "application/json",
28267             "Cache-Control": "no-cache",
28268             "X-Requested-With": "XMLHttpRequest"
28269         };
28270         
28271         for (var headerName in headers) {
28272             var headerValue = headers[headerName];
28273             if (headerValue) {
28274                 this.xhr.setRequestHeader(headerName, headerValue);
28275             }
28276         }
28277         
28278         var _this = this;
28279         
28280         this.xhr.onload = function()
28281         {
28282             _this.xhrOnLoad(_this.xhr);
28283         }
28284         
28285         this.xhr.onerror = function()
28286         {
28287             _this.xhrOnError(_this.xhr);
28288         }
28289         
28290         var formData = new FormData();
28291
28292         formData.append('returnHTML', 'NO');
28293         
28294         if(crop){
28295             formData.append('crop', crop);
28296         }
28297         
28298         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28299             formData.append(this.paramName, file, file.name);
28300         }
28301         
28302         if(typeof(file.filename) != 'undefined'){
28303             formData.append('filename', file.filename);
28304         }
28305         
28306         if(typeof(file.mimetype) != 'undefined'){
28307             formData.append('mimetype', file.mimetype);
28308         }
28309         
28310         if(this.fireEvent('arrange', this, formData) != false){
28311             this.xhr.send(formData);
28312         };
28313     },
28314     
28315     xhrOnLoad : function(xhr)
28316     {
28317         if(this.loadMask){
28318             this.maskEl.unmask();
28319         }
28320         
28321         if (xhr.readyState !== 4) {
28322             this.fireEvent('exception', this, xhr);
28323             return;
28324         }
28325
28326         var response = Roo.decode(xhr.responseText);
28327         
28328         if(!response.success){
28329             this.fireEvent('exception', this, xhr);
28330             return;
28331         }
28332         
28333         var response = Roo.decode(xhr.responseText);
28334         
28335         this.fireEvent('upload', this, response);
28336         
28337     },
28338     
28339     xhrOnError : function()
28340     {
28341         if(this.loadMask){
28342             this.maskEl.unmask();
28343         }
28344         
28345         Roo.log('xhr on error');
28346         
28347         var response = Roo.decode(xhr.responseText);
28348           
28349         Roo.log(response);
28350         
28351     },
28352     
28353     prepare : function(file)
28354     {   
28355         if(this.loadMask){
28356             this.maskEl.mask(this.loadingText);
28357         }
28358         
28359         this.file = false;
28360         this.exif = {};
28361         
28362         if(typeof(file) === 'string'){
28363             this.loadCanvas(file);
28364             return;
28365         }
28366         
28367         if(!file || !this.urlAPI){
28368             return;
28369         }
28370         
28371         this.file = file;
28372         this.cropType = file.type;
28373         
28374         var _this = this;
28375         
28376         if(this.fireEvent('prepare', this, this.file) != false){
28377             
28378             var reader = new FileReader();
28379             
28380             reader.onload = function (e) {
28381                 if (e.target.error) {
28382                     Roo.log(e.target.error);
28383                     return;
28384                 }
28385                 
28386                 var buffer = e.target.result,
28387                     dataView = new DataView(buffer),
28388                     offset = 2,
28389                     maxOffset = dataView.byteLength - 4,
28390                     markerBytes,
28391                     markerLength;
28392                 
28393                 if (dataView.getUint16(0) === 0xffd8) {
28394                     while (offset < maxOffset) {
28395                         markerBytes = dataView.getUint16(offset);
28396                         
28397                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28398                             markerLength = dataView.getUint16(offset + 2) + 2;
28399                             if (offset + markerLength > dataView.byteLength) {
28400                                 Roo.log('Invalid meta data: Invalid segment size.');
28401                                 break;
28402                             }
28403                             
28404                             if(markerBytes == 0xffe1){
28405                                 _this.parseExifData(
28406                                     dataView,
28407                                     offset,
28408                                     markerLength
28409                                 );
28410                             }
28411                             
28412                             offset += markerLength;
28413                             
28414                             continue;
28415                         }
28416                         
28417                         break;
28418                     }
28419                     
28420                 }
28421                 
28422                 var url = _this.urlAPI.createObjectURL(_this.file);
28423                 
28424                 _this.loadCanvas(url);
28425                 
28426                 return;
28427             }
28428             
28429             reader.readAsArrayBuffer(this.file);
28430             
28431         }
28432         
28433     },
28434     
28435     parseExifData : function(dataView, offset, length)
28436     {
28437         var tiffOffset = offset + 10,
28438             littleEndian,
28439             dirOffset;
28440     
28441         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28442             // No Exif data, might be XMP data instead
28443             return;
28444         }
28445         
28446         // Check for the ASCII code for "Exif" (0x45786966):
28447         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28448             // No Exif data, might be XMP data instead
28449             return;
28450         }
28451         if (tiffOffset + 8 > dataView.byteLength) {
28452             Roo.log('Invalid Exif data: Invalid segment size.');
28453             return;
28454         }
28455         // Check for the two null bytes:
28456         if (dataView.getUint16(offset + 8) !== 0x0000) {
28457             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28458             return;
28459         }
28460         // Check the byte alignment:
28461         switch (dataView.getUint16(tiffOffset)) {
28462         case 0x4949:
28463             littleEndian = true;
28464             break;
28465         case 0x4D4D:
28466             littleEndian = false;
28467             break;
28468         default:
28469             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28470             return;
28471         }
28472         // Check for the TIFF tag marker (0x002A):
28473         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28474             Roo.log('Invalid Exif data: Missing TIFF marker.');
28475             return;
28476         }
28477         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28478         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28479         
28480         this.parseExifTags(
28481             dataView,
28482             tiffOffset,
28483             tiffOffset + dirOffset,
28484             littleEndian
28485         );
28486     },
28487     
28488     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28489     {
28490         var tagsNumber,
28491             dirEndOffset,
28492             i;
28493         if (dirOffset + 6 > dataView.byteLength) {
28494             Roo.log('Invalid Exif data: Invalid directory offset.');
28495             return;
28496         }
28497         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28498         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28499         if (dirEndOffset + 4 > dataView.byteLength) {
28500             Roo.log('Invalid Exif data: Invalid directory size.');
28501             return;
28502         }
28503         for (i = 0; i < tagsNumber; i += 1) {
28504             this.parseExifTag(
28505                 dataView,
28506                 tiffOffset,
28507                 dirOffset + 2 + 12 * i, // tag offset
28508                 littleEndian
28509             );
28510         }
28511         // Return the offset to the next directory:
28512         return dataView.getUint32(dirEndOffset, littleEndian);
28513     },
28514     
28515     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28516     {
28517         var tag = dataView.getUint16(offset, littleEndian);
28518         
28519         this.exif[tag] = this.getExifValue(
28520             dataView,
28521             tiffOffset,
28522             offset,
28523             dataView.getUint16(offset + 2, littleEndian), // tag type
28524             dataView.getUint32(offset + 4, littleEndian), // tag length
28525             littleEndian
28526         );
28527     },
28528     
28529     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28530     {
28531         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28532             tagSize,
28533             dataOffset,
28534             values,
28535             i,
28536             str,
28537             c;
28538     
28539         if (!tagType) {
28540             Roo.log('Invalid Exif data: Invalid tag type.');
28541             return;
28542         }
28543         
28544         tagSize = tagType.size * length;
28545         // Determine if the value is contained in the dataOffset bytes,
28546         // or if the value at the dataOffset is a pointer to the actual data:
28547         dataOffset = tagSize > 4 ?
28548                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28549         if (dataOffset + tagSize > dataView.byteLength) {
28550             Roo.log('Invalid Exif data: Invalid data offset.');
28551             return;
28552         }
28553         if (length === 1) {
28554             return tagType.getValue(dataView, dataOffset, littleEndian);
28555         }
28556         values = [];
28557         for (i = 0; i < length; i += 1) {
28558             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28559         }
28560         
28561         if (tagType.ascii) {
28562             str = '';
28563             // Concatenate the chars:
28564             for (i = 0; i < values.length; i += 1) {
28565                 c = values[i];
28566                 // Ignore the terminating NULL byte(s):
28567                 if (c === '\u0000') {
28568                     break;
28569                 }
28570                 str += c;
28571             }
28572             return str;
28573         }
28574         return values;
28575     }
28576     
28577 });
28578
28579 Roo.apply(Roo.bootstrap.UploadCropbox, {
28580     tags : {
28581         'Orientation': 0x0112
28582     },
28583     
28584     Orientation: {
28585             1: 0, //'top-left',
28586 //            2: 'top-right',
28587             3: 180, //'bottom-right',
28588 //            4: 'bottom-left',
28589 //            5: 'left-top',
28590             6: 90, //'right-top',
28591 //            7: 'right-bottom',
28592             8: 270 //'left-bottom'
28593     },
28594     
28595     exifTagTypes : {
28596         // byte, 8-bit unsigned int:
28597         1: {
28598             getValue: function (dataView, dataOffset) {
28599                 return dataView.getUint8(dataOffset);
28600             },
28601             size: 1
28602         },
28603         // ascii, 8-bit byte:
28604         2: {
28605             getValue: function (dataView, dataOffset) {
28606                 return String.fromCharCode(dataView.getUint8(dataOffset));
28607             },
28608             size: 1,
28609             ascii: true
28610         },
28611         // short, 16 bit int:
28612         3: {
28613             getValue: function (dataView, dataOffset, littleEndian) {
28614                 return dataView.getUint16(dataOffset, littleEndian);
28615             },
28616             size: 2
28617         },
28618         // long, 32 bit int:
28619         4: {
28620             getValue: function (dataView, dataOffset, littleEndian) {
28621                 return dataView.getUint32(dataOffset, littleEndian);
28622             },
28623             size: 4
28624         },
28625         // rational = two long values, first is numerator, second is denominator:
28626         5: {
28627             getValue: function (dataView, dataOffset, littleEndian) {
28628                 return dataView.getUint32(dataOffset, littleEndian) /
28629                     dataView.getUint32(dataOffset + 4, littleEndian);
28630             },
28631             size: 8
28632         },
28633         // slong, 32 bit signed int:
28634         9: {
28635             getValue: function (dataView, dataOffset, littleEndian) {
28636                 return dataView.getInt32(dataOffset, littleEndian);
28637             },
28638             size: 4
28639         },
28640         // srational, two slongs, first is numerator, second is denominator:
28641         10: {
28642             getValue: function (dataView, dataOffset, littleEndian) {
28643                 return dataView.getInt32(dataOffset, littleEndian) /
28644                     dataView.getInt32(dataOffset + 4, littleEndian);
28645             },
28646             size: 8
28647         }
28648     },
28649     
28650     footer : {
28651         STANDARD : [
28652             {
28653                 tag : 'div',
28654                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28655                 action : 'rotate-left',
28656                 cn : [
28657                     {
28658                         tag : 'button',
28659                         cls : 'btn btn-default',
28660                         html : '<i class="fa fa-undo"></i>'
28661                     }
28662                 ]
28663             },
28664             {
28665                 tag : 'div',
28666                 cls : 'btn-group roo-upload-cropbox-picture',
28667                 action : 'picture',
28668                 cn : [
28669                     {
28670                         tag : 'button',
28671                         cls : 'btn btn-default',
28672                         html : '<i class="fa fa-picture-o"></i>'
28673                     }
28674                 ]
28675             },
28676             {
28677                 tag : 'div',
28678                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28679                 action : 'rotate-right',
28680                 cn : [
28681                     {
28682                         tag : 'button',
28683                         cls : 'btn btn-default',
28684                         html : '<i class="fa fa-repeat"></i>'
28685                     }
28686                 ]
28687             }
28688         ],
28689         DOCUMENT : [
28690             {
28691                 tag : 'div',
28692                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28693                 action : 'rotate-left',
28694                 cn : [
28695                     {
28696                         tag : 'button',
28697                         cls : 'btn btn-default',
28698                         html : '<i class="fa fa-undo"></i>'
28699                     }
28700                 ]
28701             },
28702             {
28703                 tag : 'div',
28704                 cls : 'btn-group roo-upload-cropbox-download',
28705                 action : 'download',
28706                 cn : [
28707                     {
28708                         tag : 'button',
28709                         cls : 'btn btn-default',
28710                         html : '<i class="fa fa-download"></i>'
28711                     }
28712                 ]
28713             },
28714             {
28715                 tag : 'div',
28716                 cls : 'btn-group roo-upload-cropbox-crop',
28717                 action : 'crop',
28718                 cn : [
28719                     {
28720                         tag : 'button',
28721                         cls : 'btn btn-default',
28722                         html : '<i class="fa fa-crop"></i>'
28723                     }
28724                 ]
28725             },
28726             {
28727                 tag : 'div',
28728                 cls : 'btn-group roo-upload-cropbox-trash',
28729                 action : 'trash',
28730                 cn : [
28731                     {
28732                         tag : 'button',
28733                         cls : 'btn btn-default',
28734                         html : '<i class="fa fa-trash"></i>'
28735                     }
28736                 ]
28737             },
28738             {
28739                 tag : 'div',
28740                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28741                 action : 'rotate-right',
28742                 cn : [
28743                     {
28744                         tag : 'button',
28745                         cls : 'btn btn-default',
28746                         html : '<i class="fa fa-repeat"></i>'
28747                     }
28748                 ]
28749             }
28750         ],
28751         ROTATOR : [
28752             {
28753                 tag : 'div',
28754                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28755                 action : 'rotate-left',
28756                 cn : [
28757                     {
28758                         tag : 'button',
28759                         cls : 'btn btn-default',
28760                         html : '<i class="fa fa-undo"></i>'
28761                     }
28762                 ]
28763             },
28764             {
28765                 tag : 'div',
28766                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28767                 action : 'rotate-right',
28768                 cn : [
28769                     {
28770                         tag : 'button',
28771                         cls : 'btn btn-default',
28772                         html : '<i class="fa fa-repeat"></i>'
28773                     }
28774                 ]
28775             }
28776         ]
28777     }
28778 });
28779
28780 /*
28781 * Licence: LGPL
28782 */
28783
28784 /**
28785  * @class Roo.bootstrap.DocumentManager
28786  * @extends Roo.bootstrap.Component
28787  * Bootstrap DocumentManager class
28788  * @cfg {String} paramName default 'imageUpload'
28789  * @cfg {String} toolTipName default 'filename'
28790  * @cfg {String} method default POST
28791  * @cfg {String} url action url
28792  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28793  * @cfg {Boolean} multiple multiple upload default true
28794  * @cfg {Number} thumbSize default 300
28795  * @cfg {String} fieldLabel
28796  * @cfg {Number} labelWidth default 4
28797  * @cfg {String} labelAlign (left|top) default left
28798  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28799 * @cfg {Number} labellg set the width of label (1-12)
28800  * @cfg {Number} labelmd set the width of label (1-12)
28801  * @cfg {Number} labelsm set the width of label (1-12)
28802  * @cfg {Number} labelxs set the width of label (1-12)
28803  * 
28804  * @constructor
28805  * Create a new DocumentManager
28806  * @param {Object} config The config object
28807  */
28808
28809 Roo.bootstrap.DocumentManager = function(config){
28810     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28811     
28812     this.files = [];
28813     this.delegates = [];
28814     
28815     this.addEvents({
28816         /**
28817          * @event initial
28818          * Fire when initial the DocumentManager
28819          * @param {Roo.bootstrap.DocumentManager} this
28820          */
28821         "initial" : true,
28822         /**
28823          * @event inspect
28824          * inspect selected file
28825          * @param {Roo.bootstrap.DocumentManager} this
28826          * @param {File} file
28827          */
28828         "inspect" : true,
28829         /**
28830          * @event exception
28831          * Fire when xhr load exception
28832          * @param {Roo.bootstrap.DocumentManager} this
28833          * @param {XMLHttpRequest} xhr
28834          */
28835         "exception" : true,
28836         /**
28837          * @event afterupload
28838          * Fire when xhr load exception
28839          * @param {Roo.bootstrap.DocumentManager} this
28840          * @param {XMLHttpRequest} xhr
28841          */
28842         "afterupload" : true,
28843         /**
28844          * @event prepare
28845          * prepare the form data
28846          * @param {Roo.bootstrap.DocumentManager} this
28847          * @param {Object} formData
28848          */
28849         "prepare" : true,
28850         /**
28851          * @event remove
28852          * Fire when remove the file
28853          * @param {Roo.bootstrap.DocumentManager} this
28854          * @param {Object} file
28855          */
28856         "remove" : true,
28857         /**
28858          * @event refresh
28859          * Fire after refresh the file
28860          * @param {Roo.bootstrap.DocumentManager} this
28861          */
28862         "refresh" : true,
28863         /**
28864          * @event click
28865          * Fire after click the image
28866          * @param {Roo.bootstrap.DocumentManager} this
28867          * @param {Object} file
28868          */
28869         "click" : true,
28870         /**
28871          * @event edit
28872          * Fire when upload a image and editable set to true
28873          * @param {Roo.bootstrap.DocumentManager} this
28874          * @param {Object} file
28875          */
28876         "edit" : true,
28877         /**
28878          * @event beforeselectfile
28879          * Fire before select file
28880          * @param {Roo.bootstrap.DocumentManager} this
28881          */
28882         "beforeselectfile" : true,
28883         /**
28884          * @event process
28885          * Fire before process file
28886          * @param {Roo.bootstrap.DocumentManager} this
28887          * @param {Object} file
28888          */
28889         "process" : true,
28890         /**
28891          * @event previewrendered
28892          * Fire when preview rendered
28893          * @param {Roo.bootstrap.DocumentManager} this
28894          * @param {Object} file
28895          */
28896         "previewrendered" : true,
28897         /**
28898          */
28899         "previewResize" : true
28900         
28901     });
28902 };
28903
28904 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28905     
28906     boxes : 0,
28907     inputName : '',
28908     thumbSize : 300,
28909     multiple : true,
28910     files : false,
28911     method : 'POST',
28912     url : '',
28913     paramName : 'imageUpload',
28914     toolTipName : 'filename',
28915     fieldLabel : '',
28916     labelWidth : 4,
28917     labelAlign : 'left',
28918     editable : true,
28919     delegates : false,
28920     xhr : false, 
28921     
28922     labellg : 0,
28923     labelmd : 0,
28924     labelsm : 0,
28925     labelxs : 0,
28926     
28927     getAutoCreate : function()
28928     {   
28929         var managerWidget = {
28930             tag : 'div',
28931             cls : 'roo-document-manager',
28932             cn : [
28933                 {
28934                     tag : 'input',
28935                     cls : 'roo-document-manager-selector',
28936                     type : 'file'
28937                 },
28938                 {
28939                     tag : 'div',
28940                     cls : 'roo-document-manager-uploader',
28941                     cn : [
28942                         {
28943                             tag : 'div',
28944                             cls : 'roo-document-manager-upload-btn',
28945                             html : '<i class="fa fa-plus"></i>'
28946                         }
28947                     ]
28948                     
28949                 }
28950             ]
28951         };
28952         
28953         var content = [
28954             {
28955                 tag : 'div',
28956                 cls : 'column col-md-12',
28957                 cn : managerWidget
28958             }
28959         ];
28960         
28961         if(this.fieldLabel.length){
28962             
28963             content = [
28964                 {
28965                     tag : 'div',
28966                     cls : 'column col-md-12',
28967                     html : this.fieldLabel
28968                 },
28969                 {
28970                     tag : 'div',
28971                     cls : 'column col-md-12',
28972                     cn : managerWidget
28973                 }
28974             ];
28975
28976             if(this.labelAlign == 'left'){
28977                 content = [
28978                     {
28979                         tag : 'div',
28980                         cls : 'column',
28981                         html : this.fieldLabel
28982                     },
28983                     {
28984                         tag : 'div',
28985                         cls : 'column',
28986                         cn : managerWidget
28987                     }
28988                 ];
28989                 
28990                 if(this.labelWidth > 12){
28991                     content[0].style = "width: " + this.labelWidth + 'px';
28992                 }
28993
28994                 if(this.labelWidth < 13 && this.labelmd == 0){
28995                     this.labelmd = this.labelWidth;
28996                 }
28997
28998                 if(this.labellg > 0){
28999                     content[0].cls += ' col-lg-' + this.labellg;
29000                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29001                 }
29002
29003                 if(this.labelmd > 0){
29004                     content[0].cls += ' col-md-' + this.labelmd;
29005                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29006                 }
29007
29008                 if(this.labelsm > 0){
29009                     content[0].cls += ' col-sm-' + this.labelsm;
29010                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29011                 }
29012
29013                 if(this.labelxs > 0){
29014                     content[0].cls += ' col-xs-' + this.labelxs;
29015                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29016                 }
29017                 
29018             }
29019         }
29020         
29021         var cfg = {
29022             tag : 'div',
29023             cls : 'row clearfix',
29024             cn : content
29025         };
29026         
29027         return cfg;
29028         
29029     },
29030     
29031     initEvents : function()
29032     {
29033         this.managerEl = this.el.select('.roo-document-manager', true).first();
29034         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29035         
29036         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29037         this.selectorEl.hide();
29038         
29039         if(this.multiple){
29040             this.selectorEl.attr('multiple', 'multiple');
29041         }
29042         
29043         this.selectorEl.on('change', this.onFileSelected, this);
29044         
29045         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29046         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29047         
29048         this.uploader.on('click', this.onUploaderClick, this);
29049         
29050         this.renderProgressDialog();
29051         
29052         var _this = this;
29053         
29054         window.addEventListener("resize", function() { _this.refresh(); } );
29055         
29056         this.fireEvent('initial', this);
29057     },
29058     
29059     renderProgressDialog : function()
29060     {
29061         var _this = this;
29062         
29063         this.progressDialog = new Roo.bootstrap.Modal({
29064             cls : 'roo-document-manager-progress-dialog',
29065             allow_close : false,
29066             title : '',
29067             buttons : [
29068                 {
29069                     name  :'cancel',
29070                     weight : 'danger',
29071                     html : 'Cancel'
29072                 }
29073             ], 
29074             listeners : { 
29075                 btnclick : function() {
29076                     _this.uploadCancel();
29077                     this.hide();
29078                 }
29079             }
29080         });
29081          
29082         this.progressDialog.render(Roo.get(document.body));
29083          
29084         this.progress = new Roo.bootstrap.Progress({
29085             cls : 'roo-document-manager-progress',
29086             active : true,
29087             striped : true
29088         });
29089         
29090         this.progress.render(this.progressDialog.getChildContainer());
29091         
29092         this.progressBar = new Roo.bootstrap.ProgressBar({
29093             cls : 'roo-document-manager-progress-bar',
29094             aria_valuenow : 0,
29095             aria_valuemin : 0,
29096             aria_valuemax : 12,
29097             panel : 'success'
29098         });
29099         
29100         this.progressBar.render(this.progress.getChildContainer());
29101     },
29102     
29103     onUploaderClick : function(e)
29104     {
29105         e.preventDefault();
29106      
29107         if(this.fireEvent('beforeselectfile', this) != false){
29108             this.selectorEl.dom.click();
29109         }
29110         
29111     },
29112     
29113     onFileSelected : function(e)
29114     {
29115         e.preventDefault();
29116         
29117         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29118             return;
29119         }
29120         
29121         Roo.each(this.selectorEl.dom.files, function(file){
29122             if(this.fireEvent('inspect', this, file) != false){
29123                 this.files.push(file);
29124             }
29125         }, this);
29126         
29127         this.queue();
29128         
29129     },
29130     
29131     queue : function()
29132     {
29133         this.selectorEl.dom.value = '';
29134         
29135         if(!this.files || !this.files.length){
29136             return;
29137         }
29138         
29139         if(this.boxes > 0 && this.files.length > this.boxes){
29140             this.files = this.files.slice(0, this.boxes);
29141         }
29142         
29143         this.uploader.show();
29144         
29145         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29146             this.uploader.hide();
29147         }
29148         
29149         var _this = this;
29150         
29151         var files = [];
29152         
29153         var docs = [];
29154         
29155         Roo.each(this.files, function(file){
29156             
29157             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29158                 var f = this.renderPreview(file);
29159                 files.push(f);
29160                 return;
29161             }
29162             
29163             if(file.type.indexOf('image') != -1){
29164                 this.delegates.push(
29165                     (function(){
29166                         _this.process(file);
29167                     }).createDelegate(this)
29168                 );
29169         
29170                 return;
29171             }
29172             
29173             docs.push(
29174                 (function(){
29175                     _this.process(file);
29176                 }).createDelegate(this)
29177             );
29178             
29179         }, this);
29180         
29181         this.files = files;
29182         
29183         this.delegates = this.delegates.concat(docs);
29184         
29185         if(!this.delegates.length){
29186             this.refresh();
29187             return;
29188         }
29189         
29190         this.progressBar.aria_valuemax = this.delegates.length;
29191         
29192         this.arrange();
29193         
29194         return;
29195     },
29196     
29197     arrange : function()
29198     {
29199         if(!this.delegates.length){
29200             this.progressDialog.hide();
29201             this.refresh();
29202             return;
29203         }
29204         
29205         var delegate = this.delegates.shift();
29206         
29207         this.progressDialog.show();
29208         
29209         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29210         
29211         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29212         
29213         delegate();
29214     },
29215     
29216     refresh : function()
29217     {
29218         this.uploader.show();
29219         
29220         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29221             this.uploader.hide();
29222         }
29223         
29224         Roo.isTouch ? this.closable(false) : this.closable(true);
29225         
29226         this.fireEvent('refresh', this);
29227     },
29228     
29229     onRemove : function(e, el, o)
29230     {
29231         e.preventDefault();
29232         
29233         this.fireEvent('remove', this, o);
29234         
29235     },
29236     
29237     remove : function(o)
29238     {
29239         var files = [];
29240         
29241         Roo.each(this.files, function(file){
29242             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29243                 files.push(file);
29244                 return;
29245             }
29246
29247             o.target.remove();
29248
29249         }, this);
29250         
29251         this.files = files;
29252         
29253         this.refresh();
29254     },
29255     
29256     clear : function()
29257     {
29258         Roo.each(this.files, function(file){
29259             if(!file.target){
29260                 return;
29261             }
29262             
29263             file.target.remove();
29264
29265         }, this);
29266         
29267         this.files = [];
29268         
29269         this.refresh();
29270     },
29271     
29272     onClick : function(e, el, o)
29273     {
29274         e.preventDefault();
29275         
29276         this.fireEvent('click', this, o);
29277         
29278     },
29279     
29280     closable : function(closable)
29281     {
29282         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29283             
29284             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29285             
29286             if(closable){
29287                 el.show();
29288                 return;
29289             }
29290             
29291             el.hide();
29292             
29293         }, this);
29294     },
29295     
29296     xhrOnLoad : function(xhr)
29297     {
29298         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29299             el.remove();
29300         }, this);
29301         
29302         if (xhr.readyState !== 4) {
29303             this.arrange();
29304             this.fireEvent('exception', this, xhr);
29305             return;
29306         }
29307
29308         var response = Roo.decode(xhr.responseText);
29309         
29310         if(!response.success){
29311             this.arrange();
29312             this.fireEvent('exception', this, xhr);
29313             return;
29314         }
29315         
29316         var file = this.renderPreview(response.data);
29317         
29318         this.files.push(file);
29319         
29320         this.arrange();
29321         
29322         this.fireEvent('afterupload', this, xhr);
29323         
29324     },
29325     
29326     xhrOnError : function(xhr)
29327     {
29328         Roo.log('xhr on error');
29329         
29330         var response = Roo.decode(xhr.responseText);
29331           
29332         Roo.log(response);
29333         
29334         this.arrange();
29335     },
29336     
29337     process : function(file)
29338     {
29339         if(this.fireEvent('process', this, file) !== false){
29340             if(this.editable && file.type.indexOf('image') != -1){
29341                 this.fireEvent('edit', this, file);
29342                 return;
29343             }
29344
29345             this.uploadStart(file, false);
29346
29347             return;
29348         }
29349         
29350     },
29351     
29352     uploadStart : function(file, crop)
29353     {
29354         this.xhr = new XMLHttpRequest();
29355         
29356         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29357             this.arrange();
29358             return;
29359         }
29360         
29361         file.xhr = this.xhr;
29362             
29363         this.managerEl.createChild({
29364             tag : 'div',
29365             cls : 'roo-document-manager-loading',
29366             cn : [
29367                 {
29368                     tag : 'div',
29369                     tooltip : file.name,
29370                     cls : 'roo-document-manager-thumb',
29371                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29372                 }
29373             ]
29374
29375         });
29376
29377         this.xhr.open(this.method, this.url, true);
29378         
29379         var headers = {
29380             "Accept": "application/json",
29381             "Cache-Control": "no-cache",
29382             "X-Requested-With": "XMLHttpRequest"
29383         };
29384         
29385         for (var headerName in headers) {
29386             var headerValue = headers[headerName];
29387             if (headerValue) {
29388                 this.xhr.setRequestHeader(headerName, headerValue);
29389             }
29390         }
29391         
29392         var _this = this;
29393         
29394         this.xhr.onload = function()
29395         {
29396             _this.xhrOnLoad(_this.xhr);
29397         }
29398         
29399         this.xhr.onerror = function()
29400         {
29401             _this.xhrOnError(_this.xhr);
29402         }
29403         
29404         var formData = new FormData();
29405
29406         formData.append('returnHTML', 'NO');
29407         
29408         if(crop){
29409             formData.append('crop', crop);
29410         }
29411         
29412         formData.append(this.paramName, file, file.name);
29413         
29414         var options = {
29415             file : file, 
29416             manually : false
29417         };
29418         
29419         if(this.fireEvent('prepare', this, formData, options) != false){
29420             
29421             if(options.manually){
29422                 return;
29423             }
29424             
29425             this.xhr.send(formData);
29426             return;
29427         };
29428         
29429         this.uploadCancel();
29430     },
29431     
29432     uploadCancel : function()
29433     {
29434         if (this.xhr) {
29435             this.xhr.abort();
29436         }
29437         
29438         this.delegates = [];
29439         
29440         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29441             el.remove();
29442         }, this);
29443         
29444         this.arrange();
29445     },
29446     
29447     renderPreview : function(file)
29448     {
29449         if(typeof(file.target) != 'undefined' && file.target){
29450             return file;
29451         }
29452         
29453         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29454         
29455         var previewEl = this.managerEl.createChild({
29456             tag : 'div',
29457             cls : 'roo-document-manager-preview',
29458             cn : [
29459                 {
29460                     tag : 'div',
29461                     tooltip : file[this.toolTipName],
29462                     cls : 'roo-document-manager-thumb',
29463                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29464                 },
29465                 {
29466                     tag : 'button',
29467                     cls : 'close',
29468                     html : '<i class="fa fa-times-circle"></i>'
29469                 }
29470             ]
29471         });
29472
29473         var close = previewEl.select('button.close', true).first();
29474
29475         close.on('click', this.onRemove, this, file);
29476
29477         file.target = previewEl;
29478
29479         var image = previewEl.select('img', true).first();
29480         
29481         var _this = this;
29482         
29483         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29484         
29485         image.on('click', this.onClick, this, file);
29486         
29487         this.fireEvent('previewrendered', this, file);
29488         
29489         return file;
29490         
29491     },
29492     
29493     onPreviewLoad : function(file, image)
29494     {
29495         if(typeof(file.target) == 'undefined' || !file.target){
29496             return;
29497         }
29498         
29499         var width = image.dom.naturalWidth || image.dom.width;
29500         var height = image.dom.naturalHeight || image.dom.height;
29501         
29502         if(!this.previewResize) {
29503             return;
29504         }
29505         
29506         if(width > height){
29507             file.target.addClass('wide');
29508             return;
29509         }
29510         
29511         file.target.addClass('tall');
29512         return;
29513         
29514     },
29515     
29516     uploadFromSource : function(file, crop)
29517     {
29518         this.xhr = new XMLHttpRequest();
29519         
29520         this.managerEl.createChild({
29521             tag : 'div',
29522             cls : 'roo-document-manager-loading',
29523             cn : [
29524                 {
29525                     tag : 'div',
29526                     tooltip : file.name,
29527                     cls : 'roo-document-manager-thumb',
29528                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29529                 }
29530             ]
29531
29532         });
29533
29534         this.xhr.open(this.method, this.url, true);
29535         
29536         var headers = {
29537             "Accept": "application/json",
29538             "Cache-Control": "no-cache",
29539             "X-Requested-With": "XMLHttpRequest"
29540         };
29541         
29542         for (var headerName in headers) {
29543             var headerValue = headers[headerName];
29544             if (headerValue) {
29545                 this.xhr.setRequestHeader(headerName, headerValue);
29546             }
29547         }
29548         
29549         var _this = this;
29550         
29551         this.xhr.onload = function()
29552         {
29553             _this.xhrOnLoad(_this.xhr);
29554         }
29555         
29556         this.xhr.onerror = function()
29557         {
29558             _this.xhrOnError(_this.xhr);
29559         }
29560         
29561         var formData = new FormData();
29562
29563         formData.append('returnHTML', 'NO');
29564         
29565         formData.append('crop', crop);
29566         
29567         if(typeof(file.filename) != 'undefined'){
29568             formData.append('filename', file.filename);
29569         }
29570         
29571         if(typeof(file.mimetype) != 'undefined'){
29572             formData.append('mimetype', file.mimetype);
29573         }
29574         
29575         Roo.log(formData);
29576         
29577         if(this.fireEvent('prepare', this, formData) != false){
29578             this.xhr.send(formData);
29579         };
29580     }
29581 });
29582
29583 /*
29584 * Licence: LGPL
29585 */
29586
29587 /**
29588  * @class Roo.bootstrap.DocumentViewer
29589  * @extends Roo.bootstrap.Component
29590  * Bootstrap DocumentViewer class
29591  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29592  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29593  * 
29594  * @constructor
29595  * Create a new DocumentViewer
29596  * @param {Object} config The config object
29597  */
29598
29599 Roo.bootstrap.DocumentViewer = function(config){
29600     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29601     
29602     this.addEvents({
29603         /**
29604          * @event initial
29605          * Fire after initEvent
29606          * @param {Roo.bootstrap.DocumentViewer} this
29607          */
29608         "initial" : true,
29609         /**
29610          * @event click
29611          * Fire after click
29612          * @param {Roo.bootstrap.DocumentViewer} this
29613          */
29614         "click" : true,
29615         /**
29616          * @event download
29617          * Fire after download button
29618          * @param {Roo.bootstrap.DocumentViewer} this
29619          */
29620         "download" : true,
29621         /**
29622          * @event trash
29623          * Fire after trash button
29624          * @param {Roo.bootstrap.DocumentViewer} this
29625          */
29626         "trash" : true
29627         
29628     });
29629 };
29630
29631 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29632     
29633     showDownload : true,
29634     
29635     showTrash : true,
29636     
29637     getAutoCreate : function()
29638     {
29639         var cfg = {
29640             tag : 'div',
29641             cls : 'roo-document-viewer',
29642             cn : [
29643                 {
29644                     tag : 'div',
29645                     cls : 'roo-document-viewer-body',
29646                     cn : [
29647                         {
29648                             tag : 'div',
29649                             cls : 'roo-document-viewer-thumb',
29650                             cn : [
29651                                 {
29652                                     tag : 'img',
29653                                     cls : 'roo-document-viewer-image'
29654                                 }
29655                             ]
29656                         }
29657                     ]
29658                 },
29659                 {
29660                     tag : 'div',
29661                     cls : 'roo-document-viewer-footer',
29662                     cn : {
29663                         tag : 'div',
29664                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29665                         cn : [
29666                             {
29667                                 tag : 'div',
29668                                 cls : 'btn-group roo-document-viewer-download',
29669                                 cn : [
29670                                     {
29671                                         tag : 'button',
29672                                         cls : 'btn btn-default',
29673                                         html : '<i class="fa fa-download"></i>'
29674                                     }
29675                                 ]
29676                             },
29677                             {
29678                                 tag : 'div',
29679                                 cls : 'btn-group roo-document-viewer-trash',
29680                                 cn : [
29681                                     {
29682                                         tag : 'button',
29683                                         cls : 'btn btn-default',
29684                                         html : '<i class="fa fa-trash"></i>'
29685                                     }
29686                                 ]
29687                             }
29688                         ]
29689                     }
29690                 }
29691             ]
29692         };
29693         
29694         return cfg;
29695     },
29696     
29697     initEvents : function()
29698     {
29699         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29700         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29701         
29702         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29703         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29704         
29705         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29706         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29707         
29708         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29709         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29710         
29711         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29712         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29713         
29714         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29715         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29716         
29717         this.bodyEl.on('click', this.onClick, this);
29718         this.downloadBtn.on('click', this.onDownload, this);
29719         this.trashBtn.on('click', this.onTrash, this);
29720         
29721         this.downloadBtn.hide();
29722         this.trashBtn.hide();
29723         
29724         if(this.showDownload){
29725             this.downloadBtn.show();
29726         }
29727         
29728         if(this.showTrash){
29729             this.trashBtn.show();
29730         }
29731         
29732         if(!this.showDownload && !this.showTrash) {
29733             this.footerEl.hide();
29734         }
29735         
29736     },
29737     
29738     initial : function()
29739     {
29740         this.fireEvent('initial', this);
29741         
29742     },
29743     
29744     onClick : function(e)
29745     {
29746         e.preventDefault();
29747         
29748         this.fireEvent('click', this);
29749     },
29750     
29751     onDownload : function(e)
29752     {
29753         e.preventDefault();
29754         
29755         this.fireEvent('download', this);
29756     },
29757     
29758     onTrash : function(e)
29759     {
29760         e.preventDefault();
29761         
29762         this.fireEvent('trash', this);
29763     }
29764     
29765 });
29766 /*
29767  * - LGPL
29768  *
29769  * nav progress bar
29770  * 
29771  */
29772
29773 /**
29774  * @class Roo.bootstrap.NavProgressBar
29775  * @extends Roo.bootstrap.Component
29776  * Bootstrap NavProgressBar class
29777  * 
29778  * @constructor
29779  * Create a new nav progress bar
29780  * @param {Object} config The config object
29781  */
29782
29783 Roo.bootstrap.NavProgressBar = function(config){
29784     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29785
29786     this.bullets = this.bullets || [];
29787    
29788 //    Roo.bootstrap.NavProgressBar.register(this);
29789      this.addEvents({
29790         /**
29791              * @event changed
29792              * Fires when the active item changes
29793              * @param {Roo.bootstrap.NavProgressBar} this
29794              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29795              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29796          */
29797         'changed': true
29798      });
29799     
29800 };
29801
29802 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29803     
29804     bullets : [],
29805     barItems : [],
29806     
29807     getAutoCreate : function()
29808     {
29809         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29810         
29811         cfg = {
29812             tag : 'div',
29813             cls : 'roo-navigation-bar-group',
29814             cn : [
29815                 {
29816                     tag : 'div',
29817                     cls : 'roo-navigation-top-bar'
29818                 },
29819                 {
29820                     tag : 'div',
29821                     cls : 'roo-navigation-bullets-bar',
29822                     cn : [
29823                         {
29824                             tag : 'ul',
29825                             cls : 'roo-navigation-bar'
29826                         }
29827                     ]
29828                 },
29829                 
29830                 {
29831                     tag : 'div',
29832                     cls : 'roo-navigation-bottom-bar'
29833                 }
29834             ]
29835             
29836         };
29837         
29838         return cfg;
29839         
29840     },
29841     
29842     initEvents: function() 
29843     {
29844         
29845     },
29846     
29847     onRender : function(ct, position) 
29848     {
29849         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29850         
29851         if(this.bullets.length){
29852             Roo.each(this.bullets, function(b){
29853                this.addItem(b);
29854             }, this);
29855         }
29856         
29857         this.format();
29858         
29859     },
29860     
29861     addItem : function(cfg)
29862     {
29863         var item = new Roo.bootstrap.NavProgressItem(cfg);
29864         
29865         item.parentId = this.id;
29866         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29867         
29868         if(cfg.html){
29869             var top = new Roo.bootstrap.Element({
29870                 tag : 'div',
29871                 cls : 'roo-navigation-bar-text'
29872             });
29873             
29874             var bottom = new Roo.bootstrap.Element({
29875                 tag : 'div',
29876                 cls : 'roo-navigation-bar-text'
29877             });
29878             
29879             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29880             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29881             
29882             var topText = new Roo.bootstrap.Element({
29883                 tag : 'span',
29884                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29885             });
29886             
29887             var bottomText = new Roo.bootstrap.Element({
29888                 tag : 'span',
29889                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29890             });
29891             
29892             topText.onRender(top.el, null);
29893             bottomText.onRender(bottom.el, null);
29894             
29895             item.topEl = top;
29896             item.bottomEl = bottom;
29897         }
29898         
29899         this.barItems.push(item);
29900         
29901         return item;
29902     },
29903     
29904     getActive : function()
29905     {
29906         var active = false;
29907         
29908         Roo.each(this.barItems, function(v){
29909             
29910             if (!v.isActive()) {
29911                 return;
29912             }
29913             
29914             active = v;
29915             return false;
29916             
29917         });
29918         
29919         return active;
29920     },
29921     
29922     setActiveItem : function(item)
29923     {
29924         var prev = false;
29925         
29926         Roo.each(this.barItems, function(v){
29927             if (v.rid == item.rid) {
29928                 return ;
29929             }
29930             
29931             if (v.isActive()) {
29932                 v.setActive(false);
29933                 prev = v;
29934             }
29935         });
29936
29937         item.setActive(true);
29938         
29939         this.fireEvent('changed', this, item, prev);
29940     },
29941     
29942     getBarItem: function(rid)
29943     {
29944         var ret = false;
29945         
29946         Roo.each(this.barItems, function(e) {
29947             if (e.rid != rid) {
29948                 return;
29949             }
29950             
29951             ret =  e;
29952             return false;
29953         });
29954         
29955         return ret;
29956     },
29957     
29958     indexOfItem : function(item)
29959     {
29960         var index = false;
29961         
29962         Roo.each(this.barItems, function(v, i){
29963             
29964             if (v.rid != item.rid) {
29965                 return;
29966             }
29967             
29968             index = i;
29969             return false
29970         });
29971         
29972         return index;
29973     },
29974     
29975     setActiveNext : function()
29976     {
29977         var i = this.indexOfItem(this.getActive());
29978         
29979         if (i > this.barItems.length) {
29980             return;
29981         }
29982         
29983         this.setActiveItem(this.barItems[i+1]);
29984     },
29985     
29986     setActivePrev : function()
29987     {
29988         var i = this.indexOfItem(this.getActive());
29989         
29990         if (i  < 1) {
29991             return;
29992         }
29993         
29994         this.setActiveItem(this.barItems[i-1]);
29995     },
29996     
29997     format : function()
29998     {
29999         if(!this.barItems.length){
30000             return;
30001         }
30002      
30003         var width = 100 / this.barItems.length;
30004         
30005         Roo.each(this.barItems, function(i){
30006             i.el.setStyle('width', width + '%');
30007             i.topEl.el.setStyle('width', width + '%');
30008             i.bottomEl.el.setStyle('width', width + '%');
30009         }, this);
30010         
30011     }
30012     
30013 });
30014 /*
30015  * - LGPL
30016  *
30017  * Nav Progress Item
30018  * 
30019  */
30020
30021 /**
30022  * @class Roo.bootstrap.NavProgressItem
30023  * @extends Roo.bootstrap.Component
30024  * Bootstrap NavProgressItem class
30025  * @cfg {String} rid the reference id
30026  * @cfg {Boolean} active (true|false) Is item active default false
30027  * @cfg {Boolean} disabled (true|false) Is item active default false
30028  * @cfg {String} html
30029  * @cfg {String} position (top|bottom) text position default bottom
30030  * @cfg {String} icon show icon instead of number
30031  * 
30032  * @constructor
30033  * Create a new NavProgressItem
30034  * @param {Object} config The config object
30035  */
30036 Roo.bootstrap.NavProgressItem = function(config){
30037     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30038     this.addEvents({
30039         // raw events
30040         /**
30041          * @event click
30042          * The raw click event for the entire grid.
30043          * @param {Roo.bootstrap.NavProgressItem} this
30044          * @param {Roo.EventObject} e
30045          */
30046         "click" : true
30047     });
30048    
30049 };
30050
30051 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30052     
30053     rid : '',
30054     active : false,
30055     disabled : false,
30056     html : '',
30057     position : 'bottom',
30058     icon : false,
30059     
30060     getAutoCreate : function()
30061     {
30062         var iconCls = 'roo-navigation-bar-item-icon';
30063         
30064         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30065         
30066         var cfg = {
30067             tag: 'li',
30068             cls: 'roo-navigation-bar-item',
30069             cn : [
30070                 {
30071                     tag : 'i',
30072                     cls : iconCls
30073                 }
30074             ]
30075         };
30076         
30077         if(this.active){
30078             cfg.cls += ' active';
30079         }
30080         if(this.disabled){
30081             cfg.cls += ' disabled';
30082         }
30083         
30084         return cfg;
30085     },
30086     
30087     disable : function()
30088     {
30089         this.setDisabled(true);
30090     },
30091     
30092     enable : function()
30093     {
30094         this.setDisabled(false);
30095     },
30096     
30097     initEvents: function() 
30098     {
30099         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30100         
30101         this.iconEl.on('click', this.onClick, this);
30102     },
30103     
30104     onClick : function(e)
30105     {
30106         e.preventDefault();
30107         
30108         if(this.disabled){
30109             return;
30110         }
30111         
30112         if(this.fireEvent('click', this, e) === false){
30113             return;
30114         };
30115         
30116         this.parent().setActiveItem(this);
30117     },
30118     
30119     isActive: function () 
30120     {
30121         return this.active;
30122     },
30123     
30124     setActive : function(state)
30125     {
30126         if(this.active == state){
30127             return;
30128         }
30129         
30130         this.active = state;
30131         
30132         if (state) {
30133             this.el.addClass('active');
30134             return;
30135         }
30136         
30137         this.el.removeClass('active');
30138         
30139         return;
30140     },
30141     
30142     setDisabled : function(state)
30143     {
30144         if(this.disabled == state){
30145             return;
30146         }
30147         
30148         this.disabled = state;
30149         
30150         if (state) {
30151             this.el.addClass('disabled');
30152             return;
30153         }
30154         
30155         this.el.removeClass('disabled');
30156     },
30157     
30158     tooltipEl : function()
30159     {
30160         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30161     }
30162 });
30163  
30164
30165  /*
30166  * - LGPL
30167  *
30168  * FieldLabel
30169  * 
30170  */
30171
30172 /**
30173  * @class Roo.bootstrap.FieldLabel
30174  * @extends Roo.bootstrap.Component
30175  * Bootstrap FieldLabel class
30176  * @cfg {String} html contents of the element
30177  * @cfg {String} tag tag of the element default label
30178  * @cfg {String} cls class of the element
30179  * @cfg {String} target label target 
30180  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30181  * @cfg {String} invalidClass default "text-warning"
30182  * @cfg {String} validClass default "text-success"
30183  * @cfg {String} iconTooltip default "This field is required"
30184  * @cfg {String} indicatorpos (left|right) default left
30185  * 
30186  * @constructor
30187  * Create a new FieldLabel
30188  * @param {Object} config The config object
30189  */
30190
30191 Roo.bootstrap.FieldLabel = function(config){
30192     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30193     
30194     this.addEvents({
30195             /**
30196              * @event invalid
30197              * Fires after the field has been marked as invalid.
30198              * @param {Roo.form.FieldLabel} this
30199              * @param {String} msg The validation message
30200              */
30201             invalid : true,
30202             /**
30203              * @event valid
30204              * Fires after the field has been validated with no errors.
30205              * @param {Roo.form.FieldLabel} this
30206              */
30207             valid : true
30208         });
30209 };
30210
30211 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30212     
30213     tag: 'label',
30214     cls: '',
30215     html: '',
30216     target: '',
30217     allowBlank : true,
30218     invalidClass : 'has-warning',
30219     validClass : 'has-success',
30220     iconTooltip : 'This field is required',
30221     indicatorpos : 'left',
30222     
30223     getAutoCreate : function(){
30224         
30225         var cls = "";
30226         if (!this.allowBlank) {
30227             cls  = "visible";
30228         }
30229         
30230         var cfg = {
30231             tag : this.tag,
30232             cls : 'roo-bootstrap-field-label ' + this.cls,
30233             for : this.target,
30234             cn : [
30235                 {
30236                     tag : 'i',
30237                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30238                     tooltip : this.iconTooltip
30239                 },
30240                 {
30241                     tag : 'span',
30242                     html : this.html
30243                 }
30244             ] 
30245         };
30246         
30247         if(this.indicatorpos == 'right'){
30248             var cfg = {
30249                 tag : this.tag,
30250                 cls : 'roo-bootstrap-field-label ' + this.cls,
30251                 for : this.target,
30252                 cn : [
30253                     {
30254                         tag : 'span',
30255                         html : this.html
30256                     },
30257                     {
30258                         tag : 'i',
30259                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30260                         tooltip : this.iconTooltip
30261                     }
30262                 ] 
30263             };
30264         }
30265         
30266         return cfg;
30267     },
30268     
30269     initEvents: function() 
30270     {
30271         Roo.bootstrap.Element.superclass.initEvents.call(this);
30272         
30273         this.indicator = this.indicatorEl();
30274         
30275         if(this.indicator){
30276             this.indicator.removeClass('visible');
30277             this.indicator.addClass('invisible');
30278         }
30279         
30280         Roo.bootstrap.FieldLabel.register(this);
30281     },
30282     
30283     indicatorEl : function()
30284     {
30285         var indicator = this.el.select('i.roo-required-indicator',true).first();
30286         
30287         if(!indicator){
30288             return false;
30289         }
30290         
30291         return indicator;
30292         
30293     },
30294     
30295     /**
30296      * Mark this field as valid
30297      */
30298     markValid : function()
30299     {
30300         if(this.indicator){
30301             this.indicator.removeClass('visible');
30302             this.indicator.addClass('invisible');
30303         }
30304         
30305         this.el.removeClass(this.invalidClass);
30306         
30307         this.el.addClass(this.validClass);
30308         
30309         this.fireEvent('valid', this);
30310     },
30311     
30312     /**
30313      * Mark this field as invalid
30314      * @param {String} msg The validation message
30315      */
30316     markInvalid : function(msg)
30317     {
30318         if(this.indicator){
30319             this.indicator.removeClass('invisible');
30320             this.indicator.addClass('visible');
30321         }
30322         
30323         this.el.removeClass(this.validClass);
30324         
30325         this.el.addClass(this.invalidClass);
30326         
30327         this.fireEvent('invalid', this, msg);
30328     }
30329     
30330    
30331 });
30332
30333 Roo.apply(Roo.bootstrap.FieldLabel, {
30334     
30335     groups: {},
30336     
30337      /**
30338     * register a FieldLabel Group
30339     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30340     */
30341     register : function(label)
30342     {
30343         if(this.groups.hasOwnProperty(label.target)){
30344             return;
30345         }
30346      
30347         this.groups[label.target] = label;
30348         
30349     },
30350     /**
30351     * fetch a FieldLabel Group based on the target
30352     * @param {string} target
30353     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30354     */
30355     get: function(target) {
30356         if (typeof(this.groups[target]) == 'undefined') {
30357             return false;
30358         }
30359         
30360         return this.groups[target] ;
30361     }
30362 });
30363
30364  
30365
30366  /*
30367  * - LGPL
30368  *
30369  * page DateSplitField.
30370  * 
30371  */
30372
30373
30374 /**
30375  * @class Roo.bootstrap.DateSplitField
30376  * @extends Roo.bootstrap.Component
30377  * Bootstrap DateSplitField class
30378  * @cfg {string} fieldLabel - the label associated
30379  * @cfg {Number} labelWidth set the width of label (0-12)
30380  * @cfg {String} labelAlign (top|left)
30381  * @cfg {Boolean} dayAllowBlank (true|false) default false
30382  * @cfg {Boolean} monthAllowBlank (true|false) default false
30383  * @cfg {Boolean} yearAllowBlank (true|false) default false
30384  * @cfg {string} dayPlaceholder 
30385  * @cfg {string} monthPlaceholder
30386  * @cfg {string} yearPlaceholder
30387  * @cfg {string} dayFormat default 'd'
30388  * @cfg {string} monthFormat default 'm'
30389  * @cfg {string} yearFormat default 'Y'
30390  * @cfg {Number} labellg set the width of label (1-12)
30391  * @cfg {Number} labelmd set the width of label (1-12)
30392  * @cfg {Number} labelsm set the width of label (1-12)
30393  * @cfg {Number} labelxs set the width of label (1-12)
30394
30395  *     
30396  * @constructor
30397  * Create a new DateSplitField
30398  * @param {Object} config The config object
30399  */
30400
30401 Roo.bootstrap.DateSplitField = function(config){
30402     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30403     
30404     this.addEvents({
30405         // raw events
30406          /**
30407          * @event years
30408          * getting the data of years
30409          * @param {Roo.bootstrap.DateSplitField} this
30410          * @param {Object} years
30411          */
30412         "years" : true,
30413         /**
30414          * @event days
30415          * getting the data of days
30416          * @param {Roo.bootstrap.DateSplitField} this
30417          * @param {Object} days
30418          */
30419         "days" : true,
30420         /**
30421          * @event invalid
30422          * Fires after the field has been marked as invalid.
30423          * @param {Roo.form.Field} this
30424          * @param {String} msg The validation message
30425          */
30426         invalid : true,
30427        /**
30428          * @event valid
30429          * Fires after the field has been validated with no errors.
30430          * @param {Roo.form.Field} this
30431          */
30432         valid : true
30433     });
30434 };
30435
30436 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30437     
30438     fieldLabel : '',
30439     labelAlign : 'top',
30440     labelWidth : 3,
30441     dayAllowBlank : false,
30442     monthAllowBlank : false,
30443     yearAllowBlank : false,
30444     dayPlaceholder : '',
30445     monthPlaceholder : '',
30446     yearPlaceholder : '',
30447     dayFormat : 'd',
30448     monthFormat : 'm',
30449     yearFormat : 'Y',
30450     isFormField : true,
30451     labellg : 0,
30452     labelmd : 0,
30453     labelsm : 0,
30454     labelxs : 0,
30455     
30456     getAutoCreate : function()
30457     {
30458         var cfg = {
30459             tag : 'div',
30460             cls : 'row roo-date-split-field-group',
30461             cn : [
30462                 {
30463                     tag : 'input',
30464                     type : 'hidden',
30465                     cls : 'form-hidden-field roo-date-split-field-group-value',
30466                     name : this.name
30467                 }
30468             ]
30469         };
30470         
30471         var labelCls = 'col-md-12';
30472         var contentCls = 'col-md-4';
30473         
30474         if(this.fieldLabel){
30475             
30476             var label = {
30477                 tag : 'div',
30478                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30479                 cn : [
30480                     {
30481                         tag : 'label',
30482                         html : this.fieldLabel
30483                     }
30484                 ]
30485             };
30486             
30487             if(this.labelAlign == 'left'){
30488             
30489                 if(this.labelWidth > 12){
30490                     label.style = "width: " + this.labelWidth + 'px';
30491                 }
30492
30493                 if(this.labelWidth < 13 && this.labelmd == 0){
30494                     this.labelmd = this.labelWidth;
30495                 }
30496
30497                 if(this.labellg > 0){
30498                     labelCls = ' col-lg-' + this.labellg;
30499                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30500                 }
30501
30502                 if(this.labelmd > 0){
30503                     labelCls = ' col-md-' + this.labelmd;
30504                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30505                 }
30506
30507                 if(this.labelsm > 0){
30508                     labelCls = ' col-sm-' + this.labelsm;
30509                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30510                 }
30511
30512                 if(this.labelxs > 0){
30513                     labelCls = ' col-xs-' + this.labelxs;
30514                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30515                 }
30516             }
30517             
30518             label.cls += ' ' + labelCls;
30519             
30520             cfg.cn.push(label);
30521         }
30522         
30523         Roo.each(['day', 'month', 'year'], function(t){
30524             cfg.cn.push({
30525                 tag : 'div',
30526                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30527             });
30528         }, this);
30529         
30530         return cfg;
30531     },
30532     
30533     inputEl: function ()
30534     {
30535         return this.el.select('.roo-date-split-field-group-value', true).first();
30536     },
30537     
30538     onRender : function(ct, position) 
30539     {
30540         var _this = this;
30541         
30542         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30543         
30544         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30545         
30546         this.dayField = new Roo.bootstrap.ComboBox({
30547             allowBlank : this.dayAllowBlank,
30548             alwaysQuery : true,
30549             displayField : 'value',
30550             editable : false,
30551             fieldLabel : '',
30552             forceSelection : true,
30553             mode : 'local',
30554             placeholder : this.dayPlaceholder,
30555             selectOnFocus : true,
30556             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30557             triggerAction : 'all',
30558             typeAhead : true,
30559             valueField : 'value',
30560             store : new Roo.data.SimpleStore({
30561                 data : (function() {    
30562                     var days = [];
30563                     _this.fireEvent('days', _this, days);
30564                     return days;
30565                 })(),
30566                 fields : [ 'value' ]
30567             }),
30568             listeners : {
30569                 select : function (_self, record, index)
30570                 {
30571                     _this.setValue(_this.getValue());
30572                 }
30573             }
30574         });
30575
30576         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30577         
30578         this.monthField = new Roo.bootstrap.MonthField({
30579             after : '<i class=\"fa fa-calendar\"></i>',
30580             allowBlank : this.monthAllowBlank,
30581             placeholder : this.monthPlaceholder,
30582             readOnly : true,
30583             listeners : {
30584                 render : function (_self)
30585                 {
30586                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30587                         e.preventDefault();
30588                         _self.focus();
30589                     });
30590                 },
30591                 select : function (_self, oldvalue, newvalue)
30592                 {
30593                     _this.setValue(_this.getValue());
30594                 }
30595             }
30596         });
30597         
30598         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30599         
30600         this.yearField = new Roo.bootstrap.ComboBox({
30601             allowBlank : this.yearAllowBlank,
30602             alwaysQuery : true,
30603             displayField : 'value',
30604             editable : false,
30605             fieldLabel : '',
30606             forceSelection : true,
30607             mode : 'local',
30608             placeholder : this.yearPlaceholder,
30609             selectOnFocus : true,
30610             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30611             triggerAction : 'all',
30612             typeAhead : true,
30613             valueField : 'value',
30614             store : new Roo.data.SimpleStore({
30615                 data : (function() {
30616                     var years = [];
30617                     _this.fireEvent('years', _this, years);
30618                     return years;
30619                 })(),
30620                 fields : [ 'value' ]
30621             }),
30622             listeners : {
30623                 select : function (_self, record, index)
30624                 {
30625                     _this.setValue(_this.getValue());
30626                 }
30627             }
30628         });
30629
30630         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30631     },
30632     
30633     setValue : function(v, format)
30634     {
30635         this.inputEl.dom.value = v;
30636         
30637         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30638         
30639         var d = Date.parseDate(v, f);
30640         
30641         if(!d){
30642             this.validate();
30643             return;
30644         }
30645         
30646         this.setDay(d.format(this.dayFormat));
30647         this.setMonth(d.format(this.monthFormat));
30648         this.setYear(d.format(this.yearFormat));
30649         
30650         this.validate();
30651         
30652         return;
30653     },
30654     
30655     setDay : function(v)
30656     {
30657         this.dayField.setValue(v);
30658         this.inputEl.dom.value = this.getValue();
30659         this.validate();
30660         return;
30661     },
30662     
30663     setMonth : function(v)
30664     {
30665         this.monthField.setValue(v, true);
30666         this.inputEl.dom.value = this.getValue();
30667         this.validate();
30668         return;
30669     },
30670     
30671     setYear : function(v)
30672     {
30673         this.yearField.setValue(v);
30674         this.inputEl.dom.value = this.getValue();
30675         this.validate();
30676         return;
30677     },
30678     
30679     getDay : function()
30680     {
30681         return this.dayField.getValue();
30682     },
30683     
30684     getMonth : function()
30685     {
30686         return this.monthField.getValue();
30687     },
30688     
30689     getYear : function()
30690     {
30691         return this.yearField.getValue();
30692     },
30693     
30694     getValue : function()
30695     {
30696         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30697         
30698         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30699         
30700         return date;
30701     },
30702     
30703     reset : function()
30704     {
30705         this.setDay('');
30706         this.setMonth('');
30707         this.setYear('');
30708         this.inputEl.dom.value = '';
30709         this.validate();
30710         return;
30711     },
30712     
30713     validate : function()
30714     {
30715         var d = this.dayField.validate();
30716         var m = this.monthField.validate();
30717         var y = this.yearField.validate();
30718         
30719         var valid = true;
30720         
30721         if(
30722                 (!this.dayAllowBlank && !d) ||
30723                 (!this.monthAllowBlank && !m) ||
30724                 (!this.yearAllowBlank && !y)
30725         ){
30726             valid = false;
30727         }
30728         
30729         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30730             return valid;
30731         }
30732         
30733         if(valid){
30734             this.markValid();
30735             return valid;
30736         }
30737         
30738         this.markInvalid();
30739         
30740         return valid;
30741     },
30742     
30743     markValid : function()
30744     {
30745         
30746         var label = this.el.select('label', true).first();
30747         var icon = this.el.select('i.fa-star', true).first();
30748
30749         if(label && icon){
30750             icon.remove();
30751         }
30752         
30753         this.fireEvent('valid', this);
30754     },
30755     
30756      /**
30757      * Mark this field as invalid
30758      * @param {String} msg The validation message
30759      */
30760     markInvalid : function(msg)
30761     {
30762         
30763         var label = this.el.select('label', true).first();
30764         var icon = this.el.select('i.fa-star', true).first();
30765
30766         if(label && !icon){
30767             this.el.select('.roo-date-split-field-label', true).createChild({
30768                 tag : 'i',
30769                 cls : 'text-danger fa fa-lg fa-star',
30770                 tooltip : 'This field is required',
30771                 style : 'margin-right:5px;'
30772             }, label, true);
30773         }
30774         
30775         this.fireEvent('invalid', this, msg);
30776     },
30777     
30778     clearInvalid : function()
30779     {
30780         var label = this.el.select('label', true).first();
30781         var icon = this.el.select('i.fa-star', true).first();
30782
30783         if(label && icon){
30784             icon.remove();
30785         }
30786         
30787         this.fireEvent('valid', this);
30788     },
30789     
30790     getName: function()
30791     {
30792         return this.name;
30793     }
30794     
30795 });
30796
30797  /**
30798  *
30799  * This is based on 
30800  * http://masonry.desandro.com
30801  *
30802  * The idea is to render all the bricks based on vertical width...
30803  *
30804  * The original code extends 'outlayer' - we might need to use that....
30805  * 
30806  */
30807
30808
30809 /**
30810  * @class Roo.bootstrap.LayoutMasonry
30811  * @extends Roo.bootstrap.Component
30812  * Bootstrap Layout Masonry class
30813  * 
30814  * @constructor
30815  * Create a new Element
30816  * @param {Object} config The config object
30817  */
30818
30819 Roo.bootstrap.LayoutMasonry = function(config){
30820     
30821     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30822     
30823     this.bricks = [];
30824     
30825     Roo.bootstrap.LayoutMasonry.register(this);
30826     
30827     this.addEvents({
30828         // raw events
30829         /**
30830          * @event layout
30831          * Fire after layout the items
30832          * @param {Roo.bootstrap.LayoutMasonry} this
30833          * @param {Roo.EventObject} e
30834          */
30835         "layout" : true
30836     });
30837     
30838 };
30839
30840 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30841     
30842     /**
30843      * @cfg {Boolean} isLayoutInstant = no animation?
30844      */   
30845     isLayoutInstant : false, // needed?
30846    
30847     /**
30848      * @cfg {Number} boxWidth  width of the columns
30849      */   
30850     boxWidth : 450,
30851     
30852       /**
30853      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30854      */   
30855     boxHeight : 0,
30856     
30857     /**
30858      * @cfg {Number} padWidth padding below box..
30859      */   
30860     padWidth : 10, 
30861     
30862     /**
30863      * @cfg {Number} gutter gutter width..
30864      */   
30865     gutter : 10,
30866     
30867      /**
30868      * @cfg {Number} maxCols maximum number of columns
30869      */   
30870     
30871     maxCols: 0,
30872     
30873     /**
30874      * @cfg {Boolean} isAutoInitial defalut true
30875      */   
30876     isAutoInitial : true, 
30877     
30878     containerWidth: 0,
30879     
30880     /**
30881      * @cfg {Boolean} isHorizontal defalut false
30882      */   
30883     isHorizontal : false, 
30884
30885     currentSize : null,
30886     
30887     tag: 'div',
30888     
30889     cls: '',
30890     
30891     bricks: null, //CompositeElement
30892     
30893     cols : 1,
30894     
30895     _isLayoutInited : false,
30896     
30897 //    isAlternative : false, // only use for vertical layout...
30898     
30899     /**
30900      * @cfg {Number} alternativePadWidth padding below box..
30901      */   
30902     alternativePadWidth : 50,
30903     
30904     selectedBrick : [],
30905     
30906     getAutoCreate : function(){
30907         
30908         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30909         
30910         var cfg = {
30911             tag: this.tag,
30912             cls: 'blog-masonary-wrapper ' + this.cls,
30913             cn : {
30914                 cls : 'mas-boxes masonary'
30915             }
30916         };
30917         
30918         return cfg;
30919     },
30920     
30921     getChildContainer: function( )
30922     {
30923         if (this.boxesEl) {
30924             return this.boxesEl;
30925         }
30926         
30927         this.boxesEl = this.el.select('.mas-boxes').first();
30928         
30929         return this.boxesEl;
30930     },
30931     
30932     
30933     initEvents : function()
30934     {
30935         var _this = this;
30936         
30937         if(this.isAutoInitial){
30938             Roo.log('hook children rendered');
30939             this.on('childrenrendered', function() {
30940                 Roo.log('children rendered');
30941                 _this.initial();
30942             } ,this);
30943         }
30944     },
30945     
30946     initial : function()
30947     {
30948         this.selectedBrick = [];
30949         
30950         this.currentSize = this.el.getBox(true);
30951         
30952         Roo.EventManager.onWindowResize(this.resize, this); 
30953
30954         if(!this.isAutoInitial){
30955             this.layout();
30956             return;
30957         }
30958         
30959         this.layout();
30960         
30961         return;
30962         //this.layout.defer(500,this);
30963         
30964     },
30965     
30966     resize : function()
30967     {
30968         var cs = this.el.getBox(true);
30969         
30970         if (
30971                 this.currentSize.width == cs.width && 
30972                 this.currentSize.x == cs.x && 
30973                 this.currentSize.height == cs.height && 
30974                 this.currentSize.y == cs.y 
30975         ) {
30976             Roo.log("no change in with or X or Y");
30977             return;
30978         }
30979         
30980         this.currentSize = cs;
30981         
30982         this.layout();
30983         
30984     },
30985     
30986     layout : function()
30987     {   
30988         this._resetLayout();
30989         
30990         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30991         
30992         this.layoutItems( isInstant );
30993       
30994         this._isLayoutInited = true;
30995         
30996         this.fireEvent('layout', this);
30997         
30998     },
30999     
31000     _resetLayout : function()
31001     {
31002         if(this.isHorizontal){
31003             this.horizontalMeasureColumns();
31004             return;
31005         }
31006         
31007         this.verticalMeasureColumns();
31008         
31009     },
31010     
31011     verticalMeasureColumns : function()
31012     {
31013         this.getContainerWidth();
31014         
31015 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31016 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31017 //            return;
31018 //        }
31019         
31020         var boxWidth = this.boxWidth + this.padWidth;
31021         
31022         if(this.containerWidth < this.boxWidth){
31023             boxWidth = this.containerWidth
31024         }
31025         
31026         var containerWidth = this.containerWidth;
31027         
31028         var cols = Math.floor(containerWidth / boxWidth);
31029         
31030         this.cols = Math.max( cols, 1 );
31031         
31032         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31033         
31034         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31035         
31036         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31037         
31038         this.colWidth = boxWidth + avail - this.padWidth;
31039         
31040         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31041         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31042     },
31043     
31044     horizontalMeasureColumns : function()
31045     {
31046         this.getContainerWidth();
31047         
31048         var boxWidth = this.boxWidth;
31049         
31050         if(this.containerWidth < boxWidth){
31051             boxWidth = this.containerWidth;
31052         }
31053         
31054         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31055         
31056         this.el.setHeight(boxWidth);
31057         
31058     },
31059     
31060     getContainerWidth : function()
31061     {
31062         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31063     },
31064     
31065     layoutItems : function( isInstant )
31066     {
31067         Roo.log(this.bricks);
31068         
31069         var items = Roo.apply([], this.bricks);
31070         
31071         if(this.isHorizontal){
31072             this._horizontalLayoutItems( items , isInstant );
31073             return;
31074         }
31075         
31076 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31077 //            this._verticalAlternativeLayoutItems( items , isInstant );
31078 //            return;
31079 //        }
31080         
31081         this._verticalLayoutItems( items , isInstant );
31082         
31083     },
31084     
31085     _verticalLayoutItems : function ( items , isInstant)
31086     {
31087         if ( !items || !items.length ) {
31088             return;
31089         }
31090         
31091         var standard = [
31092             ['xs', 'xs', 'xs', 'tall'],
31093             ['xs', 'xs', 'tall'],
31094             ['xs', 'xs', 'sm'],
31095             ['xs', 'xs', 'xs'],
31096             ['xs', 'tall'],
31097             ['xs', 'sm'],
31098             ['xs', 'xs'],
31099             ['xs'],
31100             
31101             ['sm', 'xs', 'xs'],
31102             ['sm', 'xs'],
31103             ['sm'],
31104             
31105             ['tall', 'xs', 'xs', 'xs'],
31106             ['tall', 'xs', 'xs'],
31107             ['tall', 'xs'],
31108             ['tall']
31109             
31110         ];
31111         
31112         var queue = [];
31113         
31114         var boxes = [];
31115         
31116         var box = [];
31117         
31118         Roo.each(items, function(item, k){
31119             
31120             switch (item.size) {
31121                 // these layouts take up a full box,
31122                 case 'md' :
31123                 case 'md-left' :
31124                 case 'md-right' :
31125                 case 'wide' :
31126                     
31127                     if(box.length){
31128                         boxes.push(box);
31129                         box = [];
31130                     }
31131                     
31132                     boxes.push([item]);
31133                     
31134                     break;
31135                     
31136                 case 'xs' :
31137                 case 'sm' :
31138                 case 'tall' :
31139                     
31140                     box.push(item);
31141                     
31142                     break;
31143                 default :
31144                     break;
31145                     
31146             }
31147             
31148         }, this);
31149         
31150         if(box.length){
31151             boxes.push(box);
31152             box = [];
31153         }
31154         
31155         var filterPattern = function(box, length)
31156         {
31157             if(!box.length){
31158                 return;
31159             }
31160             
31161             var match = false;
31162             
31163             var pattern = box.slice(0, length);
31164             
31165             var format = [];
31166             
31167             Roo.each(pattern, function(i){
31168                 format.push(i.size);
31169             }, this);
31170             
31171             Roo.each(standard, function(s){
31172                 
31173                 if(String(s) != String(format)){
31174                     return;
31175                 }
31176                 
31177                 match = true;
31178                 return false;
31179                 
31180             }, this);
31181             
31182             if(!match && length == 1){
31183                 return;
31184             }
31185             
31186             if(!match){
31187                 filterPattern(box, length - 1);
31188                 return;
31189             }
31190                 
31191             queue.push(pattern);
31192
31193             box = box.slice(length, box.length);
31194
31195             filterPattern(box, 4);
31196
31197             return;
31198             
31199         }
31200         
31201         Roo.each(boxes, function(box, k){
31202             
31203             if(!box.length){
31204                 return;
31205             }
31206             
31207             if(box.length == 1){
31208                 queue.push(box);
31209                 return;
31210             }
31211             
31212             filterPattern(box, 4);
31213             
31214         }, this);
31215         
31216         this._processVerticalLayoutQueue( queue, isInstant );
31217         
31218     },
31219     
31220 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31221 //    {
31222 //        if ( !items || !items.length ) {
31223 //            return;
31224 //        }
31225 //
31226 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31227 //        
31228 //    },
31229     
31230     _horizontalLayoutItems : function ( items , isInstant)
31231     {
31232         if ( !items || !items.length || items.length < 3) {
31233             return;
31234         }
31235         
31236         items.reverse();
31237         
31238         var eItems = items.slice(0, 3);
31239         
31240         items = items.slice(3, items.length);
31241         
31242         var standard = [
31243             ['xs', 'xs', 'xs', 'wide'],
31244             ['xs', 'xs', 'wide'],
31245             ['xs', 'xs', 'sm'],
31246             ['xs', 'xs', 'xs'],
31247             ['xs', 'wide'],
31248             ['xs', 'sm'],
31249             ['xs', 'xs'],
31250             ['xs'],
31251             
31252             ['sm', 'xs', 'xs'],
31253             ['sm', 'xs'],
31254             ['sm'],
31255             
31256             ['wide', 'xs', 'xs', 'xs'],
31257             ['wide', 'xs', 'xs'],
31258             ['wide', 'xs'],
31259             ['wide'],
31260             
31261             ['wide-thin']
31262         ];
31263         
31264         var queue = [];
31265         
31266         var boxes = [];
31267         
31268         var box = [];
31269         
31270         Roo.each(items, function(item, k){
31271             
31272             switch (item.size) {
31273                 case 'md' :
31274                 case 'md-left' :
31275                 case 'md-right' :
31276                 case 'tall' :
31277                     
31278                     if(box.length){
31279                         boxes.push(box);
31280                         box = [];
31281                     }
31282                     
31283                     boxes.push([item]);
31284                     
31285                     break;
31286                     
31287                 case 'xs' :
31288                 case 'sm' :
31289                 case 'wide' :
31290                 case 'wide-thin' :
31291                     
31292                     box.push(item);
31293                     
31294                     break;
31295                 default :
31296                     break;
31297                     
31298             }
31299             
31300         }, this);
31301         
31302         if(box.length){
31303             boxes.push(box);
31304             box = [];
31305         }
31306         
31307         var filterPattern = function(box, length)
31308         {
31309             if(!box.length){
31310                 return;
31311             }
31312             
31313             var match = false;
31314             
31315             var pattern = box.slice(0, length);
31316             
31317             var format = [];
31318             
31319             Roo.each(pattern, function(i){
31320                 format.push(i.size);
31321             }, this);
31322             
31323             Roo.each(standard, function(s){
31324                 
31325                 if(String(s) != String(format)){
31326                     return;
31327                 }
31328                 
31329                 match = true;
31330                 return false;
31331                 
31332             }, this);
31333             
31334             if(!match && length == 1){
31335                 return;
31336             }
31337             
31338             if(!match){
31339                 filterPattern(box, length - 1);
31340                 return;
31341             }
31342                 
31343             queue.push(pattern);
31344
31345             box = box.slice(length, box.length);
31346
31347             filterPattern(box, 4);
31348
31349             return;
31350             
31351         }
31352         
31353         Roo.each(boxes, function(box, k){
31354             
31355             if(!box.length){
31356                 return;
31357             }
31358             
31359             if(box.length == 1){
31360                 queue.push(box);
31361                 return;
31362             }
31363             
31364             filterPattern(box, 4);
31365             
31366         }, this);
31367         
31368         
31369         var prune = [];
31370         
31371         var pos = this.el.getBox(true);
31372         
31373         var minX = pos.x;
31374         
31375         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31376         
31377         var hit_end = false;
31378         
31379         Roo.each(queue, function(box){
31380             
31381             if(hit_end){
31382                 
31383                 Roo.each(box, function(b){
31384                 
31385                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31386                     b.el.hide();
31387
31388                 }, this);
31389
31390                 return;
31391             }
31392             
31393             var mx = 0;
31394             
31395             Roo.each(box, function(b){
31396                 
31397                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31398                 b.el.show();
31399
31400                 mx = Math.max(mx, b.x);
31401                 
31402             }, this);
31403             
31404             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31405             
31406             if(maxX < minX){
31407                 
31408                 Roo.each(box, function(b){
31409                 
31410                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31411                     b.el.hide();
31412                     
31413                 }, this);
31414                 
31415                 hit_end = true;
31416                 
31417                 return;
31418             }
31419             
31420             prune.push(box);
31421             
31422         }, this);
31423         
31424         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31425     },
31426     
31427     /** Sets position of item in DOM
31428     * @param {Element} item
31429     * @param {Number} x - horizontal position
31430     * @param {Number} y - vertical position
31431     * @param {Boolean} isInstant - disables transitions
31432     */
31433     _processVerticalLayoutQueue : function( queue, isInstant )
31434     {
31435         var pos = this.el.getBox(true);
31436         var x = pos.x;
31437         var y = pos.y;
31438         var maxY = [];
31439         
31440         for (var i = 0; i < this.cols; i++){
31441             maxY[i] = pos.y;
31442         }
31443         
31444         Roo.each(queue, function(box, k){
31445             
31446             var col = k % this.cols;
31447             
31448             Roo.each(box, function(b,kk){
31449                 
31450                 b.el.position('absolute');
31451                 
31452                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31453                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31454                 
31455                 if(b.size == 'md-left' || b.size == 'md-right'){
31456                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31457                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31458                 }
31459                 
31460                 b.el.setWidth(width);
31461                 b.el.setHeight(height);
31462                 // iframe?
31463                 b.el.select('iframe',true).setSize(width,height);
31464                 
31465             }, this);
31466             
31467             for (var i = 0; i < this.cols; i++){
31468                 
31469                 if(maxY[i] < maxY[col]){
31470                     col = i;
31471                     continue;
31472                 }
31473                 
31474                 col = Math.min(col, i);
31475                 
31476             }
31477             
31478             x = pos.x + col * (this.colWidth + this.padWidth);
31479             
31480             y = maxY[col];
31481             
31482             var positions = [];
31483             
31484             switch (box.length){
31485                 case 1 :
31486                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31487                     break;
31488                 case 2 :
31489                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31490                     break;
31491                 case 3 :
31492                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31493                     break;
31494                 case 4 :
31495                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31496                     break;
31497                 default :
31498                     break;
31499             }
31500             
31501             Roo.each(box, function(b,kk){
31502                 
31503                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31504                 
31505                 var sz = b.el.getSize();
31506                 
31507                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31508                 
31509             }, this);
31510             
31511         }, this);
31512         
31513         var mY = 0;
31514         
31515         for (var i = 0; i < this.cols; i++){
31516             mY = Math.max(mY, maxY[i]);
31517         }
31518         
31519         this.el.setHeight(mY - pos.y);
31520         
31521     },
31522     
31523 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31524 //    {
31525 //        var pos = this.el.getBox(true);
31526 //        var x = pos.x;
31527 //        var y = pos.y;
31528 //        var maxX = pos.right;
31529 //        
31530 //        var maxHeight = 0;
31531 //        
31532 //        Roo.each(items, function(item, k){
31533 //            
31534 //            var c = k % 2;
31535 //            
31536 //            item.el.position('absolute');
31537 //                
31538 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31539 //
31540 //            item.el.setWidth(width);
31541 //
31542 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31543 //
31544 //            item.el.setHeight(height);
31545 //            
31546 //            if(c == 0){
31547 //                item.el.setXY([x, y], isInstant ? false : true);
31548 //            } else {
31549 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31550 //            }
31551 //            
31552 //            y = y + height + this.alternativePadWidth;
31553 //            
31554 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31555 //            
31556 //        }, this);
31557 //        
31558 //        this.el.setHeight(maxHeight);
31559 //        
31560 //    },
31561     
31562     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31563     {
31564         var pos = this.el.getBox(true);
31565         
31566         var minX = pos.x;
31567         var minY = pos.y;
31568         
31569         var maxX = pos.right;
31570         
31571         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31572         
31573         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31574         
31575         Roo.each(queue, function(box, k){
31576             
31577             Roo.each(box, function(b, kk){
31578                 
31579                 b.el.position('absolute');
31580                 
31581                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31582                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31583                 
31584                 if(b.size == 'md-left' || b.size == 'md-right'){
31585                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31586                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31587                 }
31588                 
31589                 b.el.setWidth(width);
31590                 b.el.setHeight(height);
31591                 
31592             }, this);
31593             
31594             if(!box.length){
31595                 return;
31596             }
31597             
31598             var positions = [];
31599             
31600             switch (box.length){
31601                 case 1 :
31602                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31603                     break;
31604                 case 2 :
31605                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31606                     break;
31607                 case 3 :
31608                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31609                     break;
31610                 case 4 :
31611                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31612                     break;
31613                 default :
31614                     break;
31615             }
31616             
31617             Roo.each(box, function(b,kk){
31618                 
31619                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31620                 
31621                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31622                 
31623             }, this);
31624             
31625         }, this);
31626         
31627     },
31628     
31629     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31630     {
31631         Roo.each(eItems, function(b,k){
31632             
31633             b.size = (k == 0) ? 'sm' : 'xs';
31634             b.x = (k == 0) ? 2 : 1;
31635             b.y = (k == 0) ? 2 : 1;
31636             
31637             b.el.position('absolute');
31638             
31639             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31640                 
31641             b.el.setWidth(width);
31642             
31643             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31644             
31645             b.el.setHeight(height);
31646             
31647         }, this);
31648
31649         var positions = [];
31650         
31651         positions.push({
31652             x : maxX - this.unitWidth * 2 - this.gutter,
31653             y : minY
31654         });
31655         
31656         positions.push({
31657             x : maxX - this.unitWidth,
31658             y : minY + (this.unitWidth + this.gutter) * 2
31659         });
31660         
31661         positions.push({
31662             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31663             y : minY
31664         });
31665         
31666         Roo.each(eItems, function(b,k){
31667             
31668             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31669
31670         }, this);
31671         
31672     },
31673     
31674     getVerticalOneBoxColPositions : function(x, y, box)
31675     {
31676         var pos = [];
31677         
31678         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31679         
31680         if(box[0].size == 'md-left'){
31681             rand = 0;
31682         }
31683         
31684         if(box[0].size == 'md-right'){
31685             rand = 1;
31686         }
31687         
31688         pos.push({
31689             x : x + (this.unitWidth + this.gutter) * rand,
31690             y : y
31691         });
31692         
31693         return pos;
31694     },
31695     
31696     getVerticalTwoBoxColPositions : function(x, y, box)
31697     {
31698         var pos = [];
31699         
31700         if(box[0].size == 'xs'){
31701             
31702             pos.push({
31703                 x : x,
31704                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31705             });
31706
31707             pos.push({
31708                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31709                 y : y
31710             });
31711             
31712             return pos;
31713             
31714         }
31715         
31716         pos.push({
31717             x : x,
31718             y : y
31719         });
31720
31721         pos.push({
31722             x : x + (this.unitWidth + this.gutter) * 2,
31723             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31724         });
31725         
31726         return pos;
31727         
31728     },
31729     
31730     getVerticalThreeBoxColPositions : function(x, y, box)
31731     {
31732         var pos = [];
31733         
31734         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31735             
31736             pos.push({
31737                 x : x,
31738                 y : y
31739             });
31740
31741             pos.push({
31742                 x : x + (this.unitWidth + this.gutter) * 1,
31743                 y : y
31744             });
31745             
31746             pos.push({
31747                 x : x + (this.unitWidth + this.gutter) * 2,
31748                 y : y
31749             });
31750             
31751             return pos;
31752             
31753         }
31754         
31755         if(box[0].size == 'xs' && box[1].size == 'xs'){
31756             
31757             pos.push({
31758                 x : x,
31759                 y : y
31760             });
31761
31762             pos.push({
31763                 x : x,
31764                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31765             });
31766             
31767             pos.push({
31768                 x : x + (this.unitWidth + this.gutter) * 1,
31769                 y : y
31770             });
31771             
31772             return pos;
31773             
31774         }
31775         
31776         pos.push({
31777             x : x,
31778             y : y
31779         });
31780
31781         pos.push({
31782             x : x + (this.unitWidth + this.gutter) * 2,
31783             y : y
31784         });
31785
31786         pos.push({
31787             x : x + (this.unitWidth + this.gutter) * 2,
31788             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31789         });
31790             
31791         return pos;
31792         
31793     },
31794     
31795     getVerticalFourBoxColPositions : function(x, y, box)
31796     {
31797         var pos = [];
31798         
31799         if(box[0].size == 'xs'){
31800             
31801             pos.push({
31802                 x : x,
31803                 y : y
31804             });
31805
31806             pos.push({
31807                 x : x,
31808                 y : y + (this.unitHeight + this.gutter) * 1
31809             });
31810             
31811             pos.push({
31812                 x : x,
31813                 y : y + (this.unitHeight + this.gutter) * 2
31814             });
31815             
31816             pos.push({
31817                 x : x + (this.unitWidth + this.gutter) * 1,
31818                 y : y
31819             });
31820             
31821             return pos;
31822             
31823         }
31824         
31825         pos.push({
31826             x : x,
31827             y : y
31828         });
31829
31830         pos.push({
31831             x : x + (this.unitWidth + this.gutter) * 2,
31832             y : y
31833         });
31834
31835         pos.push({
31836             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31837             y : y + (this.unitHeight + this.gutter) * 1
31838         });
31839
31840         pos.push({
31841             x : x + (this.unitWidth + this.gutter) * 2,
31842             y : y + (this.unitWidth + this.gutter) * 2
31843         });
31844
31845         return pos;
31846         
31847     },
31848     
31849     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31850     {
31851         var pos = [];
31852         
31853         if(box[0].size == 'md-left'){
31854             pos.push({
31855                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31856                 y : minY
31857             });
31858             
31859             return pos;
31860         }
31861         
31862         if(box[0].size == 'md-right'){
31863             pos.push({
31864                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31865                 y : minY + (this.unitWidth + this.gutter) * 1
31866             });
31867             
31868             return pos;
31869         }
31870         
31871         var rand = Math.floor(Math.random() * (4 - box[0].y));
31872         
31873         pos.push({
31874             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31875             y : minY + (this.unitWidth + this.gutter) * rand
31876         });
31877         
31878         return pos;
31879         
31880     },
31881     
31882     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31883     {
31884         var pos = [];
31885         
31886         if(box[0].size == 'xs'){
31887             
31888             pos.push({
31889                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31890                 y : minY
31891             });
31892
31893             pos.push({
31894                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31895                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31896             });
31897             
31898             return pos;
31899             
31900         }
31901         
31902         pos.push({
31903             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31904             y : minY
31905         });
31906
31907         pos.push({
31908             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31909             y : minY + (this.unitWidth + this.gutter) * 2
31910         });
31911         
31912         return pos;
31913         
31914     },
31915     
31916     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31917     {
31918         var pos = [];
31919         
31920         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31921             
31922             pos.push({
31923                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31924                 y : minY
31925             });
31926
31927             pos.push({
31928                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31929                 y : minY + (this.unitWidth + this.gutter) * 1
31930             });
31931             
31932             pos.push({
31933                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31934                 y : minY + (this.unitWidth + this.gutter) * 2
31935             });
31936             
31937             return pos;
31938             
31939         }
31940         
31941         if(box[0].size == 'xs' && box[1].size == 'xs'){
31942             
31943             pos.push({
31944                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31945                 y : minY
31946             });
31947
31948             pos.push({
31949                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31950                 y : minY
31951             });
31952             
31953             pos.push({
31954                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31955                 y : minY + (this.unitWidth + this.gutter) * 1
31956             });
31957             
31958             return pos;
31959             
31960         }
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) * 2
31970         });
31971
31972         pos.push({
31973             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - 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     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31982     {
31983         var pos = [];
31984         
31985         if(box[0].size == 'xs'){
31986             
31987             pos.push({
31988                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31989                 y : minY
31990             });
31991
31992             pos.push({
31993                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31994                 y : minY
31995             });
31996             
31997             pos.push({
31998                 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),
31999                 y : minY
32000             });
32001             
32002             pos.push({
32003                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32004                 y : minY + (this.unitWidth + this.gutter) * 1
32005             });
32006             
32007             return pos;
32008             
32009         }
32010         
32011         pos.push({
32012             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32013             y : minY
32014         });
32015         
32016         pos.push({
32017             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32018             y : minY + (this.unitWidth + this.gutter) * 2
32019         });
32020         
32021         pos.push({
32022             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32023             y : minY + (this.unitWidth + this.gutter) * 2
32024         });
32025         
32026         pos.push({
32027             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32028             y : minY + (this.unitWidth + this.gutter) * 2
32029         });
32030
32031         return pos;
32032         
32033     },
32034     
32035     /**
32036     * remove a Masonry Brick
32037     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32038     */
32039     removeBrick : function(brick_id)
32040     {
32041         if (!brick_id) {
32042             return;
32043         }
32044         
32045         for (var i = 0; i<this.bricks.length; i++) {
32046             if (this.bricks[i].id == brick_id) {
32047                 this.bricks.splice(i,1);
32048                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32049                 this.initial();
32050             }
32051         }
32052     },
32053     
32054     /**
32055     * adds a Masonry Brick
32056     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32057     */
32058     addBrick : function(cfg)
32059     {
32060         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32061         //this.register(cn);
32062         cn.parentId = this.id;
32063         cn.render(this.el);
32064         return cn;
32065     },
32066     
32067     /**
32068     * register a Masonry Brick
32069     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32070     */
32071     
32072     register : function(brick)
32073     {
32074         this.bricks.push(brick);
32075         brick.masonryId = this.id;
32076     },
32077     
32078     /**
32079     * clear all the Masonry Brick
32080     */
32081     clearAll : function()
32082     {
32083         this.bricks = [];
32084         //this.getChildContainer().dom.innerHTML = "";
32085         this.el.dom.innerHTML = '';
32086     },
32087     
32088     getSelected : function()
32089     {
32090         if (!this.selectedBrick) {
32091             return false;
32092         }
32093         
32094         return this.selectedBrick;
32095     }
32096 });
32097
32098 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32099     
32100     groups: {},
32101      /**
32102     * register a Masonry Layout
32103     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32104     */
32105     
32106     register : function(layout)
32107     {
32108         this.groups[layout.id] = layout;
32109     },
32110     /**
32111     * fetch a  Masonry Layout based on the masonry layout ID
32112     * @param {string} the masonry layout to add
32113     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32114     */
32115     
32116     get: function(layout_id) {
32117         if (typeof(this.groups[layout_id]) == 'undefined') {
32118             return false;
32119         }
32120         return this.groups[layout_id] ;
32121     }
32122     
32123     
32124     
32125 });
32126
32127  
32128
32129  /**
32130  *
32131  * This is based on 
32132  * http://masonry.desandro.com
32133  *
32134  * The idea is to render all the bricks based on vertical width...
32135  *
32136  * The original code extends 'outlayer' - we might need to use that....
32137  * 
32138  */
32139
32140
32141 /**
32142  * @class Roo.bootstrap.LayoutMasonryAuto
32143  * @extends Roo.bootstrap.Component
32144  * Bootstrap Layout Masonry class
32145  * 
32146  * @constructor
32147  * Create a new Element
32148  * @param {Object} config The config object
32149  */
32150
32151 Roo.bootstrap.LayoutMasonryAuto = function(config){
32152     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32153 };
32154
32155 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32156     
32157       /**
32158      * @cfg {Boolean} isFitWidth  - resize the width..
32159      */   
32160     isFitWidth : false,  // options..
32161     /**
32162      * @cfg {Boolean} isOriginLeft = left align?
32163      */   
32164     isOriginLeft : true,
32165     /**
32166      * @cfg {Boolean} isOriginTop = top align?
32167      */   
32168     isOriginTop : false,
32169     /**
32170      * @cfg {Boolean} isLayoutInstant = no animation?
32171      */   
32172     isLayoutInstant : false, // needed?
32173     /**
32174      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32175      */   
32176     isResizingContainer : true,
32177     /**
32178      * @cfg {Number} columnWidth  width of the columns 
32179      */   
32180     
32181     columnWidth : 0,
32182     
32183     /**
32184      * @cfg {Number} maxCols maximum number of columns
32185      */   
32186     
32187     maxCols: 0,
32188     /**
32189      * @cfg {Number} padHeight padding below box..
32190      */   
32191     
32192     padHeight : 10, 
32193     
32194     /**
32195      * @cfg {Boolean} isAutoInitial defalut true
32196      */   
32197     
32198     isAutoInitial : true, 
32199     
32200     // private?
32201     gutter : 0,
32202     
32203     containerWidth: 0,
32204     initialColumnWidth : 0,
32205     currentSize : null,
32206     
32207     colYs : null, // array.
32208     maxY : 0,
32209     padWidth: 10,
32210     
32211     
32212     tag: 'div',
32213     cls: '',
32214     bricks: null, //CompositeElement
32215     cols : 0, // array?
32216     // element : null, // wrapped now this.el
32217     _isLayoutInited : null, 
32218     
32219     
32220     getAutoCreate : function(){
32221         
32222         var cfg = {
32223             tag: this.tag,
32224             cls: 'blog-masonary-wrapper ' + this.cls,
32225             cn : {
32226                 cls : 'mas-boxes masonary'
32227             }
32228         };
32229         
32230         return cfg;
32231     },
32232     
32233     getChildContainer: function( )
32234     {
32235         if (this.boxesEl) {
32236             return this.boxesEl;
32237         }
32238         
32239         this.boxesEl = this.el.select('.mas-boxes').first();
32240         
32241         return this.boxesEl;
32242     },
32243     
32244     
32245     initEvents : function()
32246     {
32247         var _this = this;
32248         
32249         if(this.isAutoInitial){
32250             Roo.log('hook children rendered');
32251             this.on('childrenrendered', function() {
32252                 Roo.log('children rendered');
32253                 _this.initial();
32254             } ,this);
32255         }
32256         
32257     },
32258     
32259     initial : function()
32260     {
32261         this.reloadItems();
32262
32263         this.currentSize = this.el.getBox(true);
32264
32265         /// was window resize... - let's see if this works..
32266         Roo.EventManager.onWindowResize(this.resize, this); 
32267
32268         if(!this.isAutoInitial){
32269             this.layout();
32270             return;
32271         }
32272         
32273         this.layout.defer(500,this);
32274     },
32275     
32276     reloadItems: function()
32277     {
32278         this.bricks = this.el.select('.masonry-brick', true);
32279         
32280         this.bricks.each(function(b) {
32281             //Roo.log(b.getSize());
32282             if (!b.attr('originalwidth')) {
32283                 b.attr('originalwidth',  b.getSize().width);
32284             }
32285             
32286         });
32287         
32288         Roo.log(this.bricks.elements.length);
32289     },
32290     
32291     resize : function()
32292     {
32293         Roo.log('resize');
32294         var cs = this.el.getBox(true);
32295         
32296         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32297             Roo.log("no change in with or X");
32298             return;
32299         }
32300         this.currentSize = cs;
32301         this.layout();
32302     },
32303     
32304     layout : function()
32305     {
32306          Roo.log('layout');
32307         this._resetLayout();
32308         //this._manageStamps();
32309       
32310         // don't animate first layout
32311         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32312         this.layoutItems( isInstant );
32313       
32314         // flag for initalized
32315         this._isLayoutInited = true;
32316     },
32317     
32318     layoutItems : function( isInstant )
32319     {
32320         //var items = this._getItemsForLayout( this.items );
32321         // original code supports filtering layout items.. we just ignore it..
32322         
32323         this._layoutItems( this.bricks , isInstant );
32324       
32325         this._postLayout();
32326     },
32327     _layoutItems : function ( items , isInstant)
32328     {
32329        //this.fireEvent( 'layout', this, items );
32330     
32331
32332         if ( !items || !items.elements.length ) {
32333           // no items, emit event with empty array
32334             return;
32335         }
32336
32337         var queue = [];
32338         items.each(function(item) {
32339             Roo.log("layout item");
32340             Roo.log(item);
32341             // get x/y object from method
32342             var position = this._getItemLayoutPosition( item );
32343             // enqueue
32344             position.item = item;
32345             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32346             queue.push( position );
32347         }, this);
32348       
32349         this._processLayoutQueue( queue );
32350     },
32351     /** Sets position of item in DOM
32352     * @param {Element} item
32353     * @param {Number} x - horizontal position
32354     * @param {Number} y - vertical position
32355     * @param {Boolean} isInstant - disables transitions
32356     */
32357     _processLayoutQueue : function( queue )
32358     {
32359         for ( var i=0, len = queue.length; i < len; i++ ) {
32360             var obj = queue[i];
32361             obj.item.position('absolute');
32362             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32363         }
32364     },
32365       
32366     
32367     /**
32368     * Any logic you want to do after each layout,
32369     * i.e. size the container
32370     */
32371     _postLayout : function()
32372     {
32373         this.resizeContainer();
32374     },
32375     
32376     resizeContainer : function()
32377     {
32378         if ( !this.isResizingContainer ) {
32379             return;
32380         }
32381         var size = this._getContainerSize();
32382         if ( size ) {
32383             this.el.setSize(size.width,size.height);
32384             this.boxesEl.setSize(size.width,size.height);
32385         }
32386     },
32387     
32388     
32389     
32390     _resetLayout : function()
32391     {
32392         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32393         this.colWidth = this.el.getWidth();
32394         //this.gutter = this.el.getWidth(); 
32395         
32396         this.measureColumns();
32397
32398         // reset column Y
32399         var i = this.cols;
32400         this.colYs = [];
32401         while (i--) {
32402             this.colYs.push( 0 );
32403         }
32404     
32405         this.maxY = 0;
32406     },
32407
32408     measureColumns : function()
32409     {
32410         this.getContainerWidth();
32411       // if columnWidth is 0, default to outerWidth of first item
32412         if ( !this.columnWidth ) {
32413             var firstItem = this.bricks.first();
32414             Roo.log(firstItem);
32415             this.columnWidth  = this.containerWidth;
32416             if (firstItem && firstItem.attr('originalwidth') ) {
32417                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32418             }
32419             // columnWidth fall back to item of first element
32420             Roo.log("set column width?");
32421                         this.initialColumnWidth = this.columnWidth  ;
32422
32423             // if first elem has no width, default to size of container
32424             
32425         }
32426         
32427         
32428         if (this.initialColumnWidth) {
32429             this.columnWidth = this.initialColumnWidth;
32430         }
32431         
32432         
32433             
32434         // column width is fixed at the top - however if container width get's smaller we should
32435         // reduce it...
32436         
32437         // this bit calcs how man columns..
32438             
32439         var columnWidth = this.columnWidth += this.gutter;
32440       
32441         // calculate columns
32442         var containerWidth = this.containerWidth + this.gutter;
32443         
32444         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32445         // fix rounding errors, typically with gutters
32446         var excess = columnWidth - containerWidth % columnWidth;
32447         
32448         
32449         // if overshoot is less than a pixel, round up, otherwise floor it
32450         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32451         cols = Math[ mathMethod ]( cols );
32452         this.cols = Math.max( cols, 1 );
32453         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32454         
32455          // padding positioning..
32456         var totalColWidth = this.cols * this.columnWidth;
32457         var padavail = this.containerWidth - totalColWidth;
32458         // so for 2 columns - we need 3 'pads'
32459         
32460         var padNeeded = (1+this.cols) * this.padWidth;
32461         
32462         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32463         
32464         this.columnWidth += padExtra
32465         //this.padWidth = Math.floor(padavail /  ( this.cols));
32466         
32467         // adjust colum width so that padding is fixed??
32468         
32469         // we have 3 columns ... total = width * 3
32470         // we have X left over... that should be used by 
32471         
32472         //if (this.expandC) {
32473             
32474         //}
32475         
32476         
32477         
32478     },
32479     
32480     getContainerWidth : function()
32481     {
32482        /* // container is parent if fit width
32483         var container = this.isFitWidth ? this.element.parentNode : this.element;
32484         // check that this.size and size are there
32485         // IE8 triggers resize on body size change, so they might not be
32486         
32487         var size = getSize( container );  //FIXME
32488         this.containerWidth = size && size.innerWidth; //FIXME
32489         */
32490          
32491         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32492         
32493     },
32494     
32495     _getItemLayoutPosition : function( item )  // what is item?
32496     {
32497         // we resize the item to our columnWidth..
32498       
32499         item.setWidth(this.columnWidth);
32500         item.autoBoxAdjust  = false;
32501         
32502         var sz = item.getSize();
32503  
32504         // how many columns does this brick span
32505         var remainder = this.containerWidth % this.columnWidth;
32506         
32507         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32508         // round if off by 1 pixel, otherwise use ceil
32509         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32510         colSpan = Math.min( colSpan, this.cols );
32511         
32512         // normally this should be '1' as we dont' currently allow multi width columns..
32513         
32514         var colGroup = this._getColGroup( colSpan );
32515         // get the minimum Y value from the columns
32516         var minimumY = Math.min.apply( Math, colGroup );
32517         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32518         
32519         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32520          
32521         // position the brick
32522         var position = {
32523             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32524             y: this.currentSize.y + minimumY + this.padHeight
32525         };
32526         
32527         Roo.log(position);
32528         // apply setHeight to necessary columns
32529         var setHeight = minimumY + sz.height + this.padHeight;
32530         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32531         
32532         var setSpan = this.cols + 1 - colGroup.length;
32533         for ( var i = 0; i < setSpan; i++ ) {
32534           this.colYs[ shortColIndex + i ] = setHeight ;
32535         }
32536       
32537         return position;
32538     },
32539     
32540     /**
32541      * @param {Number} colSpan - number of columns the element spans
32542      * @returns {Array} colGroup
32543      */
32544     _getColGroup : function( colSpan )
32545     {
32546         if ( colSpan < 2 ) {
32547           // if brick spans only one column, use all the column Ys
32548           return this.colYs;
32549         }
32550       
32551         var colGroup = [];
32552         // how many different places could this brick fit horizontally
32553         var groupCount = this.cols + 1 - colSpan;
32554         // for each group potential horizontal position
32555         for ( var i = 0; i < groupCount; i++ ) {
32556           // make an array of colY values for that one group
32557           var groupColYs = this.colYs.slice( i, i + colSpan );
32558           // and get the max value of the array
32559           colGroup[i] = Math.max.apply( Math, groupColYs );
32560         }
32561         return colGroup;
32562     },
32563     /*
32564     _manageStamp : function( stamp )
32565     {
32566         var stampSize =  stamp.getSize();
32567         var offset = stamp.getBox();
32568         // get the columns that this stamp affects
32569         var firstX = this.isOriginLeft ? offset.x : offset.right;
32570         var lastX = firstX + stampSize.width;
32571         var firstCol = Math.floor( firstX / this.columnWidth );
32572         firstCol = Math.max( 0, firstCol );
32573         
32574         var lastCol = Math.floor( lastX / this.columnWidth );
32575         // lastCol should not go over if multiple of columnWidth #425
32576         lastCol -= lastX % this.columnWidth ? 0 : 1;
32577         lastCol = Math.min( this.cols - 1, lastCol );
32578         
32579         // set colYs to bottom of the stamp
32580         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32581             stampSize.height;
32582             
32583         for ( var i = firstCol; i <= lastCol; i++ ) {
32584           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32585         }
32586     },
32587     */
32588     
32589     _getContainerSize : function()
32590     {
32591         this.maxY = Math.max.apply( Math, this.colYs );
32592         var size = {
32593             height: this.maxY
32594         };
32595       
32596         if ( this.isFitWidth ) {
32597             size.width = this._getContainerFitWidth();
32598         }
32599       
32600         return size;
32601     },
32602     
32603     _getContainerFitWidth : function()
32604     {
32605         var unusedCols = 0;
32606         // count unused columns
32607         var i = this.cols;
32608         while ( --i ) {
32609           if ( this.colYs[i] !== 0 ) {
32610             break;
32611           }
32612           unusedCols++;
32613         }
32614         // fit container to columns that have been used
32615         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32616     },
32617     
32618     needsResizeLayout : function()
32619     {
32620         var previousWidth = this.containerWidth;
32621         this.getContainerWidth();
32622         return previousWidth !== this.containerWidth;
32623     }
32624  
32625 });
32626
32627  
32628
32629  /*
32630  * - LGPL
32631  *
32632  * element
32633  * 
32634  */
32635
32636 /**
32637  * @class Roo.bootstrap.MasonryBrick
32638  * @extends Roo.bootstrap.Component
32639  * Bootstrap MasonryBrick class
32640  * 
32641  * @constructor
32642  * Create a new MasonryBrick
32643  * @param {Object} config The config object
32644  */
32645
32646 Roo.bootstrap.MasonryBrick = function(config){
32647     
32648     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32649     
32650     Roo.bootstrap.MasonryBrick.register(this);
32651     
32652     this.addEvents({
32653         // raw events
32654         /**
32655          * @event click
32656          * When a MasonryBrick is clcik
32657          * @param {Roo.bootstrap.MasonryBrick} this
32658          * @param {Roo.EventObject} e
32659          */
32660         "click" : true
32661     });
32662 };
32663
32664 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32665     
32666     /**
32667      * @cfg {String} title
32668      */   
32669     title : '',
32670     /**
32671      * @cfg {String} html
32672      */   
32673     html : '',
32674     /**
32675      * @cfg {String} bgimage
32676      */   
32677     bgimage : '',
32678     /**
32679      * @cfg {String} videourl
32680      */   
32681     videourl : '',
32682     /**
32683      * @cfg {String} cls
32684      */   
32685     cls : '',
32686     /**
32687      * @cfg {String} href
32688      */   
32689     href : '',
32690     /**
32691      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32692      */   
32693     size : 'xs',
32694     
32695     /**
32696      * @cfg {String} placetitle (center|bottom)
32697      */   
32698     placetitle : '',
32699     
32700     /**
32701      * @cfg {Boolean} isFitContainer defalut true
32702      */   
32703     isFitContainer : true, 
32704     
32705     /**
32706      * @cfg {Boolean} preventDefault defalut false
32707      */   
32708     preventDefault : false, 
32709     
32710     /**
32711      * @cfg {Boolean} inverse defalut false
32712      */   
32713     maskInverse : false, 
32714     
32715     getAutoCreate : function()
32716     {
32717         if(!this.isFitContainer){
32718             return this.getSplitAutoCreate();
32719         }
32720         
32721         var cls = 'masonry-brick masonry-brick-full';
32722         
32723         if(this.href.length){
32724             cls += ' masonry-brick-link';
32725         }
32726         
32727         if(this.bgimage.length){
32728             cls += ' masonry-brick-image';
32729         }
32730         
32731         if(this.maskInverse){
32732             cls += ' mask-inverse';
32733         }
32734         
32735         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32736             cls += ' enable-mask';
32737         }
32738         
32739         if(this.size){
32740             cls += ' masonry-' + this.size + '-brick';
32741         }
32742         
32743         if(this.placetitle.length){
32744             
32745             switch (this.placetitle) {
32746                 case 'center' :
32747                     cls += ' masonry-center-title';
32748                     break;
32749                 case 'bottom' :
32750                     cls += ' masonry-bottom-title';
32751                     break;
32752                 default:
32753                     break;
32754             }
32755             
32756         } else {
32757             if(!this.html.length && !this.bgimage.length){
32758                 cls += ' masonry-center-title';
32759             }
32760
32761             if(!this.html.length && this.bgimage.length){
32762                 cls += ' masonry-bottom-title';
32763             }
32764         }
32765         
32766         if(this.cls){
32767             cls += ' ' + this.cls;
32768         }
32769         
32770         var cfg = {
32771             tag: (this.href.length) ? 'a' : 'div',
32772             cls: cls,
32773             cn: [
32774                 {
32775                     tag: 'div',
32776                     cls: 'masonry-brick-mask'
32777                 },
32778                 {
32779                     tag: 'div',
32780                     cls: 'masonry-brick-paragraph',
32781                     cn: []
32782                 }
32783             ]
32784         };
32785         
32786         if(this.href.length){
32787             cfg.href = this.href;
32788         }
32789         
32790         var cn = cfg.cn[1].cn;
32791         
32792         if(this.title.length){
32793             cn.push({
32794                 tag: 'h4',
32795                 cls: 'masonry-brick-title',
32796                 html: this.title
32797             });
32798         }
32799         
32800         if(this.html.length){
32801             cn.push({
32802                 tag: 'p',
32803                 cls: 'masonry-brick-text',
32804                 html: this.html
32805             });
32806         }
32807         
32808         if (!this.title.length && !this.html.length) {
32809             cfg.cn[1].cls += ' hide';
32810         }
32811         
32812         if(this.bgimage.length){
32813             cfg.cn.push({
32814                 tag: 'img',
32815                 cls: 'masonry-brick-image-view',
32816                 src: this.bgimage
32817             });
32818         }
32819         
32820         if(this.videourl.length){
32821             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32822             // youtube support only?
32823             cfg.cn.push({
32824                 tag: 'iframe',
32825                 cls: 'masonry-brick-image-view',
32826                 src: vurl,
32827                 frameborder : 0,
32828                 allowfullscreen : true
32829             });
32830         }
32831         
32832         return cfg;
32833         
32834     },
32835     
32836     getSplitAutoCreate : function()
32837     {
32838         var cls = 'masonry-brick masonry-brick-split';
32839         
32840         if(this.href.length){
32841             cls += ' masonry-brick-link';
32842         }
32843         
32844         if(this.bgimage.length){
32845             cls += ' masonry-brick-image';
32846         }
32847         
32848         if(this.size){
32849             cls += ' masonry-' + this.size + '-brick';
32850         }
32851         
32852         switch (this.placetitle) {
32853             case 'center' :
32854                 cls += ' masonry-center-title';
32855                 break;
32856             case 'bottom' :
32857                 cls += ' masonry-bottom-title';
32858                 break;
32859             default:
32860                 if(!this.bgimage.length){
32861                     cls += ' masonry-center-title';
32862                 }
32863
32864                 if(this.bgimage.length){
32865                     cls += ' masonry-bottom-title';
32866                 }
32867                 break;
32868         }
32869         
32870         if(this.cls){
32871             cls += ' ' + this.cls;
32872         }
32873         
32874         var cfg = {
32875             tag: (this.href.length) ? 'a' : 'div',
32876             cls: cls,
32877             cn: [
32878                 {
32879                     tag: 'div',
32880                     cls: 'masonry-brick-split-head',
32881                     cn: [
32882                         {
32883                             tag: 'div',
32884                             cls: 'masonry-brick-paragraph',
32885                             cn: []
32886                         }
32887                     ]
32888                 },
32889                 {
32890                     tag: 'div',
32891                     cls: 'masonry-brick-split-body',
32892                     cn: []
32893                 }
32894             ]
32895         };
32896         
32897         if(this.href.length){
32898             cfg.href = this.href;
32899         }
32900         
32901         if(this.title.length){
32902             cfg.cn[0].cn[0].cn.push({
32903                 tag: 'h4',
32904                 cls: 'masonry-brick-title',
32905                 html: this.title
32906             });
32907         }
32908         
32909         if(this.html.length){
32910             cfg.cn[1].cn.push({
32911                 tag: 'p',
32912                 cls: 'masonry-brick-text',
32913                 html: this.html
32914             });
32915         }
32916
32917         if(this.bgimage.length){
32918             cfg.cn[0].cn.push({
32919                 tag: 'img',
32920                 cls: 'masonry-brick-image-view',
32921                 src: this.bgimage
32922             });
32923         }
32924         
32925         if(this.videourl.length){
32926             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32927             // youtube support only?
32928             cfg.cn[0].cn.cn.push({
32929                 tag: 'iframe',
32930                 cls: 'masonry-brick-image-view',
32931                 src: vurl,
32932                 frameborder : 0,
32933                 allowfullscreen : true
32934             });
32935         }
32936         
32937         return cfg;
32938     },
32939     
32940     initEvents: function() 
32941     {
32942         switch (this.size) {
32943             case 'xs' :
32944                 this.x = 1;
32945                 this.y = 1;
32946                 break;
32947             case 'sm' :
32948                 this.x = 2;
32949                 this.y = 2;
32950                 break;
32951             case 'md' :
32952             case 'md-left' :
32953             case 'md-right' :
32954                 this.x = 3;
32955                 this.y = 3;
32956                 break;
32957             case 'tall' :
32958                 this.x = 2;
32959                 this.y = 3;
32960                 break;
32961             case 'wide' :
32962                 this.x = 3;
32963                 this.y = 2;
32964                 break;
32965             case 'wide-thin' :
32966                 this.x = 3;
32967                 this.y = 1;
32968                 break;
32969                         
32970             default :
32971                 break;
32972         }
32973         
32974         if(Roo.isTouch){
32975             this.el.on('touchstart', this.onTouchStart, this);
32976             this.el.on('touchmove', this.onTouchMove, this);
32977             this.el.on('touchend', this.onTouchEnd, this);
32978             this.el.on('contextmenu', this.onContextMenu, this);
32979         } else {
32980             this.el.on('mouseenter'  ,this.enter, this);
32981             this.el.on('mouseleave', this.leave, this);
32982             this.el.on('click', this.onClick, this);
32983         }
32984         
32985         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32986             this.parent().bricks.push(this);   
32987         }
32988         
32989     },
32990     
32991     onClick: function(e, el)
32992     {
32993         var time = this.endTimer - this.startTimer;
32994         // Roo.log(e.preventDefault());
32995         if(Roo.isTouch){
32996             if(time > 1000){
32997                 e.preventDefault();
32998                 return;
32999             }
33000         }
33001         
33002         if(!this.preventDefault){
33003             return;
33004         }
33005         
33006         e.preventDefault();
33007         
33008         if (this.activeClass != '') {
33009             this.selectBrick();
33010         }
33011         
33012         this.fireEvent('click', this, e);
33013     },
33014     
33015     enter: function(e, el)
33016     {
33017         e.preventDefault();
33018         
33019         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33020             return;
33021         }
33022         
33023         if(this.bgimage.length && this.html.length){
33024             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33025         }
33026     },
33027     
33028     leave: function(e, el)
33029     {
33030         e.preventDefault();
33031         
33032         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33033             return;
33034         }
33035         
33036         if(this.bgimage.length && this.html.length){
33037             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33038         }
33039     },
33040     
33041     onTouchStart: function(e, el)
33042     {
33043 //        e.preventDefault();
33044         
33045         this.touchmoved = false;
33046         
33047         if(!this.isFitContainer){
33048             return;
33049         }
33050         
33051         if(!this.bgimage.length || !this.html.length){
33052             return;
33053         }
33054         
33055         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33056         
33057         this.timer = new Date().getTime();
33058         
33059     },
33060     
33061     onTouchMove: function(e, el)
33062     {
33063         this.touchmoved = true;
33064     },
33065     
33066     onContextMenu : function(e,el)
33067     {
33068         e.preventDefault();
33069         e.stopPropagation();
33070         return false;
33071     },
33072     
33073     onTouchEnd: function(e, el)
33074     {
33075 //        e.preventDefault();
33076         
33077         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33078         
33079             this.leave(e,el);
33080             
33081             return;
33082         }
33083         
33084         if(!this.bgimage.length || !this.html.length){
33085             
33086             if(this.href.length){
33087                 window.location.href = this.href;
33088             }
33089             
33090             return;
33091         }
33092         
33093         if(!this.isFitContainer){
33094             return;
33095         }
33096         
33097         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33098         
33099         window.location.href = this.href;
33100     },
33101     
33102     //selection on single brick only
33103     selectBrick : function() {
33104         
33105         if (!this.parentId) {
33106             return;
33107         }
33108         
33109         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33110         var index = m.selectedBrick.indexOf(this.id);
33111         
33112         if ( index > -1) {
33113             m.selectedBrick.splice(index,1);
33114             this.el.removeClass(this.activeClass);
33115             return;
33116         }
33117         
33118         for(var i = 0; i < m.selectedBrick.length; i++) {
33119             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33120             b.el.removeClass(b.activeClass);
33121         }
33122         
33123         m.selectedBrick = [];
33124         
33125         m.selectedBrick.push(this.id);
33126         this.el.addClass(this.activeClass);
33127         return;
33128     },
33129     
33130     isSelected : function(){
33131         return this.el.hasClass(this.activeClass);
33132         
33133     }
33134 });
33135
33136 Roo.apply(Roo.bootstrap.MasonryBrick, {
33137     
33138     //groups: {},
33139     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33140      /**
33141     * register a Masonry Brick
33142     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33143     */
33144     
33145     register : function(brick)
33146     {
33147         //this.groups[brick.id] = brick;
33148         this.groups.add(brick.id, brick);
33149     },
33150     /**
33151     * fetch a  masonry brick based on the masonry brick ID
33152     * @param {string} the masonry brick to add
33153     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33154     */
33155     
33156     get: function(brick_id) 
33157     {
33158         // if (typeof(this.groups[brick_id]) == 'undefined') {
33159         //     return false;
33160         // }
33161         // return this.groups[brick_id] ;
33162         
33163         if(this.groups.key(brick_id)) {
33164             return this.groups.key(brick_id);
33165         }
33166         
33167         return false;
33168     }
33169     
33170     
33171     
33172 });
33173
33174  /*
33175  * - LGPL
33176  *
33177  * element
33178  * 
33179  */
33180
33181 /**
33182  * @class Roo.bootstrap.Brick
33183  * @extends Roo.bootstrap.Component
33184  * Bootstrap Brick class
33185  * 
33186  * @constructor
33187  * Create a new Brick
33188  * @param {Object} config The config object
33189  */
33190
33191 Roo.bootstrap.Brick = function(config){
33192     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33193     
33194     this.addEvents({
33195         // raw events
33196         /**
33197          * @event click
33198          * When a Brick is click
33199          * @param {Roo.bootstrap.Brick} this
33200          * @param {Roo.EventObject} e
33201          */
33202         "click" : true
33203     });
33204 };
33205
33206 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33207     
33208     /**
33209      * @cfg {String} title
33210      */   
33211     title : '',
33212     /**
33213      * @cfg {String} html
33214      */   
33215     html : '',
33216     /**
33217      * @cfg {String} bgimage
33218      */   
33219     bgimage : '',
33220     /**
33221      * @cfg {String} cls
33222      */   
33223     cls : '',
33224     /**
33225      * @cfg {String} href
33226      */   
33227     href : '',
33228     /**
33229      * @cfg {String} video
33230      */   
33231     video : '',
33232     /**
33233      * @cfg {Boolean} square
33234      */   
33235     square : true,
33236     
33237     getAutoCreate : function()
33238     {
33239         var cls = 'roo-brick';
33240         
33241         if(this.href.length){
33242             cls += ' roo-brick-link';
33243         }
33244         
33245         if(this.bgimage.length){
33246             cls += ' roo-brick-image';
33247         }
33248         
33249         if(!this.html.length && !this.bgimage.length){
33250             cls += ' roo-brick-center-title';
33251         }
33252         
33253         if(!this.html.length && this.bgimage.length){
33254             cls += ' roo-brick-bottom-title';
33255         }
33256         
33257         if(this.cls){
33258             cls += ' ' + this.cls;
33259         }
33260         
33261         var cfg = {
33262             tag: (this.href.length) ? 'a' : 'div',
33263             cls: cls,
33264             cn: [
33265                 {
33266                     tag: 'div',
33267                     cls: 'roo-brick-paragraph',
33268                     cn: []
33269                 }
33270             ]
33271         };
33272         
33273         if(this.href.length){
33274             cfg.href = this.href;
33275         }
33276         
33277         var cn = cfg.cn[0].cn;
33278         
33279         if(this.title.length){
33280             cn.push({
33281                 tag: 'h4',
33282                 cls: 'roo-brick-title',
33283                 html: this.title
33284             });
33285         }
33286         
33287         if(this.html.length){
33288             cn.push({
33289                 tag: 'p',
33290                 cls: 'roo-brick-text',
33291                 html: this.html
33292             });
33293         } else {
33294             cn.cls += ' hide';
33295         }
33296         
33297         if(this.bgimage.length){
33298             cfg.cn.push({
33299                 tag: 'img',
33300                 cls: 'roo-brick-image-view',
33301                 src: this.bgimage
33302             });
33303         }
33304         
33305         return cfg;
33306     },
33307     
33308     initEvents: function() 
33309     {
33310         if(this.title.length || this.html.length){
33311             this.el.on('mouseenter'  ,this.enter, this);
33312             this.el.on('mouseleave', this.leave, this);
33313         }
33314         
33315         Roo.EventManager.onWindowResize(this.resize, this); 
33316         
33317         if(this.bgimage.length){
33318             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33319             this.imageEl.on('load', this.onImageLoad, this);
33320             return;
33321         }
33322         
33323         this.resize();
33324     },
33325     
33326     onImageLoad : function()
33327     {
33328         this.resize();
33329     },
33330     
33331     resize : function()
33332     {
33333         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33334         
33335         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33336         
33337         if(this.bgimage.length){
33338             var image = this.el.select('.roo-brick-image-view', true).first();
33339             
33340             image.setWidth(paragraph.getWidth());
33341             
33342             if(this.square){
33343                 image.setHeight(paragraph.getWidth());
33344             }
33345             
33346             this.el.setHeight(image.getHeight());
33347             paragraph.setHeight(image.getHeight());
33348             
33349         }
33350         
33351     },
33352     
33353     enter: function(e, el)
33354     {
33355         e.preventDefault();
33356         
33357         if(this.bgimage.length){
33358             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33359             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33360         }
33361     },
33362     
33363     leave: function(e, el)
33364     {
33365         e.preventDefault();
33366         
33367         if(this.bgimage.length){
33368             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33369             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33370         }
33371     }
33372     
33373 });
33374
33375  
33376
33377  /*
33378  * - LGPL
33379  *
33380  * Number field 
33381  */
33382
33383 /**
33384  * @class Roo.bootstrap.NumberField
33385  * @extends Roo.bootstrap.Input
33386  * Bootstrap NumberField class
33387  * 
33388  * 
33389  * 
33390  * 
33391  * @constructor
33392  * Create a new NumberField
33393  * @param {Object} config The config object
33394  */
33395
33396 Roo.bootstrap.NumberField = function(config){
33397     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33398 };
33399
33400 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33401     
33402     /**
33403      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33404      */
33405     allowDecimals : true,
33406     /**
33407      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33408      */
33409     decimalSeparator : ".",
33410     /**
33411      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33412      */
33413     decimalPrecision : 2,
33414     /**
33415      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33416      */
33417     allowNegative : true,
33418     
33419     /**
33420      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33421      */
33422     allowZero: true,
33423     /**
33424      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33425      */
33426     minValue : Number.NEGATIVE_INFINITY,
33427     /**
33428      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33429      */
33430     maxValue : Number.MAX_VALUE,
33431     /**
33432      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33433      */
33434     minText : "The minimum value for this field is {0}",
33435     /**
33436      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33437      */
33438     maxText : "The maximum value for this field is {0}",
33439     /**
33440      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33441      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33442      */
33443     nanText : "{0} is not a valid number",
33444     /**
33445      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33446      */
33447     thousandsDelimiter : false,
33448     /**
33449      * @cfg {String} valueAlign alignment of value
33450      */
33451     valueAlign : "left",
33452
33453     getAutoCreate : function()
33454     {
33455         var hiddenInput = {
33456             tag: 'input',
33457             type: 'hidden',
33458             id: Roo.id(),
33459             cls: 'hidden-number-input'
33460         };
33461         
33462         if (this.name) {
33463             hiddenInput.name = this.name;
33464         }
33465         
33466         this.name = '';
33467         
33468         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33469         
33470         this.name = hiddenInput.name;
33471         
33472         if(cfg.cn.length > 0) {
33473             cfg.cn.push(hiddenInput);
33474         }
33475         
33476         return cfg;
33477     },
33478
33479     // private
33480     initEvents : function()
33481     {   
33482         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33483         
33484         var allowed = "0123456789";
33485         
33486         if(this.allowDecimals){
33487             allowed += this.decimalSeparator;
33488         }
33489         
33490         if(this.allowNegative){
33491             allowed += "-";
33492         }
33493         
33494         if(this.thousandsDelimiter) {
33495             allowed += ",";
33496         }
33497         
33498         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33499         
33500         var keyPress = function(e){
33501             
33502             var k = e.getKey();
33503             
33504             var c = e.getCharCode();
33505             
33506             if(
33507                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33508                     allowed.indexOf(String.fromCharCode(c)) === -1
33509             ){
33510                 e.stopEvent();
33511                 return;
33512             }
33513             
33514             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33515                 return;
33516             }
33517             
33518             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33519                 e.stopEvent();
33520             }
33521         };
33522         
33523         this.el.on("keypress", keyPress, this);
33524     },
33525     
33526     validateValue : function(value)
33527     {
33528         
33529         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33530             return false;
33531         }
33532         
33533         var num = this.parseValue(value);
33534         
33535         if(isNaN(num)){
33536             this.markInvalid(String.format(this.nanText, value));
33537             return false;
33538         }
33539         
33540         if(num < this.minValue){
33541             this.markInvalid(String.format(this.minText, this.minValue));
33542             return false;
33543         }
33544         
33545         if(num > this.maxValue){
33546             this.markInvalid(String.format(this.maxText, this.maxValue));
33547             return false;
33548         }
33549         
33550         return true;
33551     },
33552
33553     getValue : function()
33554     {
33555         var v = this.hiddenEl().getValue();
33556         
33557         return this.fixPrecision(this.parseValue(v));
33558     },
33559
33560     parseValue : function(value)
33561     {
33562         if(this.thousandsDelimiter) {
33563             value += "";
33564             r = new RegExp(",", "g");
33565             value = value.replace(r, "");
33566         }
33567         
33568         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33569         return isNaN(value) ? '' : value;
33570     },
33571
33572     fixPrecision : function(value)
33573     {
33574         if(this.thousandsDelimiter) {
33575             value += "";
33576             r = new RegExp(",", "g");
33577             value = value.replace(r, "");
33578         }
33579         
33580         var nan = isNaN(value);
33581         
33582         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33583             return nan ? '' : value;
33584         }
33585         return parseFloat(value).toFixed(this.decimalPrecision);
33586     },
33587
33588     setValue : function(v)
33589     {
33590         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33591         
33592         this.value = v;
33593         
33594         if(this.rendered){
33595             
33596             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33597             
33598             this.inputEl().dom.value = (v == '') ? '' :
33599                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33600             
33601             if(!this.allowZero && v === '0') {
33602                 this.hiddenEl().dom.value = '';
33603                 this.inputEl().dom.value = '';
33604             }
33605             
33606             this.validate();
33607         }
33608     },
33609
33610     decimalPrecisionFcn : function(v)
33611     {
33612         return Math.floor(v);
33613     },
33614
33615     beforeBlur : function()
33616     {
33617         var v = this.parseValue(this.getRawValue());
33618         
33619         if(v || v === 0 || v === ''){
33620             this.setValue(v);
33621         }
33622     },
33623     
33624     hiddenEl : function()
33625     {
33626         return this.el.select('input.hidden-number-input',true).first();
33627     }
33628     
33629 });
33630
33631  
33632
33633 /*
33634 * Licence: LGPL
33635 */
33636
33637 /**
33638  * @class Roo.bootstrap.DocumentSlider
33639  * @extends Roo.bootstrap.Component
33640  * Bootstrap DocumentSlider class
33641  * 
33642  * @constructor
33643  * Create a new DocumentViewer
33644  * @param {Object} config The config object
33645  */
33646
33647 Roo.bootstrap.DocumentSlider = function(config){
33648     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33649     
33650     this.files = [];
33651     
33652     this.addEvents({
33653         /**
33654          * @event initial
33655          * Fire after initEvent
33656          * @param {Roo.bootstrap.DocumentSlider} this
33657          */
33658         "initial" : true,
33659         /**
33660          * @event update
33661          * Fire after update
33662          * @param {Roo.bootstrap.DocumentSlider} this
33663          */
33664         "update" : true,
33665         /**
33666          * @event click
33667          * Fire after click
33668          * @param {Roo.bootstrap.DocumentSlider} this
33669          */
33670         "click" : true
33671     });
33672 };
33673
33674 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33675     
33676     files : false,
33677     
33678     indicator : 0,
33679     
33680     getAutoCreate : function()
33681     {
33682         var cfg = {
33683             tag : 'div',
33684             cls : 'roo-document-slider',
33685             cn : [
33686                 {
33687                     tag : 'div',
33688                     cls : 'roo-document-slider-header',
33689                     cn : [
33690                         {
33691                             tag : 'div',
33692                             cls : 'roo-document-slider-header-title'
33693                         }
33694                     ]
33695                 },
33696                 {
33697                     tag : 'div',
33698                     cls : 'roo-document-slider-body',
33699                     cn : [
33700                         {
33701                             tag : 'div',
33702                             cls : 'roo-document-slider-prev',
33703                             cn : [
33704                                 {
33705                                     tag : 'i',
33706                                     cls : 'fa fa-chevron-left'
33707                                 }
33708                             ]
33709                         },
33710                         {
33711                             tag : 'div',
33712                             cls : 'roo-document-slider-thumb',
33713                             cn : [
33714                                 {
33715                                     tag : 'img',
33716                                     cls : 'roo-document-slider-image'
33717                                 }
33718                             ]
33719                         },
33720                         {
33721                             tag : 'div',
33722                             cls : 'roo-document-slider-next',
33723                             cn : [
33724                                 {
33725                                     tag : 'i',
33726                                     cls : 'fa fa-chevron-right'
33727                                 }
33728                             ]
33729                         }
33730                     ]
33731                 }
33732             ]
33733         };
33734         
33735         return cfg;
33736     },
33737     
33738     initEvents : function()
33739     {
33740         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33741         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33742         
33743         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33744         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33745         
33746         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33747         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33748         
33749         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33750         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33751         
33752         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33753         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33754         
33755         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33756         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33757         
33758         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33759         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33760         
33761         this.thumbEl.on('click', this.onClick, this);
33762         
33763         this.prevIndicator.on('click', this.prev, this);
33764         
33765         this.nextIndicator.on('click', this.next, this);
33766         
33767     },
33768     
33769     initial : function()
33770     {
33771         if(this.files.length){
33772             this.indicator = 1;
33773             this.update()
33774         }
33775         
33776         this.fireEvent('initial', this);
33777     },
33778     
33779     update : function()
33780     {
33781         this.imageEl.attr('src', this.files[this.indicator - 1]);
33782         
33783         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33784         
33785         this.prevIndicator.show();
33786         
33787         if(this.indicator == 1){
33788             this.prevIndicator.hide();
33789         }
33790         
33791         this.nextIndicator.show();
33792         
33793         if(this.indicator == this.files.length){
33794             this.nextIndicator.hide();
33795         }
33796         
33797         this.thumbEl.scrollTo('top');
33798         
33799         this.fireEvent('update', this);
33800     },
33801     
33802     onClick : function(e)
33803     {
33804         e.preventDefault();
33805         
33806         this.fireEvent('click', this);
33807     },
33808     
33809     prev : function(e)
33810     {
33811         e.preventDefault();
33812         
33813         this.indicator = Math.max(1, this.indicator - 1);
33814         
33815         this.update();
33816     },
33817     
33818     next : function(e)
33819     {
33820         e.preventDefault();
33821         
33822         this.indicator = Math.min(this.files.length, this.indicator + 1);
33823         
33824         this.update();
33825     }
33826 });
33827 /*
33828  * - LGPL
33829  *
33830  * RadioSet
33831  *
33832  *
33833  */
33834
33835 /**
33836  * @class Roo.bootstrap.RadioSet
33837  * @extends Roo.bootstrap.Input
33838  * Bootstrap RadioSet class
33839  * @cfg {String} indicatorpos (left|right) default left
33840  * @cfg {Boolean} inline (true|false) inline the element (default true)
33841  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33842  * @constructor
33843  * Create a new RadioSet
33844  * @param {Object} config The config object
33845  */
33846
33847 Roo.bootstrap.RadioSet = function(config){
33848     
33849     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33850     
33851     this.radioes = [];
33852     
33853     Roo.bootstrap.RadioSet.register(this);
33854     
33855     this.addEvents({
33856         /**
33857         * @event check
33858         * Fires when the element is checked or unchecked.
33859         * @param {Roo.bootstrap.RadioSet} this This radio
33860         * @param {Roo.bootstrap.Radio} item The checked item
33861         */
33862        check : true,
33863        /**
33864         * @event click
33865         * Fires when the element is click.
33866         * @param {Roo.bootstrap.RadioSet} this This radio set
33867         * @param {Roo.bootstrap.Radio} item The checked item
33868         * @param {Roo.EventObject} e The event object
33869         */
33870        click : true
33871     });
33872     
33873 };
33874
33875 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33876
33877     radioes : false,
33878     
33879     inline : true,
33880     
33881     weight : '',
33882     
33883     indicatorpos : 'left',
33884     
33885     getAutoCreate : function()
33886     {
33887         var label = {
33888             tag : 'label',
33889             cls : 'roo-radio-set-label',
33890             cn : [
33891                 {
33892                     tag : 'span',
33893                     html : this.fieldLabel
33894                 }
33895             ]
33896         };
33897         
33898         if(this.indicatorpos == 'left'){
33899             label.cn.unshift({
33900                 tag : 'i',
33901                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33902                 tooltip : 'This field is required'
33903             });
33904         } else {
33905             label.cn.push({
33906                 tag : 'i',
33907                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33908                 tooltip : 'This field is required'
33909             });
33910         }
33911         
33912         var items = {
33913             tag : 'div',
33914             cls : 'roo-radio-set-items'
33915         };
33916         
33917         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33918         
33919         if (align === 'left' && this.fieldLabel.length) {
33920             
33921             items = {
33922                 cls : "roo-radio-set-right", 
33923                 cn: [
33924                     items
33925                 ]
33926             };
33927             
33928             if(this.labelWidth > 12){
33929                 label.style = "width: " + this.labelWidth + 'px';
33930             }
33931             
33932             if(this.labelWidth < 13 && this.labelmd == 0){
33933                 this.labelmd = this.labelWidth;
33934             }
33935             
33936             if(this.labellg > 0){
33937                 label.cls += ' col-lg-' + this.labellg;
33938                 items.cls += ' col-lg-' + (12 - this.labellg);
33939             }
33940             
33941             if(this.labelmd > 0){
33942                 label.cls += ' col-md-' + this.labelmd;
33943                 items.cls += ' col-md-' + (12 - this.labelmd);
33944             }
33945             
33946             if(this.labelsm > 0){
33947                 label.cls += ' col-sm-' + this.labelsm;
33948                 items.cls += ' col-sm-' + (12 - this.labelsm);
33949             }
33950             
33951             if(this.labelxs > 0){
33952                 label.cls += ' col-xs-' + this.labelxs;
33953                 items.cls += ' col-xs-' + (12 - this.labelxs);
33954             }
33955         }
33956         
33957         var cfg = {
33958             tag : 'div',
33959             cls : 'roo-radio-set',
33960             cn : [
33961                 {
33962                     tag : 'input',
33963                     cls : 'roo-radio-set-input',
33964                     type : 'hidden',
33965                     name : this.name,
33966                     value : this.value ? this.value :  ''
33967                 },
33968                 label,
33969                 items
33970             ]
33971         };
33972         
33973         if(this.weight.length){
33974             cfg.cls += ' roo-radio-' + this.weight;
33975         }
33976         
33977         if(this.inline) {
33978             cfg.cls += ' roo-radio-set-inline';
33979         }
33980         
33981         var settings=this;
33982         ['xs','sm','md','lg'].map(function(size){
33983             if (settings[size]) {
33984                 cfg.cls += ' col-' + size + '-' + settings[size];
33985             }
33986         });
33987         
33988         return cfg;
33989         
33990     },
33991
33992     initEvents : function()
33993     {
33994         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33995         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33996         
33997         if(!this.fieldLabel.length){
33998             this.labelEl.hide();
33999         }
34000         
34001         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34002         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34003         
34004         this.indicator = this.indicatorEl();
34005         
34006         if(this.indicator){
34007             this.indicator.addClass('invisible');
34008         }
34009         
34010         this.originalValue = this.getValue();
34011         
34012     },
34013     
34014     inputEl: function ()
34015     {
34016         return this.el.select('.roo-radio-set-input', true).first();
34017     },
34018     
34019     getChildContainer : function()
34020     {
34021         return this.itemsEl;
34022     },
34023     
34024     register : function(item)
34025     {
34026         this.radioes.push(item);
34027         
34028     },
34029     
34030     validate : function()
34031     {   
34032         if(this.getVisibilityEl().hasClass('hidden')){
34033             return true;
34034         }
34035         
34036         var valid = false;
34037         
34038         Roo.each(this.radioes, function(i){
34039             if(!i.checked){
34040                 return;
34041             }
34042             
34043             valid = true;
34044             return false;
34045         });
34046         
34047         if(this.allowBlank) {
34048             return true;
34049         }
34050         
34051         if(this.disabled || valid){
34052             this.markValid();
34053             return true;
34054         }
34055         
34056         this.markInvalid();
34057         return false;
34058         
34059     },
34060     
34061     markValid : function()
34062     {
34063         if(this.labelEl.isVisible(true)){
34064             this.indicatorEl().removeClass('visible');
34065             this.indicatorEl().addClass('invisible');
34066         }
34067         
34068         this.el.removeClass([this.invalidClass, this.validClass]);
34069         this.el.addClass(this.validClass);
34070         
34071         this.fireEvent('valid', this);
34072     },
34073     
34074     markInvalid : function(msg)
34075     {
34076         if(this.allowBlank || this.disabled){
34077             return;
34078         }
34079         
34080         if(this.labelEl.isVisible(true)){
34081             this.indicatorEl().removeClass('invisible');
34082             this.indicatorEl().addClass('visible');
34083         }
34084         
34085         this.el.removeClass([this.invalidClass, this.validClass]);
34086         this.el.addClass(this.invalidClass);
34087         
34088         this.fireEvent('invalid', this, msg);
34089         
34090     },
34091     
34092     setValue : function(v, suppressEvent)
34093     {   
34094         if(this.value === v){
34095             return;
34096         }
34097         
34098         this.value = v;
34099         
34100         if(this.rendered){
34101             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34102         }
34103         
34104         Roo.each(this.radioes, function(i){
34105             i.checked = false;
34106             i.el.removeClass('checked');
34107         });
34108         
34109         Roo.each(this.radioes, function(i){
34110             
34111             if(i.value === v || i.value.toString() === v.toString()){
34112                 i.checked = true;
34113                 i.el.addClass('checked');
34114                 
34115                 if(suppressEvent !== true){
34116                     this.fireEvent('check', this, i);
34117                 }
34118                 
34119                 return false;
34120             }
34121             
34122         }, this);
34123         
34124         this.validate();
34125     },
34126     
34127     clearInvalid : function(){
34128         
34129         if(!this.el || this.preventMark){
34130             return;
34131         }
34132         
34133         this.el.removeClass([this.invalidClass]);
34134         
34135         this.fireEvent('valid', this);
34136     }
34137     
34138 });
34139
34140 Roo.apply(Roo.bootstrap.RadioSet, {
34141     
34142     groups: {},
34143     
34144     register : function(set)
34145     {
34146         this.groups[set.name] = set;
34147     },
34148     
34149     get: function(name) 
34150     {
34151         if (typeof(this.groups[name]) == 'undefined') {
34152             return false;
34153         }
34154         
34155         return this.groups[name] ;
34156     }
34157     
34158 });
34159 /*
34160  * Based on:
34161  * Ext JS Library 1.1.1
34162  * Copyright(c) 2006-2007, Ext JS, LLC.
34163  *
34164  * Originally Released Under LGPL - original licence link has changed is not relivant.
34165  *
34166  * Fork - LGPL
34167  * <script type="text/javascript">
34168  */
34169
34170
34171 /**
34172  * @class Roo.bootstrap.SplitBar
34173  * @extends Roo.util.Observable
34174  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34175  * <br><br>
34176  * Usage:
34177  * <pre><code>
34178 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34179                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34180 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34181 split.minSize = 100;
34182 split.maxSize = 600;
34183 split.animate = true;
34184 split.on('moved', splitterMoved);
34185 </code></pre>
34186  * @constructor
34187  * Create a new SplitBar
34188  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34189  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34190  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34191  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34192                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34193                         position of the SplitBar).
34194  */
34195 Roo.bootstrap.SplitBar = function(cfg){
34196     
34197     /** @private */
34198     
34199     //{
34200     //  dragElement : elm
34201     //  resizingElement: el,
34202         // optional..
34203     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34204     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34205         // existingProxy ???
34206     //}
34207     
34208     this.el = Roo.get(cfg.dragElement, true);
34209     this.el.dom.unselectable = "on";
34210     /** @private */
34211     this.resizingEl = Roo.get(cfg.resizingElement, true);
34212
34213     /**
34214      * @private
34215      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34216      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34217      * @type Number
34218      */
34219     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34220     
34221     /**
34222      * The minimum size of the resizing element. (Defaults to 0)
34223      * @type Number
34224      */
34225     this.minSize = 0;
34226     
34227     /**
34228      * The maximum size of the resizing element. (Defaults to 2000)
34229      * @type Number
34230      */
34231     this.maxSize = 2000;
34232     
34233     /**
34234      * Whether to animate the transition to the new size
34235      * @type Boolean
34236      */
34237     this.animate = false;
34238     
34239     /**
34240      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34241      * @type Boolean
34242      */
34243     this.useShim = false;
34244     
34245     /** @private */
34246     this.shim = null;
34247     
34248     if(!cfg.existingProxy){
34249         /** @private */
34250         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34251     }else{
34252         this.proxy = Roo.get(cfg.existingProxy).dom;
34253     }
34254     /** @private */
34255     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34256     
34257     /** @private */
34258     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34259     
34260     /** @private */
34261     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34262     
34263     /** @private */
34264     this.dragSpecs = {};
34265     
34266     /**
34267      * @private The adapter to use to positon and resize elements
34268      */
34269     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34270     this.adapter.init(this);
34271     
34272     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34273         /** @private */
34274         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34275         this.el.addClass("roo-splitbar-h");
34276     }else{
34277         /** @private */
34278         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34279         this.el.addClass("roo-splitbar-v");
34280     }
34281     
34282     this.addEvents({
34283         /**
34284          * @event resize
34285          * Fires when the splitter is moved (alias for {@link #event-moved})
34286          * @param {Roo.bootstrap.SplitBar} this
34287          * @param {Number} newSize the new width or height
34288          */
34289         "resize" : true,
34290         /**
34291          * @event moved
34292          * Fires when the splitter is moved
34293          * @param {Roo.bootstrap.SplitBar} this
34294          * @param {Number} newSize the new width or height
34295          */
34296         "moved" : true,
34297         /**
34298          * @event beforeresize
34299          * Fires before the splitter is dragged
34300          * @param {Roo.bootstrap.SplitBar} this
34301          */
34302         "beforeresize" : true,
34303
34304         "beforeapply" : true
34305     });
34306
34307     Roo.util.Observable.call(this);
34308 };
34309
34310 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34311     onStartProxyDrag : function(x, y){
34312         this.fireEvent("beforeresize", this);
34313         if(!this.overlay){
34314             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34315             o.unselectable();
34316             o.enableDisplayMode("block");
34317             // all splitbars share the same overlay
34318             Roo.bootstrap.SplitBar.prototype.overlay = o;
34319         }
34320         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34321         this.overlay.show();
34322         Roo.get(this.proxy).setDisplayed("block");
34323         var size = this.adapter.getElementSize(this);
34324         this.activeMinSize = this.getMinimumSize();;
34325         this.activeMaxSize = this.getMaximumSize();;
34326         var c1 = size - this.activeMinSize;
34327         var c2 = Math.max(this.activeMaxSize - size, 0);
34328         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34329             this.dd.resetConstraints();
34330             this.dd.setXConstraint(
34331                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34332                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34333             );
34334             this.dd.setYConstraint(0, 0);
34335         }else{
34336             this.dd.resetConstraints();
34337             this.dd.setXConstraint(0, 0);
34338             this.dd.setYConstraint(
34339                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34340                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34341             );
34342          }
34343         this.dragSpecs.startSize = size;
34344         this.dragSpecs.startPoint = [x, y];
34345         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34346     },
34347     
34348     /** 
34349      * @private Called after the drag operation by the DDProxy
34350      */
34351     onEndProxyDrag : function(e){
34352         Roo.get(this.proxy).setDisplayed(false);
34353         var endPoint = Roo.lib.Event.getXY(e);
34354         if(this.overlay){
34355             this.overlay.hide();
34356         }
34357         var newSize;
34358         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34359             newSize = this.dragSpecs.startSize + 
34360                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34361                     endPoint[0] - this.dragSpecs.startPoint[0] :
34362                     this.dragSpecs.startPoint[0] - endPoint[0]
34363                 );
34364         }else{
34365             newSize = this.dragSpecs.startSize + 
34366                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34367                     endPoint[1] - this.dragSpecs.startPoint[1] :
34368                     this.dragSpecs.startPoint[1] - endPoint[1]
34369                 );
34370         }
34371         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34372         if(newSize != this.dragSpecs.startSize){
34373             if(this.fireEvent('beforeapply', this, newSize) !== false){
34374                 this.adapter.setElementSize(this, newSize);
34375                 this.fireEvent("moved", this, newSize);
34376                 this.fireEvent("resize", this, newSize);
34377             }
34378         }
34379     },
34380     
34381     /**
34382      * Get the adapter this SplitBar uses
34383      * @return The adapter object
34384      */
34385     getAdapter : function(){
34386         return this.adapter;
34387     },
34388     
34389     /**
34390      * Set the adapter this SplitBar uses
34391      * @param {Object} adapter A SplitBar adapter object
34392      */
34393     setAdapter : function(adapter){
34394         this.adapter = adapter;
34395         this.adapter.init(this);
34396     },
34397     
34398     /**
34399      * Gets the minimum size for the resizing element
34400      * @return {Number} The minimum size
34401      */
34402     getMinimumSize : function(){
34403         return this.minSize;
34404     },
34405     
34406     /**
34407      * Sets the minimum size for the resizing element
34408      * @param {Number} minSize The minimum size
34409      */
34410     setMinimumSize : function(minSize){
34411         this.minSize = minSize;
34412     },
34413     
34414     /**
34415      * Gets the maximum size for the resizing element
34416      * @return {Number} The maximum size
34417      */
34418     getMaximumSize : function(){
34419         return this.maxSize;
34420     },
34421     
34422     /**
34423      * Sets the maximum size for the resizing element
34424      * @param {Number} maxSize The maximum size
34425      */
34426     setMaximumSize : function(maxSize){
34427         this.maxSize = maxSize;
34428     },
34429     
34430     /**
34431      * Sets the initialize size for the resizing element
34432      * @param {Number} size The initial size
34433      */
34434     setCurrentSize : function(size){
34435         var oldAnimate = this.animate;
34436         this.animate = false;
34437         this.adapter.setElementSize(this, size);
34438         this.animate = oldAnimate;
34439     },
34440     
34441     /**
34442      * Destroy this splitbar. 
34443      * @param {Boolean} removeEl True to remove the element
34444      */
34445     destroy : function(removeEl){
34446         if(this.shim){
34447             this.shim.remove();
34448         }
34449         this.dd.unreg();
34450         this.proxy.parentNode.removeChild(this.proxy);
34451         if(removeEl){
34452             this.el.remove();
34453         }
34454     }
34455 });
34456
34457 /**
34458  * @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.
34459  */
34460 Roo.bootstrap.SplitBar.createProxy = function(dir){
34461     var proxy = new Roo.Element(document.createElement("div"));
34462     proxy.unselectable();
34463     var cls = 'roo-splitbar-proxy';
34464     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34465     document.body.appendChild(proxy.dom);
34466     return proxy.dom;
34467 };
34468
34469 /** 
34470  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34471  * Default Adapter. It assumes the splitter and resizing element are not positioned
34472  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34473  */
34474 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34475 };
34476
34477 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34478     // do nothing for now
34479     init : function(s){
34480     
34481     },
34482     /**
34483      * Called before drag operations to get the current size of the resizing element. 
34484      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34485      */
34486      getElementSize : function(s){
34487         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34488             return s.resizingEl.getWidth();
34489         }else{
34490             return s.resizingEl.getHeight();
34491         }
34492     },
34493     
34494     /**
34495      * Called after drag operations to set the size of the resizing element.
34496      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34497      * @param {Number} newSize The new size to set
34498      * @param {Function} onComplete A function to be invoked when resizing is complete
34499      */
34500     setElementSize : function(s, newSize, onComplete){
34501         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34502             if(!s.animate){
34503                 s.resizingEl.setWidth(newSize);
34504                 if(onComplete){
34505                     onComplete(s, newSize);
34506                 }
34507             }else{
34508                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34509             }
34510         }else{
34511             
34512             if(!s.animate){
34513                 s.resizingEl.setHeight(newSize);
34514                 if(onComplete){
34515                     onComplete(s, newSize);
34516                 }
34517             }else{
34518                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34519             }
34520         }
34521     }
34522 };
34523
34524 /** 
34525  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34526  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34527  * Adapter that  moves the splitter element to align with the resized sizing element. 
34528  * Used with an absolute positioned SplitBar.
34529  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34530  * document.body, make sure you assign an id to the body element.
34531  */
34532 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34533     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34534     this.container = Roo.get(container);
34535 };
34536
34537 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34538     init : function(s){
34539         this.basic.init(s);
34540     },
34541     
34542     getElementSize : function(s){
34543         return this.basic.getElementSize(s);
34544     },
34545     
34546     setElementSize : function(s, newSize, onComplete){
34547         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34548     },
34549     
34550     moveSplitter : function(s){
34551         var yes = Roo.bootstrap.SplitBar;
34552         switch(s.placement){
34553             case yes.LEFT:
34554                 s.el.setX(s.resizingEl.getRight());
34555                 break;
34556             case yes.RIGHT:
34557                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34558                 break;
34559             case yes.TOP:
34560                 s.el.setY(s.resizingEl.getBottom());
34561                 break;
34562             case yes.BOTTOM:
34563                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34564                 break;
34565         }
34566     }
34567 };
34568
34569 /**
34570  * Orientation constant - Create a vertical SplitBar
34571  * @static
34572  * @type Number
34573  */
34574 Roo.bootstrap.SplitBar.VERTICAL = 1;
34575
34576 /**
34577  * Orientation constant - Create a horizontal SplitBar
34578  * @static
34579  * @type Number
34580  */
34581 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34582
34583 /**
34584  * Placement constant - The resizing element is to the left of the splitter element
34585  * @static
34586  * @type Number
34587  */
34588 Roo.bootstrap.SplitBar.LEFT = 1;
34589
34590 /**
34591  * Placement constant - The resizing element is to the right of the splitter element
34592  * @static
34593  * @type Number
34594  */
34595 Roo.bootstrap.SplitBar.RIGHT = 2;
34596
34597 /**
34598  * Placement constant - The resizing element is positioned above the splitter element
34599  * @static
34600  * @type Number
34601  */
34602 Roo.bootstrap.SplitBar.TOP = 3;
34603
34604 /**
34605  * Placement constant - The resizing element is positioned under splitter element
34606  * @static
34607  * @type Number
34608  */
34609 Roo.bootstrap.SplitBar.BOTTOM = 4;
34610 Roo.namespace("Roo.bootstrap.layout");/*
34611  * Based on:
34612  * Ext JS Library 1.1.1
34613  * Copyright(c) 2006-2007, Ext JS, LLC.
34614  *
34615  * Originally Released Under LGPL - original licence link has changed is not relivant.
34616  *
34617  * Fork - LGPL
34618  * <script type="text/javascript">
34619  */
34620
34621 /**
34622  * @class Roo.bootstrap.layout.Manager
34623  * @extends Roo.bootstrap.Component
34624  * Base class for layout managers.
34625  */
34626 Roo.bootstrap.layout.Manager = function(config)
34627 {
34628     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34629
34630
34631
34632
34633
34634     /** false to disable window resize monitoring @type Boolean */
34635     this.monitorWindowResize = true;
34636     this.regions = {};
34637     this.addEvents({
34638         /**
34639          * @event layout
34640          * Fires when a layout is performed.
34641          * @param {Roo.LayoutManager} this
34642          */
34643         "layout" : true,
34644         /**
34645          * @event regionresized
34646          * Fires when the user resizes a region.
34647          * @param {Roo.LayoutRegion} region The resized region
34648          * @param {Number} newSize The new size (width for east/west, height for north/south)
34649          */
34650         "regionresized" : true,
34651         /**
34652          * @event regioncollapsed
34653          * Fires when a region is collapsed.
34654          * @param {Roo.LayoutRegion} region The collapsed region
34655          */
34656         "regioncollapsed" : true,
34657         /**
34658          * @event regionexpanded
34659          * Fires when a region is expanded.
34660          * @param {Roo.LayoutRegion} region The expanded region
34661          */
34662         "regionexpanded" : true
34663     });
34664     this.updating = false;
34665
34666     if (config.el) {
34667         this.el = Roo.get(config.el);
34668         this.initEvents();
34669     }
34670
34671 };
34672
34673 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34674
34675
34676     regions : null,
34677
34678     monitorWindowResize : true,
34679
34680
34681     updating : false,
34682
34683
34684     onRender : function(ct, position)
34685     {
34686         if(!this.el){
34687             this.el = Roo.get(ct);
34688             this.initEvents();
34689         }
34690         //this.fireEvent('render',this);
34691     },
34692
34693
34694     initEvents: function()
34695     {
34696
34697
34698         // ie scrollbar fix
34699         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34700             document.body.scroll = "no";
34701         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34702             this.el.position('relative');
34703         }
34704         this.id = this.el.id;
34705         this.el.addClass("roo-layout-container");
34706         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34707         if(this.el.dom != document.body ) {
34708             this.el.on('resize', this.layout,this);
34709             this.el.on('show', this.layout,this);
34710         }
34711
34712     },
34713
34714     /**
34715      * Returns true if this layout is currently being updated
34716      * @return {Boolean}
34717      */
34718     isUpdating : function(){
34719         return this.updating;
34720     },
34721
34722     /**
34723      * Suspend the LayoutManager from doing auto-layouts while
34724      * making multiple add or remove calls
34725      */
34726     beginUpdate : function(){
34727         this.updating = true;
34728     },
34729
34730     /**
34731      * Restore auto-layouts and optionally disable the manager from performing a layout
34732      * @param {Boolean} noLayout true to disable a layout update
34733      */
34734     endUpdate : function(noLayout){
34735         this.updating = false;
34736         if(!noLayout){
34737             this.layout();
34738         }
34739     },
34740
34741     layout: function(){
34742         // abstract...
34743     },
34744
34745     onRegionResized : function(region, newSize){
34746         this.fireEvent("regionresized", region, newSize);
34747         this.layout();
34748     },
34749
34750     onRegionCollapsed : function(region){
34751         this.fireEvent("regioncollapsed", region);
34752     },
34753
34754     onRegionExpanded : function(region){
34755         this.fireEvent("regionexpanded", region);
34756     },
34757
34758     /**
34759      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34760      * performs box-model adjustments.
34761      * @return {Object} The size as an object {width: (the width), height: (the height)}
34762      */
34763     getViewSize : function()
34764     {
34765         var size;
34766         if(this.el.dom != document.body){
34767             size = this.el.getSize();
34768         }else{
34769             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34770         }
34771         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34772         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34773         return size;
34774     },
34775
34776     /**
34777      * Returns the Element this layout is bound to.
34778      * @return {Roo.Element}
34779      */
34780     getEl : function(){
34781         return this.el;
34782     },
34783
34784     /**
34785      * Returns the specified region.
34786      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34787      * @return {Roo.LayoutRegion}
34788      */
34789     getRegion : function(target){
34790         return this.regions[target.toLowerCase()];
34791     },
34792
34793     onWindowResize : function(){
34794         if(this.monitorWindowResize){
34795             this.layout();
34796         }
34797     }
34798 });
34799 /*
34800  * Based on:
34801  * Ext JS Library 1.1.1
34802  * Copyright(c) 2006-2007, Ext JS, LLC.
34803  *
34804  * Originally Released Under LGPL - original licence link has changed is not relivant.
34805  *
34806  * Fork - LGPL
34807  * <script type="text/javascript">
34808  */
34809 /**
34810  * @class Roo.bootstrap.layout.Border
34811  * @extends Roo.bootstrap.layout.Manager
34812  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34813  * please see: examples/bootstrap/nested.html<br><br>
34814  
34815 <b>The container the layout is rendered into can be either the body element or any other element.
34816 If it is not the body element, the container needs to either be an absolute positioned element,
34817 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34818 the container size if it is not the body element.</b>
34819
34820 * @constructor
34821 * Create a new Border
34822 * @param {Object} config Configuration options
34823  */
34824 Roo.bootstrap.layout.Border = function(config){
34825     config = config || {};
34826     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34827     
34828     
34829     
34830     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34831         if(config[region]){
34832             config[region].region = region;
34833             this.addRegion(config[region]);
34834         }
34835     },this);
34836     
34837 };
34838
34839 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34840
34841 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34842     /**
34843      * Creates and adds a new region if it doesn't already exist.
34844      * @param {String} target The target region key (north, south, east, west or center).
34845      * @param {Object} config The regions config object
34846      * @return {BorderLayoutRegion} The new region
34847      */
34848     addRegion : function(config)
34849     {
34850         if(!this.regions[config.region]){
34851             var r = this.factory(config);
34852             this.bindRegion(r);
34853         }
34854         return this.regions[config.region];
34855     },
34856
34857     // private (kinda)
34858     bindRegion : function(r){
34859         this.regions[r.config.region] = r;
34860         
34861         r.on("visibilitychange",    this.layout, this);
34862         r.on("paneladded",          this.layout, this);
34863         r.on("panelremoved",        this.layout, this);
34864         r.on("invalidated",         this.layout, this);
34865         r.on("resized",             this.onRegionResized, this);
34866         r.on("collapsed",           this.onRegionCollapsed, this);
34867         r.on("expanded",            this.onRegionExpanded, this);
34868     },
34869
34870     /**
34871      * Performs a layout update.
34872      */
34873     layout : function()
34874     {
34875         if(this.updating) {
34876             return;
34877         }
34878         
34879         // render all the rebions if they have not been done alreayd?
34880         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34881             if(this.regions[region] && !this.regions[region].bodyEl){
34882                 this.regions[region].onRender(this.el)
34883             }
34884         },this);
34885         
34886         var size = this.getViewSize();
34887         var w = size.width;
34888         var h = size.height;
34889         var centerW = w;
34890         var centerH = h;
34891         var centerY = 0;
34892         var centerX = 0;
34893         //var x = 0, y = 0;
34894
34895         var rs = this.regions;
34896         var north = rs["north"];
34897         var south = rs["south"]; 
34898         var west = rs["west"];
34899         var east = rs["east"];
34900         var center = rs["center"];
34901         //if(this.hideOnLayout){ // not supported anymore
34902             //c.el.setStyle("display", "none");
34903         //}
34904         if(north && north.isVisible()){
34905             var b = north.getBox();
34906             var m = north.getMargins();
34907             b.width = w - (m.left+m.right);
34908             b.x = m.left;
34909             b.y = m.top;
34910             centerY = b.height + b.y + m.bottom;
34911             centerH -= centerY;
34912             north.updateBox(this.safeBox(b));
34913         }
34914         if(south && south.isVisible()){
34915             var b = south.getBox();
34916             var m = south.getMargins();
34917             b.width = w - (m.left+m.right);
34918             b.x = m.left;
34919             var totalHeight = (b.height + m.top + m.bottom);
34920             b.y = h - totalHeight + m.top;
34921             centerH -= totalHeight;
34922             south.updateBox(this.safeBox(b));
34923         }
34924         if(west && west.isVisible()){
34925             var b = west.getBox();
34926             var m = west.getMargins();
34927             b.height = centerH - (m.top+m.bottom);
34928             b.x = m.left;
34929             b.y = centerY + m.top;
34930             var totalWidth = (b.width + m.left + m.right);
34931             centerX += totalWidth;
34932             centerW -= totalWidth;
34933             west.updateBox(this.safeBox(b));
34934         }
34935         if(east && east.isVisible()){
34936             var b = east.getBox();
34937             var m = east.getMargins();
34938             b.height = centerH - (m.top+m.bottom);
34939             var totalWidth = (b.width + m.left + m.right);
34940             b.x = w - totalWidth + m.left;
34941             b.y = centerY + m.top;
34942             centerW -= totalWidth;
34943             east.updateBox(this.safeBox(b));
34944         }
34945         if(center){
34946             var m = center.getMargins();
34947             var centerBox = {
34948                 x: centerX + m.left,
34949                 y: centerY + m.top,
34950                 width: centerW - (m.left+m.right),
34951                 height: centerH - (m.top+m.bottom)
34952             };
34953             //if(this.hideOnLayout){
34954                 //center.el.setStyle("display", "block");
34955             //}
34956             center.updateBox(this.safeBox(centerBox));
34957         }
34958         this.el.repaint();
34959         this.fireEvent("layout", this);
34960     },
34961
34962     // private
34963     safeBox : function(box){
34964         box.width = Math.max(0, box.width);
34965         box.height = Math.max(0, box.height);
34966         return box;
34967     },
34968
34969     /**
34970      * Adds a ContentPanel (or subclass) to this layout.
34971      * @param {String} target The target region key (north, south, east, west or center).
34972      * @param {Roo.ContentPanel} panel The panel to add
34973      * @return {Roo.ContentPanel} The added panel
34974      */
34975     add : function(target, panel){
34976          
34977         target = target.toLowerCase();
34978         return this.regions[target].add(panel);
34979     },
34980
34981     /**
34982      * Remove a ContentPanel (or subclass) to this layout.
34983      * @param {String} target The target region key (north, south, east, west or center).
34984      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34985      * @return {Roo.ContentPanel} The removed panel
34986      */
34987     remove : function(target, panel){
34988         target = target.toLowerCase();
34989         return this.regions[target].remove(panel);
34990     },
34991
34992     /**
34993      * Searches all regions for a panel with the specified id
34994      * @param {String} panelId
34995      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34996      */
34997     findPanel : function(panelId){
34998         var rs = this.regions;
34999         for(var target in rs){
35000             if(typeof rs[target] != "function"){
35001                 var p = rs[target].getPanel(panelId);
35002                 if(p){
35003                     return p;
35004                 }
35005             }
35006         }
35007         return null;
35008     },
35009
35010     /**
35011      * Searches all regions for a panel with the specified id and activates (shows) it.
35012      * @param {String/ContentPanel} panelId The panels id or the panel itself
35013      * @return {Roo.ContentPanel} The shown panel or null
35014      */
35015     showPanel : function(panelId) {
35016       var rs = this.regions;
35017       for(var target in rs){
35018          var r = rs[target];
35019          if(typeof r != "function"){
35020             if(r.hasPanel(panelId)){
35021                return r.showPanel(panelId);
35022             }
35023          }
35024       }
35025       return null;
35026    },
35027
35028    /**
35029      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35030      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35031      */
35032    /*
35033     restoreState : function(provider){
35034         if(!provider){
35035             provider = Roo.state.Manager;
35036         }
35037         var sm = new Roo.LayoutStateManager();
35038         sm.init(this, provider);
35039     },
35040 */
35041  
35042  
35043     /**
35044      * Adds a xtype elements to the layout.
35045      * <pre><code>
35046
35047 layout.addxtype({
35048        xtype : 'ContentPanel',
35049        region: 'west',
35050        items: [ .... ]
35051    }
35052 );
35053
35054 layout.addxtype({
35055         xtype : 'NestedLayoutPanel',
35056         region: 'west',
35057         layout: {
35058            center: { },
35059            west: { }   
35060         },
35061         items : [ ... list of content panels or nested layout panels.. ]
35062    }
35063 );
35064 </code></pre>
35065      * @param {Object} cfg Xtype definition of item to add.
35066      */
35067     addxtype : function(cfg)
35068     {
35069         // basically accepts a pannel...
35070         // can accept a layout region..!?!?
35071         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35072         
35073         
35074         // theory?  children can only be panels??
35075         
35076         //if (!cfg.xtype.match(/Panel$/)) {
35077         //    return false;
35078         //}
35079         var ret = false;
35080         
35081         if (typeof(cfg.region) == 'undefined') {
35082             Roo.log("Failed to add Panel, region was not set");
35083             Roo.log(cfg);
35084             return false;
35085         }
35086         var region = cfg.region;
35087         delete cfg.region;
35088         
35089           
35090         var xitems = [];
35091         if (cfg.items) {
35092             xitems = cfg.items;
35093             delete cfg.items;
35094         }
35095         var nb = false;
35096         
35097         switch(cfg.xtype) 
35098         {
35099             case 'Content':  // ContentPanel (el, cfg)
35100             case 'Scroll':  // ContentPanel (el, cfg)
35101             case 'View': 
35102                 cfg.autoCreate = true;
35103                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35104                 //} else {
35105                 //    var el = this.el.createChild();
35106                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35107                 //}
35108                 
35109                 this.add(region, ret);
35110                 break;
35111             
35112             /*
35113             case 'TreePanel': // our new panel!
35114                 cfg.el = this.el.createChild();
35115                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35116                 this.add(region, ret);
35117                 break;
35118             */
35119             
35120             case 'Nest': 
35121                 // create a new Layout (which is  a Border Layout...
35122                 
35123                 var clayout = cfg.layout;
35124                 clayout.el  = this.el.createChild();
35125                 clayout.items   = clayout.items  || [];
35126                 
35127                 delete cfg.layout;
35128                 
35129                 // replace this exitems with the clayout ones..
35130                 xitems = clayout.items;
35131                  
35132                 // force background off if it's in center...
35133                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35134                     cfg.background = false;
35135                 }
35136                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35137                 
35138                 
35139                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35140                 //console.log('adding nested layout panel '  + cfg.toSource());
35141                 this.add(region, ret);
35142                 nb = {}; /// find first...
35143                 break;
35144             
35145             case 'Grid':
35146                 
35147                 // needs grid and region
35148                 
35149                 //var el = this.getRegion(region).el.createChild();
35150                 /*
35151                  *var el = this.el.createChild();
35152                 // create the grid first...
35153                 cfg.grid.container = el;
35154                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35155                 */
35156                 
35157                 if (region == 'center' && this.active ) {
35158                     cfg.background = false;
35159                 }
35160                 
35161                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35162                 
35163                 this.add(region, ret);
35164                 /*
35165                 if (cfg.background) {
35166                     // render grid on panel activation (if panel background)
35167                     ret.on('activate', function(gp) {
35168                         if (!gp.grid.rendered) {
35169                     //        gp.grid.render(el);
35170                         }
35171                     });
35172                 } else {
35173                   //  cfg.grid.render(el);
35174                 }
35175                 */
35176                 break;
35177            
35178            
35179             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35180                 // it was the old xcomponent building that caused this before.
35181                 // espeically if border is the top element in the tree.
35182                 ret = this;
35183                 break; 
35184                 
35185                     
35186                 
35187                 
35188                 
35189             default:
35190                 /*
35191                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35192                     
35193                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35194                     this.add(region, ret);
35195                 } else {
35196                 */
35197                     Roo.log(cfg);
35198                     throw "Can not add '" + cfg.xtype + "' to Border";
35199                     return null;
35200              
35201                                 
35202              
35203         }
35204         this.beginUpdate();
35205         // add children..
35206         var region = '';
35207         var abn = {};
35208         Roo.each(xitems, function(i)  {
35209             region = nb && i.region ? i.region : false;
35210             
35211             var add = ret.addxtype(i);
35212            
35213             if (region) {
35214                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35215                 if (!i.background) {
35216                     abn[region] = nb[region] ;
35217                 }
35218             }
35219             
35220         });
35221         this.endUpdate();
35222
35223         // make the last non-background panel active..
35224         //if (nb) { Roo.log(abn); }
35225         if (nb) {
35226             
35227             for(var r in abn) {
35228                 region = this.getRegion(r);
35229                 if (region) {
35230                     // tried using nb[r], but it does not work..
35231                      
35232                     region.showPanel(abn[r]);
35233                    
35234                 }
35235             }
35236         }
35237         return ret;
35238         
35239     },
35240     
35241     
35242 // private
35243     factory : function(cfg)
35244     {
35245         
35246         var validRegions = Roo.bootstrap.layout.Border.regions;
35247
35248         var target = cfg.region;
35249         cfg.mgr = this;
35250         
35251         var r = Roo.bootstrap.layout;
35252         Roo.log(target);
35253         switch(target){
35254             case "north":
35255                 return new r.North(cfg);
35256             case "south":
35257                 return new r.South(cfg);
35258             case "east":
35259                 return new r.East(cfg);
35260             case "west":
35261                 return new r.West(cfg);
35262             case "center":
35263                 return new r.Center(cfg);
35264         }
35265         throw 'Layout region "'+target+'" not supported.';
35266     }
35267     
35268     
35269 });
35270  /*
35271  * Based on:
35272  * Ext JS Library 1.1.1
35273  * Copyright(c) 2006-2007, Ext JS, LLC.
35274  *
35275  * Originally Released Under LGPL - original licence link has changed is not relivant.
35276  *
35277  * Fork - LGPL
35278  * <script type="text/javascript">
35279  */
35280  
35281 /**
35282  * @class Roo.bootstrap.layout.Basic
35283  * @extends Roo.util.Observable
35284  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35285  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35286  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35287  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35288  * @cfg {string}   region  the region that it inhabits..
35289  * @cfg {bool}   skipConfig skip config?
35290  * 
35291
35292  */
35293 Roo.bootstrap.layout.Basic = function(config){
35294     
35295     this.mgr = config.mgr;
35296     
35297     this.position = config.region;
35298     
35299     var skipConfig = config.skipConfig;
35300     
35301     this.events = {
35302         /**
35303          * @scope Roo.BasicLayoutRegion
35304          */
35305         
35306         /**
35307          * @event beforeremove
35308          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35309          * @param {Roo.LayoutRegion} this
35310          * @param {Roo.ContentPanel} panel The panel
35311          * @param {Object} e The cancel event object
35312          */
35313         "beforeremove" : true,
35314         /**
35315          * @event invalidated
35316          * Fires when the layout for this region is changed.
35317          * @param {Roo.LayoutRegion} this
35318          */
35319         "invalidated" : true,
35320         /**
35321          * @event visibilitychange
35322          * Fires when this region is shown or hidden 
35323          * @param {Roo.LayoutRegion} this
35324          * @param {Boolean} visibility true or false
35325          */
35326         "visibilitychange" : true,
35327         /**
35328          * @event paneladded
35329          * Fires when a panel is added. 
35330          * @param {Roo.LayoutRegion} this
35331          * @param {Roo.ContentPanel} panel The panel
35332          */
35333         "paneladded" : true,
35334         /**
35335          * @event panelremoved
35336          * Fires when a panel is removed. 
35337          * @param {Roo.LayoutRegion} this
35338          * @param {Roo.ContentPanel} panel The panel
35339          */
35340         "panelremoved" : true,
35341         /**
35342          * @event beforecollapse
35343          * Fires when this region before collapse.
35344          * @param {Roo.LayoutRegion} this
35345          */
35346         "beforecollapse" : true,
35347         /**
35348          * @event collapsed
35349          * Fires when this region is collapsed.
35350          * @param {Roo.LayoutRegion} this
35351          */
35352         "collapsed" : true,
35353         /**
35354          * @event expanded
35355          * Fires when this region is expanded.
35356          * @param {Roo.LayoutRegion} this
35357          */
35358         "expanded" : true,
35359         /**
35360          * @event slideshow
35361          * Fires when this region is slid into view.
35362          * @param {Roo.LayoutRegion} this
35363          */
35364         "slideshow" : true,
35365         /**
35366          * @event slidehide
35367          * Fires when this region slides out of view. 
35368          * @param {Roo.LayoutRegion} this
35369          */
35370         "slidehide" : true,
35371         /**
35372          * @event panelactivated
35373          * Fires when a panel is activated. 
35374          * @param {Roo.LayoutRegion} this
35375          * @param {Roo.ContentPanel} panel The activated panel
35376          */
35377         "panelactivated" : true,
35378         /**
35379          * @event resized
35380          * Fires when the user resizes this region. 
35381          * @param {Roo.LayoutRegion} this
35382          * @param {Number} newSize The new size (width for east/west, height for north/south)
35383          */
35384         "resized" : true
35385     };
35386     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35387     this.panels = new Roo.util.MixedCollection();
35388     this.panels.getKey = this.getPanelId.createDelegate(this);
35389     this.box = null;
35390     this.activePanel = null;
35391     // ensure listeners are added...
35392     
35393     if (config.listeners || config.events) {
35394         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35395             listeners : config.listeners || {},
35396             events : config.events || {}
35397         });
35398     }
35399     
35400     if(skipConfig !== true){
35401         this.applyConfig(config);
35402     }
35403 };
35404
35405 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35406 {
35407     getPanelId : function(p){
35408         return p.getId();
35409     },
35410     
35411     applyConfig : function(config){
35412         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35413         this.config = config;
35414         
35415     },
35416     
35417     /**
35418      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35419      * the width, for horizontal (north, south) the height.
35420      * @param {Number} newSize The new width or height
35421      */
35422     resizeTo : function(newSize){
35423         var el = this.el ? this.el :
35424                  (this.activePanel ? this.activePanel.getEl() : null);
35425         if(el){
35426             switch(this.position){
35427                 case "east":
35428                 case "west":
35429                     el.setWidth(newSize);
35430                     this.fireEvent("resized", this, newSize);
35431                 break;
35432                 case "north":
35433                 case "south":
35434                     el.setHeight(newSize);
35435                     this.fireEvent("resized", this, newSize);
35436                 break;                
35437             }
35438         }
35439     },
35440     
35441     getBox : function(){
35442         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35443     },
35444     
35445     getMargins : function(){
35446         return this.margins;
35447     },
35448     
35449     updateBox : function(box){
35450         this.box = box;
35451         var el = this.activePanel.getEl();
35452         el.dom.style.left = box.x + "px";
35453         el.dom.style.top = box.y + "px";
35454         this.activePanel.setSize(box.width, box.height);
35455     },
35456     
35457     /**
35458      * Returns the container element for this region.
35459      * @return {Roo.Element}
35460      */
35461     getEl : function(){
35462         return this.activePanel;
35463     },
35464     
35465     /**
35466      * Returns true if this region is currently visible.
35467      * @return {Boolean}
35468      */
35469     isVisible : function(){
35470         return this.activePanel ? true : false;
35471     },
35472     
35473     setActivePanel : function(panel){
35474         panel = this.getPanel(panel);
35475         if(this.activePanel && this.activePanel != panel){
35476             this.activePanel.setActiveState(false);
35477             this.activePanel.getEl().setLeftTop(-10000,-10000);
35478         }
35479         this.activePanel = panel;
35480         panel.setActiveState(true);
35481         if(this.box){
35482             panel.setSize(this.box.width, this.box.height);
35483         }
35484         this.fireEvent("panelactivated", this, panel);
35485         this.fireEvent("invalidated");
35486     },
35487     
35488     /**
35489      * Show the specified panel.
35490      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35491      * @return {Roo.ContentPanel} The shown panel or null
35492      */
35493     showPanel : function(panel){
35494         panel = this.getPanel(panel);
35495         if(panel){
35496             this.setActivePanel(panel);
35497         }
35498         return panel;
35499     },
35500     
35501     /**
35502      * Get the active panel for this region.
35503      * @return {Roo.ContentPanel} The active panel or null
35504      */
35505     getActivePanel : function(){
35506         return this.activePanel;
35507     },
35508     
35509     /**
35510      * Add the passed ContentPanel(s)
35511      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35512      * @return {Roo.ContentPanel} The panel added (if only one was added)
35513      */
35514     add : function(panel){
35515         if(arguments.length > 1){
35516             for(var i = 0, len = arguments.length; i < len; i++) {
35517                 this.add(arguments[i]);
35518             }
35519             return null;
35520         }
35521         if(this.hasPanel(panel)){
35522             this.showPanel(panel);
35523             return panel;
35524         }
35525         var el = panel.getEl();
35526         if(el.dom.parentNode != this.mgr.el.dom){
35527             this.mgr.el.dom.appendChild(el.dom);
35528         }
35529         if(panel.setRegion){
35530             panel.setRegion(this);
35531         }
35532         this.panels.add(panel);
35533         el.setStyle("position", "absolute");
35534         if(!panel.background){
35535             this.setActivePanel(panel);
35536             if(this.config.initialSize && this.panels.getCount()==1){
35537                 this.resizeTo(this.config.initialSize);
35538             }
35539         }
35540         this.fireEvent("paneladded", this, panel);
35541         return panel;
35542     },
35543     
35544     /**
35545      * Returns true if the panel is in this region.
35546      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35547      * @return {Boolean}
35548      */
35549     hasPanel : function(panel){
35550         if(typeof panel == "object"){ // must be panel obj
35551             panel = panel.getId();
35552         }
35553         return this.getPanel(panel) ? true : false;
35554     },
35555     
35556     /**
35557      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35558      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35559      * @param {Boolean} preservePanel Overrides the config preservePanel option
35560      * @return {Roo.ContentPanel} The panel that was removed
35561      */
35562     remove : function(panel, preservePanel){
35563         panel = this.getPanel(panel);
35564         if(!panel){
35565             return null;
35566         }
35567         var e = {};
35568         this.fireEvent("beforeremove", this, panel, e);
35569         if(e.cancel === true){
35570             return null;
35571         }
35572         var panelId = panel.getId();
35573         this.panels.removeKey(panelId);
35574         return panel;
35575     },
35576     
35577     /**
35578      * Returns the panel specified or null if it's not in this region.
35579      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35580      * @return {Roo.ContentPanel}
35581      */
35582     getPanel : function(id){
35583         if(typeof id == "object"){ // must be panel obj
35584             return id;
35585         }
35586         return this.panels.get(id);
35587     },
35588     
35589     /**
35590      * Returns this regions position (north/south/east/west/center).
35591      * @return {String} 
35592      */
35593     getPosition: function(){
35594         return this.position;    
35595     }
35596 });/*
35597  * Based on:
35598  * Ext JS Library 1.1.1
35599  * Copyright(c) 2006-2007, Ext JS, LLC.
35600  *
35601  * Originally Released Under LGPL - original licence link has changed is not relivant.
35602  *
35603  * Fork - LGPL
35604  * <script type="text/javascript">
35605  */
35606  
35607 /**
35608  * @class Roo.bootstrap.layout.Region
35609  * @extends Roo.bootstrap.layout.Basic
35610  * This class represents a region in a layout manager.
35611  
35612  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35613  * @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})
35614  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35615  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35616  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35617  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35618  * @cfg {String}    title           The title for the region (overrides panel titles)
35619  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35620  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35621  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35622  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35623  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35624  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35625  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35626  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35627  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35628  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35629
35630  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35631  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35632  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35633  * @cfg {Number}    width           For East/West panels
35634  * @cfg {Number}    height          For North/South panels
35635  * @cfg {Boolean}   split           To show the splitter
35636  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35637  * 
35638  * @cfg {string}   cls             Extra CSS classes to add to region
35639  * 
35640  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35641  * @cfg {string}   region  the region that it inhabits..
35642  *
35643
35644  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35645  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35646
35647  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35648  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35649  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35650  */
35651 Roo.bootstrap.layout.Region = function(config)
35652 {
35653     this.applyConfig(config);
35654
35655     var mgr = config.mgr;
35656     var pos = config.region;
35657     config.skipConfig = true;
35658     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35659     
35660     if (mgr.el) {
35661         this.onRender(mgr.el);   
35662     }
35663      
35664     this.visible = true;
35665     this.collapsed = false;
35666     this.unrendered_panels = [];
35667 };
35668
35669 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35670
35671     position: '', // set by wrapper (eg. north/south etc..)
35672     unrendered_panels : null,  // unrendered panels.
35673     createBody : function(){
35674         /** This region's body element 
35675         * @type Roo.Element */
35676         this.bodyEl = this.el.createChild({
35677                 tag: "div",
35678                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35679         });
35680     },
35681
35682     onRender: function(ctr, pos)
35683     {
35684         var dh = Roo.DomHelper;
35685         /** This region's container element 
35686         * @type Roo.Element */
35687         this.el = dh.append(ctr.dom, {
35688                 tag: "div",
35689                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35690             }, true);
35691         /** This region's title element 
35692         * @type Roo.Element */
35693     
35694         this.titleEl = dh.append(this.el.dom,
35695             {
35696                     tag: "div",
35697                     unselectable: "on",
35698                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35699                     children:[
35700                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35701                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35702                     ]}, true);
35703         
35704         this.titleEl.enableDisplayMode();
35705         /** This region's title text element 
35706         * @type HTMLElement */
35707         this.titleTextEl = this.titleEl.dom.firstChild;
35708         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35709         /*
35710         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35711         this.closeBtn.enableDisplayMode();
35712         this.closeBtn.on("click", this.closeClicked, this);
35713         this.closeBtn.hide();
35714     */
35715         this.createBody(this.config);
35716         if(this.config.hideWhenEmpty){
35717             this.hide();
35718             this.on("paneladded", this.validateVisibility, this);
35719             this.on("panelremoved", this.validateVisibility, this);
35720         }
35721         if(this.autoScroll){
35722             this.bodyEl.setStyle("overflow", "auto");
35723         }else{
35724             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35725         }
35726         //if(c.titlebar !== false){
35727             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35728                 this.titleEl.hide();
35729             }else{
35730                 this.titleEl.show();
35731                 if(this.config.title){
35732                     this.titleTextEl.innerHTML = this.config.title;
35733                 }
35734             }
35735         //}
35736         if(this.config.collapsed){
35737             this.collapse(true);
35738         }
35739         if(this.config.hidden){
35740             this.hide();
35741         }
35742         
35743         if (this.unrendered_panels && this.unrendered_panels.length) {
35744             for (var i =0;i< this.unrendered_panels.length; i++) {
35745                 this.add(this.unrendered_panels[i]);
35746             }
35747             this.unrendered_panels = null;
35748             
35749         }
35750         
35751     },
35752     
35753     applyConfig : function(c)
35754     {
35755         /*
35756          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35757             var dh = Roo.DomHelper;
35758             if(c.titlebar !== false){
35759                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35760                 this.collapseBtn.on("click", this.collapse, this);
35761                 this.collapseBtn.enableDisplayMode();
35762                 /*
35763                 if(c.showPin === true || this.showPin){
35764                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35765                     this.stickBtn.enableDisplayMode();
35766                     this.stickBtn.on("click", this.expand, this);
35767                     this.stickBtn.hide();
35768                 }
35769                 
35770             }
35771             */
35772             /** This region's collapsed element
35773             * @type Roo.Element */
35774             /*
35775              *
35776             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35777                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35778             ]}, true);
35779             
35780             if(c.floatable !== false){
35781                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35782                this.collapsedEl.on("click", this.collapseClick, this);
35783             }
35784
35785             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35786                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35787                    id: "message", unselectable: "on", style:{"float":"left"}});
35788                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35789              }
35790             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35791             this.expandBtn.on("click", this.expand, this);
35792             
35793         }
35794         
35795         if(this.collapseBtn){
35796             this.collapseBtn.setVisible(c.collapsible == true);
35797         }
35798         
35799         this.cmargins = c.cmargins || this.cmargins ||
35800                          (this.position == "west" || this.position == "east" ?
35801                              {top: 0, left: 2, right:2, bottom: 0} :
35802                              {top: 2, left: 0, right:0, bottom: 2});
35803         */
35804         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35805         
35806         
35807         this.bottomTabs = c.tabPosition != "top";
35808         
35809         this.autoScroll = c.autoScroll || false;
35810         
35811         
35812        
35813         
35814         this.duration = c.duration || .30;
35815         this.slideDuration = c.slideDuration || .45;
35816         this.config = c;
35817        
35818     },
35819     /**
35820      * Returns true if this region is currently visible.
35821      * @return {Boolean}
35822      */
35823     isVisible : function(){
35824         return this.visible;
35825     },
35826
35827     /**
35828      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35829      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35830      */
35831     //setCollapsedTitle : function(title){
35832     //    title = title || "&#160;";
35833      //   if(this.collapsedTitleTextEl){
35834       //      this.collapsedTitleTextEl.innerHTML = title;
35835        // }
35836     //},
35837
35838     getBox : function(){
35839         var b;
35840       //  if(!this.collapsed){
35841             b = this.el.getBox(false, true);
35842        // }else{
35843           //  b = this.collapsedEl.getBox(false, true);
35844         //}
35845         return b;
35846     },
35847
35848     getMargins : function(){
35849         return this.margins;
35850         //return this.collapsed ? this.cmargins : this.margins;
35851     },
35852 /*
35853     highlight : function(){
35854         this.el.addClass("x-layout-panel-dragover");
35855     },
35856
35857     unhighlight : function(){
35858         this.el.removeClass("x-layout-panel-dragover");
35859     },
35860 */
35861     updateBox : function(box)
35862     {
35863         if (!this.bodyEl) {
35864             return; // not rendered yet..
35865         }
35866         
35867         this.box = box;
35868         if(!this.collapsed){
35869             this.el.dom.style.left = box.x + "px";
35870             this.el.dom.style.top = box.y + "px";
35871             this.updateBody(box.width, box.height);
35872         }else{
35873             this.collapsedEl.dom.style.left = box.x + "px";
35874             this.collapsedEl.dom.style.top = box.y + "px";
35875             this.collapsedEl.setSize(box.width, box.height);
35876         }
35877         if(this.tabs){
35878             this.tabs.autoSizeTabs();
35879         }
35880     },
35881
35882     updateBody : function(w, h)
35883     {
35884         if(w !== null){
35885             this.el.setWidth(w);
35886             w -= this.el.getBorderWidth("rl");
35887             if(this.config.adjustments){
35888                 w += this.config.adjustments[0];
35889             }
35890         }
35891         if(h !== null && h > 0){
35892             this.el.setHeight(h);
35893             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35894             h -= this.el.getBorderWidth("tb");
35895             if(this.config.adjustments){
35896                 h += this.config.adjustments[1];
35897             }
35898             this.bodyEl.setHeight(h);
35899             if(this.tabs){
35900                 h = this.tabs.syncHeight(h);
35901             }
35902         }
35903         if(this.panelSize){
35904             w = w !== null ? w : this.panelSize.width;
35905             h = h !== null ? h : this.panelSize.height;
35906         }
35907         if(this.activePanel){
35908             var el = this.activePanel.getEl();
35909             w = w !== null ? w : el.getWidth();
35910             h = h !== null ? h : el.getHeight();
35911             this.panelSize = {width: w, height: h};
35912             this.activePanel.setSize(w, h);
35913         }
35914         if(Roo.isIE && this.tabs){
35915             this.tabs.el.repaint();
35916         }
35917     },
35918
35919     /**
35920      * Returns the container element for this region.
35921      * @return {Roo.Element}
35922      */
35923     getEl : function(){
35924         return this.el;
35925     },
35926
35927     /**
35928      * Hides this region.
35929      */
35930     hide : function(){
35931         //if(!this.collapsed){
35932             this.el.dom.style.left = "-2000px";
35933             this.el.hide();
35934         //}else{
35935          //   this.collapsedEl.dom.style.left = "-2000px";
35936          //   this.collapsedEl.hide();
35937        // }
35938         this.visible = false;
35939         this.fireEvent("visibilitychange", this, false);
35940     },
35941
35942     /**
35943      * Shows this region if it was previously hidden.
35944      */
35945     show : function(){
35946         //if(!this.collapsed){
35947             this.el.show();
35948         //}else{
35949         //    this.collapsedEl.show();
35950        // }
35951         this.visible = true;
35952         this.fireEvent("visibilitychange", this, true);
35953     },
35954 /*
35955     closeClicked : function(){
35956         if(this.activePanel){
35957             this.remove(this.activePanel);
35958         }
35959     },
35960
35961     collapseClick : function(e){
35962         if(this.isSlid){
35963            e.stopPropagation();
35964            this.slideIn();
35965         }else{
35966            e.stopPropagation();
35967            this.slideOut();
35968         }
35969     },
35970 */
35971     /**
35972      * Collapses this region.
35973      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35974      */
35975     /*
35976     collapse : function(skipAnim, skipCheck = false){
35977         if(this.collapsed) {
35978             return;
35979         }
35980         
35981         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35982             
35983             this.collapsed = true;
35984             if(this.split){
35985                 this.split.el.hide();
35986             }
35987             if(this.config.animate && skipAnim !== true){
35988                 this.fireEvent("invalidated", this);
35989                 this.animateCollapse();
35990             }else{
35991                 this.el.setLocation(-20000,-20000);
35992                 this.el.hide();
35993                 this.collapsedEl.show();
35994                 this.fireEvent("collapsed", this);
35995                 this.fireEvent("invalidated", this);
35996             }
35997         }
35998         
35999     },
36000 */
36001     animateCollapse : function(){
36002         // overridden
36003     },
36004
36005     /**
36006      * Expands this region if it was previously collapsed.
36007      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36008      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36009      */
36010     /*
36011     expand : function(e, skipAnim){
36012         if(e) {
36013             e.stopPropagation();
36014         }
36015         if(!this.collapsed || this.el.hasActiveFx()) {
36016             return;
36017         }
36018         if(this.isSlid){
36019             this.afterSlideIn();
36020             skipAnim = true;
36021         }
36022         this.collapsed = false;
36023         if(this.config.animate && skipAnim !== true){
36024             this.animateExpand();
36025         }else{
36026             this.el.show();
36027             if(this.split){
36028                 this.split.el.show();
36029             }
36030             this.collapsedEl.setLocation(-2000,-2000);
36031             this.collapsedEl.hide();
36032             this.fireEvent("invalidated", this);
36033             this.fireEvent("expanded", this);
36034         }
36035     },
36036 */
36037     animateExpand : function(){
36038         // overridden
36039     },
36040
36041     initTabs : function()
36042     {
36043         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36044         
36045         var ts = new Roo.bootstrap.panel.Tabs({
36046                 el: this.bodyEl.dom,
36047                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36048                 disableTooltips: this.config.disableTabTips,
36049                 toolbar : this.config.toolbar
36050             });
36051         
36052         if(this.config.hideTabs){
36053             ts.stripWrap.setDisplayed(false);
36054         }
36055         this.tabs = ts;
36056         ts.resizeTabs = this.config.resizeTabs === true;
36057         ts.minTabWidth = this.config.minTabWidth || 40;
36058         ts.maxTabWidth = this.config.maxTabWidth || 250;
36059         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36060         ts.monitorResize = false;
36061         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36062         ts.bodyEl.addClass('roo-layout-tabs-body');
36063         this.panels.each(this.initPanelAsTab, this);
36064     },
36065
36066     initPanelAsTab : function(panel){
36067         var ti = this.tabs.addTab(
36068             panel.getEl().id,
36069             panel.getTitle(),
36070             null,
36071             this.config.closeOnTab && panel.isClosable(),
36072             panel.tpl
36073         );
36074         if(panel.tabTip !== undefined){
36075             ti.setTooltip(panel.tabTip);
36076         }
36077         ti.on("activate", function(){
36078               this.setActivePanel(panel);
36079         }, this);
36080         
36081         if(this.config.closeOnTab){
36082             ti.on("beforeclose", function(t, e){
36083                 e.cancel = true;
36084                 this.remove(panel);
36085             }, this);
36086         }
36087         
36088         panel.tabItem = ti;
36089         
36090         return ti;
36091     },
36092
36093     updatePanelTitle : function(panel, title)
36094     {
36095         if(this.activePanel == panel){
36096             this.updateTitle(title);
36097         }
36098         if(this.tabs){
36099             var ti = this.tabs.getTab(panel.getEl().id);
36100             ti.setText(title);
36101             if(panel.tabTip !== undefined){
36102                 ti.setTooltip(panel.tabTip);
36103             }
36104         }
36105     },
36106
36107     updateTitle : function(title){
36108         if(this.titleTextEl && !this.config.title){
36109             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36110         }
36111     },
36112
36113     setActivePanel : function(panel)
36114     {
36115         panel = this.getPanel(panel);
36116         if(this.activePanel && this.activePanel != panel){
36117             if(this.activePanel.setActiveState(false) === false){
36118                 return;
36119             }
36120         }
36121         this.activePanel = panel;
36122         panel.setActiveState(true);
36123         if(this.panelSize){
36124             panel.setSize(this.panelSize.width, this.panelSize.height);
36125         }
36126         if(this.closeBtn){
36127             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36128         }
36129         this.updateTitle(panel.getTitle());
36130         if(this.tabs){
36131             this.fireEvent("invalidated", this);
36132         }
36133         this.fireEvent("panelactivated", this, panel);
36134     },
36135
36136     /**
36137      * Shows the specified panel.
36138      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36139      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36140      */
36141     showPanel : function(panel)
36142     {
36143         panel = this.getPanel(panel);
36144         if(panel){
36145             if(this.tabs){
36146                 var tab = this.tabs.getTab(panel.getEl().id);
36147                 if(tab.isHidden()){
36148                     this.tabs.unhideTab(tab.id);
36149                 }
36150                 tab.activate();
36151             }else{
36152                 this.setActivePanel(panel);
36153             }
36154         }
36155         return panel;
36156     },
36157
36158     /**
36159      * Get the active panel for this region.
36160      * @return {Roo.ContentPanel} The active panel or null
36161      */
36162     getActivePanel : function(){
36163         return this.activePanel;
36164     },
36165
36166     validateVisibility : function(){
36167         if(this.panels.getCount() < 1){
36168             this.updateTitle("&#160;");
36169             this.closeBtn.hide();
36170             this.hide();
36171         }else{
36172             if(!this.isVisible()){
36173                 this.show();
36174             }
36175         }
36176     },
36177
36178     /**
36179      * Adds the passed ContentPanel(s) to this region.
36180      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36181      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36182      */
36183     add : function(panel)
36184     {
36185         if(arguments.length > 1){
36186             for(var i = 0, len = arguments.length; i < len; i++) {
36187                 this.add(arguments[i]);
36188             }
36189             return null;
36190         }
36191         
36192         // if we have not been rendered yet, then we can not really do much of this..
36193         if (!this.bodyEl) {
36194             this.unrendered_panels.push(panel);
36195             return panel;
36196         }
36197         
36198         
36199         
36200         
36201         if(this.hasPanel(panel)){
36202             this.showPanel(panel);
36203             return panel;
36204         }
36205         panel.setRegion(this);
36206         this.panels.add(panel);
36207        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36208             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36209             // and hide them... ???
36210             this.bodyEl.dom.appendChild(panel.getEl().dom);
36211             if(panel.background !== true){
36212                 this.setActivePanel(panel);
36213             }
36214             this.fireEvent("paneladded", this, panel);
36215             return panel;
36216         }
36217         */
36218         if(!this.tabs){
36219             this.initTabs();
36220         }else{
36221             this.initPanelAsTab(panel);
36222         }
36223         
36224         
36225         if(panel.background !== true){
36226             this.tabs.activate(panel.getEl().id);
36227         }
36228         this.fireEvent("paneladded", this, panel);
36229         return panel;
36230     },
36231
36232     /**
36233      * Hides the tab for the specified panel.
36234      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36235      */
36236     hidePanel : function(panel){
36237         if(this.tabs && (panel = this.getPanel(panel))){
36238             this.tabs.hideTab(panel.getEl().id);
36239         }
36240     },
36241
36242     /**
36243      * Unhides the tab for a previously hidden panel.
36244      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36245      */
36246     unhidePanel : function(panel){
36247         if(this.tabs && (panel = this.getPanel(panel))){
36248             this.tabs.unhideTab(panel.getEl().id);
36249         }
36250     },
36251
36252     clearPanels : function(){
36253         while(this.panels.getCount() > 0){
36254              this.remove(this.panels.first());
36255         }
36256     },
36257
36258     /**
36259      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36260      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36261      * @param {Boolean} preservePanel Overrides the config preservePanel option
36262      * @return {Roo.ContentPanel} The panel that was removed
36263      */
36264     remove : function(panel, preservePanel)
36265     {
36266         panel = this.getPanel(panel);
36267         if(!panel){
36268             return null;
36269         }
36270         var e = {};
36271         this.fireEvent("beforeremove", this, panel, e);
36272         if(e.cancel === true){
36273             return null;
36274         }
36275         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36276         var panelId = panel.getId();
36277         this.panels.removeKey(panelId);
36278         if(preservePanel){
36279             document.body.appendChild(panel.getEl().dom);
36280         }
36281         if(this.tabs){
36282             this.tabs.removeTab(panel.getEl().id);
36283         }else if (!preservePanel){
36284             this.bodyEl.dom.removeChild(panel.getEl().dom);
36285         }
36286         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36287             var p = this.panels.first();
36288             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36289             tempEl.appendChild(p.getEl().dom);
36290             this.bodyEl.update("");
36291             this.bodyEl.dom.appendChild(p.getEl().dom);
36292             tempEl = null;
36293             this.updateTitle(p.getTitle());
36294             this.tabs = null;
36295             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36296             this.setActivePanel(p);
36297         }
36298         panel.setRegion(null);
36299         if(this.activePanel == panel){
36300             this.activePanel = null;
36301         }
36302         if(this.config.autoDestroy !== false && preservePanel !== true){
36303             try{panel.destroy();}catch(e){}
36304         }
36305         this.fireEvent("panelremoved", this, panel);
36306         return panel;
36307     },
36308
36309     /**
36310      * Returns the TabPanel component used by this region
36311      * @return {Roo.TabPanel}
36312      */
36313     getTabs : function(){
36314         return this.tabs;
36315     },
36316
36317     createTool : function(parentEl, className){
36318         var btn = Roo.DomHelper.append(parentEl, {
36319             tag: "div",
36320             cls: "x-layout-tools-button",
36321             children: [ {
36322                 tag: "div",
36323                 cls: "roo-layout-tools-button-inner " + className,
36324                 html: "&#160;"
36325             }]
36326         }, true);
36327         btn.addClassOnOver("roo-layout-tools-button-over");
36328         return btn;
36329     }
36330 });/*
36331  * Based on:
36332  * Ext JS Library 1.1.1
36333  * Copyright(c) 2006-2007, Ext JS, LLC.
36334  *
36335  * Originally Released Under LGPL - original licence link has changed is not relivant.
36336  *
36337  * Fork - LGPL
36338  * <script type="text/javascript">
36339  */
36340  
36341
36342
36343 /**
36344  * @class Roo.SplitLayoutRegion
36345  * @extends Roo.LayoutRegion
36346  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36347  */
36348 Roo.bootstrap.layout.Split = function(config){
36349     this.cursor = config.cursor;
36350     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36351 };
36352
36353 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36354 {
36355     splitTip : "Drag to resize.",
36356     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36357     useSplitTips : false,
36358
36359     applyConfig : function(config){
36360         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36361     },
36362     
36363     onRender : function(ctr,pos) {
36364         
36365         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36366         if(!this.config.split){
36367             return;
36368         }
36369         if(!this.split){
36370             
36371             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36372                             tag: "div",
36373                             id: this.el.id + "-split",
36374                             cls: "roo-layout-split roo-layout-split-"+this.position,
36375                             html: "&#160;"
36376             });
36377             /** The SplitBar for this region 
36378             * @type Roo.SplitBar */
36379             // does not exist yet...
36380             Roo.log([this.position, this.orientation]);
36381             
36382             this.split = new Roo.bootstrap.SplitBar({
36383                 dragElement : splitEl,
36384                 resizingElement: this.el,
36385                 orientation : this.orientation
36386             });
36387             
36388             this.split.on("moved", this.onSplitMove, this);
36389             this.split.useShim = this.config.useShim === true;
36390             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36391             if(this.useSplitTips){
36392                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36393             }
36394             //if(config.collapsible){
36395             //    this.split.el.on("dblclick", this.collapse,  this);
36396             //}
36397         }
36398         if(typeof this.config.minSize != "undefined"){
36399             this.split.minSize = this.config.minSize;
36400         }
36401         if(typeof this.config.maxSize != "undefined"){
36402             this.split.maxSize = this.config.maxSize;
36403         }
36404         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36405             this.hideSplitter();
36406         }
36407         
36408     },
36409
36410     getHMaxSize : function(){
36411          var cmax = this.config.maxSize || 10000;
36412          var center = this.mgr.getRegion("center");
36413          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36414     },
36415
36416     getVMaxSize : function(){
36417          var cmax = this.config.maxSize || 10000;
36418          var center = this.mgr.getRegion("center");
36419          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36420     },
36421
36422     onSplitMove : function(split, newSize){
36423         this.fireEvent("resized", this, newSize);
36424     },
36425     
36426     /** 
36427      * Returns the {@link Roo.SplitBar} for this region.
36428      * @return {Roo.SplitBar}
36429      */
36430     getSplitBar : function(){
36431         return this.split;
36432     },
36433     
36434     hide : function(){
36435         this.hideSplitter();
36436         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36437     },
36438
36439     hideSplitter : function(){
36440         if(this.split){
36441             this.split.el.setLocation(-2000,-2000);
36442             this.split.el.hide();
36443         }
36444     },
36445
36446     show : function(){
36447         if(this.split){
36448             this.split.el.show();
36449         }
36450         Roo.bootstrap.layout.Split.superclass.show.call(this);
36451     },
36452     
36453     beforeSlide: function(){
36454         if(Roo.isGecko){// firefox overflow auto bug workaround
36455             this.bodyEl.clip();
36456             if(this.tabs) {
36457                 this.tabs.bodyEl.clip();
36458             }
36459             if(this.activePanel){
36460                 this.activePanel.getEl().clip();
36461                 
36462                 if(this.activePanel.beforeSlide){
36463                     this.activePanel.beforeSlide();
36464                 }
36465             }
36466         }
36467     },
36468     
36469     afterSlide : function(){
36470         if(Roo.isGecko){// firefox overflow auto bug workaround
36471             this.bodyEl.unclip();
36472             if(this.tabs) {
36473                 this.tabs.bodyEl.unclip();
36474             }
36475             if(this.activePanel){
36476                 this.activePanel.getEl().unclip();
36477                 if(this.activePanel.afterSlide){
36478                     this.activePanel.afterSlide();
36479                 }
36480             }
36481         }
36482     },
36483
36484     initAutoHide : function(){
36485         if(this.autoHide !== false){
36486             if(!this.autoHideHd){
36487                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36488                 this.autoHideHd = {
36489                     "mouseout": function(e){
36490                         if(!e.within(this.el, true)){
36491                             st.delay(500);
36492                         }
36493                     },
36494                     "mouseover" : function(e){
36495                         st.cancel();
36496                     },
36497                     scope : this
36498                 };
36499             }
36500             this.el.on(this.autoHideHd);
36501         }
36502     },
36503
36504     clearAutoHide : function(){
36505         if(this.autoHide !== false){
36506             this.el.un("mouseout", this.autoHideHd.mouseout);
36507             this.el.un("mouseover", this.autoHideHd.mouseover);
36508         }
36509     },
36510
36511     clearMonitor : function(){
36512         Roo.get(document).un("click", this.slideInIf, this);
36513     },
36514
36515     // these names are backwards but not changed for compat
36516     slideOut : function(){
36517         if(this.isSlid || this.el.hasActiveFx()){
36518             return;
36519         }
36520         this.isSlid = true;
36521         if(this.collapseBtn){
36522             this.collapseBtn.hide();
36523         }
36524         this.closeBtnState = this.closeBtn.getStyle('display');
36525         this.closeBtn.hide();
36526         if(this.stickBtn){
36527             this.stickBtn.show();
36528         }
36529         this.el.show();
36530         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36531         this.beforeSlide();
36532         this.el.setStyle("z-index", 10001);
36533         this.el.slideIn(this.getSlideAnchor(), {
36534             callback: function(){
36535                 this.afterSlide();
36536                 this.initAutoHide();
36537                 Roo.get(document).on("click", this.slideInIf, this);
36538                 this.fireEvent("slideshow", this);
36539             },
36540             scope: this,
36541             block: true
36542         });
36543     },
36544
36545     afterSlideIn : function(){
36546         this.clearAutoHide();
36547         this.isSlid = false;
36548         this.clearMonitor();
36549         this.el.setStyle("z-index", "");
36550         if(this.collapseBtn){
36551             this.collapseBtn.show();
36552         }
36553         this.closeBtn.setStyle('display', this.closeBtnState);
36554         if(this.stickBtn){
36555             this.stickBtn.hide();
36556         }
36557         this.fireEvent("slidehide", this);
36558     },
36559
36560     slideIn : function(cb){
36561         if(!this.isSlid || this.el.hasActiveFx()){
36562             Roo.callback(cb);
36563             return;
36564         }
36565         this.isSlid = false;
36566         this.beforeSlide();
36567         this.el.slideOut(this.getSlideAnchor(), {
36568             callback: function(){
36569                 this.el.setLeftTop(-10000, -10000);
36570                 this.afterSlide();
36571                 this.afterSlideIn();
36572                 Roo.callback(cb);
36573             },
36574             scope: this,
36575             block: true
36576         });
36577     },
36578     
36579     slideInIf : function(e){
36580         if(!e.within(this.el)){
36581             this.slideIn();
36582         }
36583     },
36584
36585     animateCollapse : function(){
36586         this.beforeSlide();
36587         this.el.setStyle("z-index", 20000);
36588         var anchor = this.getSlideAnchor();
36589         this.el.slideOut(anchor, {
36590             callback : function(){
36591                 this.el.setStyle("z-index", "");
36592                 this.collapsedEl.slideIn(anchor, {duration:.3});
36593                 this.afterSlide();
36594                 this.el.setLocation(-10000,-10000);
36595                 this.el.hide();
36596                 this.fireEvent("collapsed", this);
36597             },
36598             scope: this,
36599             block: true
36600         });
36601     },
36602
36603     animateExpand : function(){
36604         this.beforeSlide();
36605         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36606         this.el.setStyle("z-index", 20000);
36607         this.collapsedEl.hide({
36608             duration:.1
36609         });
36610         this.el.slideIn(this.getSlideAnchor(), {
36611             callback : function(){
36612                 this.el.setStyle("z-index", "");
36613                 this.afterSlide();
36614                 if(this.split){
36615                     this.split.el.show();
36616                 }
36617                 this.fireEvent("invalidated", this);
36618                 this.fireEvent("expanded", this);
36619             },
36620             scope: this,
36621             block: true
36622         });
36623     },
36624
36625     anchors : {
36626         "west" : "left",
36627         "east" : "right",
36628         "north" : "top",
36629         "south" : "bottom"
36630     },
36631
36632     sanchors : {
36633         "west" : "l",
36634         "east" : "r",
36635         "north" : "t",
36636         "south" : "b"
36637     },
36638
36639     canchors : {
36640         "west" : "tl-tr",
36641         "east" : "tr-tl",
36642         "north" : "tl-bl",
36643         "south" : "bl-tl"
36644     },
36645
36646     getAnchor : function(){
36647         return this.anchors[this.position];
36648     },
36649
36650     getCollapseAnchor : function(){
36651         return this.canchors[this.position];
36652     },
36653
36654     getSlideAnchor : function(){
36655         return this.sanchors[this.position];
36656     },
36657
36658     getAlignAdj : function(){
36659         var cm = this.cmargins;
36660         switch(this.position){
36661             case "west":
36662                 return [0, 0];
36663             break;
36664             case "east":
36665                 return [0, 0];
36666             break;
36667             case "north":
36668                 return [0, 0];
36669             break;
36670             case "south":
36671                 return [0, 0];
36672             break;
36673         }
36674     },
36675
36676     getExpandAdj : function(){
36677         var c = this.collapsedEl, cm = this.cmargins;
36678         switch(this.position){
36679             case "west":
36680                 return [-(cm.right+c.getWidth()+cm.left), 0];
36681             break;
36682             case "east":
36683                 return [cm.right+c.getWidth()+cm.left, 0];
36684             break;
36685             case "north":
36686                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36687             break;
36688             case "south":
36689                 return [0, cm.top+cm.bottom+c.getHeight()];
36690             break;
36691         }
36692     }
36693 });/*
36694  * Based on:
36695  * Ext JS Library 1.1.1
36696  * Copyright(c) 2006-2007, Ext JS, LLC.
36697  *
36698  * Originally Released Under LGPL - original licence link has changed is not relivant.
36699  *
36700  * Fork - LGPL
36701  * <script type="text/javascript">
36702  */
36703 /*
36704  * These classes are private internal classes
36705  */
36706 Roo.bootstrap.layout.Center = function(config){
36707     config.region = "center";
36708     Roo.bootstrap.layout.Region.call(this, config);
36709     this.visible = true;
36710     this.minWidth = config.minWidth || 20;
36711     this.minHeight = config.minHeight || 20;
36712 };
36713
36714 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36715     hide : function(){
36716         // center panel can't be hidden
36717     },
36718     
36719     show : function(){
36720         // center panel can't be hidden
36721     },
36722     
36723     getMinWidth: function(){
36724         return this.minWidth;
36725     },
36726     
36727     getMinHeight: function(){
36728         return this.minHeight;
36729     }
36730 });
36731
36732
36733
36734
36735  
36736
36737
36738
36739
36740
36741 Roo.bootstrap.layout.North = function(config)
36742 {
36743     config.region = 'north';
36744     config.cursor = 'n-resize';
36745     
36746     Roo.bootstrap.layout.Split.call(this, config);
36747     
36748     
36749     if(this.split){
36750         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36751         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36752         this.split.el.addClass("roo-layout-split-v");
36753     }
36754     var size = config.initialSize || config.height;
36755     if(typeof size != "undefined"){
36756         this.el.setHeight(size);
36757     }
36758 };
36759 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36760 {
36761     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36762     
36763     
36764     
36765     getBox : function(){
36766         if(this.collapsed){
36767             return this.collapsedEl.getBox();
36768         }
36769         var box = this.el.getBox();
36770         if(this.split){
36771             box.height += this.split.el.getHeight();
36772         }
36773         return box;
36774     },
36775     
36776     updateBox : function(box){
36777         if(this.split && !this.collapsed){
36778             box.height -= this.split.el.getHeight();
36779             this.split.el.setLeft(box.x);
36780             this.split.el.setTop(box.y+box.height);
36781             this.split.el.setWidth(box.width);
36782         }
36783         if(this.collapsed){
36784             this.updateBody(box.width, null);
36785         }
36786         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36787     }
36788 });
36789
36790
36791
36792
36793
36794 Roo.bootstrap.layout.South = function(config){
36795     config.region = 'south';
36796     config.cursor = 's-resize';
36797     Roo.bootstrap.layout.Split.call(this, config);
36798     if(this.split){
36799         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36800         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36801         this.split.el.addClass("roo-layout-split-v");
36802     }
36803     var size = config.initialSize || config.height;
36804     if(typeof size != "undefined"){
36805         this.el.setHeight(size);
36806     }
36807 };
36808
36809 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36810     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36811     getBox : function(){
36812         if(this.collapsed){
36813             return this.collapsedEl.getBox();
36814         }
36815         var box = this.el.getBox();
36816         if(this.split){
36817             var sh = this.split.el.getHeight();
36818             box.height += sh;
36819             box.y -= sh;
36820         }
36821         return box;
36822     },
36823     
36824     updateBox : function(box){
36825         if(this.split && !this.collapsed){
36826             var sh = this.split.el.getHeight();
36827             box.height -= sh;
36828             box.y += sh;
36829             this.split.el.setLeft(box.x);
36830             this.split.el.setTop(box.y-sh);
36831             this.split.el.setWidth(box.width);
36832         }
36833         if(this.collapsed){
36834             this.updateBody(box.width, null);
36835         }
36836         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36837     }
36838 });
36839
36840 Roo.bootstrap.layout.East = function(config){
36841     config.region = "east";
36842     config.cursor = "e-resize";
36843     Roo.bootstrap.layout.Split.call(this, config);
36844     if(this.split){
36845         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36846         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36847         this.split.el.addClass("roo-layout-split-h");
36848     }
36849     var size = config.initialSize || config.width;
36850     if(typeof size != "undefined"){
36851         this.el.setWidth(size);
36852     }
36853 };
36854 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36855     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36856     getBox : function(){
36857         if(this.collapsed){
36858             return this.collapsedEl.getBox();
36859         }
36860         var box = this.el.getBox();
36861         if(this.split){
36862             var sw = this.split.el.getWidth();
36863             box.width += sw;
36864             box.x -= sw;
36865         }
36866         return box;
36867     },
36868
36869     updateBox : function(box){
36870         if(this.split && !this.collapsed){
36871             var sw = this.split.el.getWidth();
36872             box.width -= sw;
36873             this.split.el.setLeft(box.x);
36874             this.split.el.setTop(box.y);
36875             this.split.el.setHeight(box.height);
36876             box.x += sw;
36877         }
36878         if(this.collapsed){
36879             this.updateBody(null, box.height);
36880         }
36881         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36882     }
36883 });
36884
36885 Roo.bootstrap.layout.West = function(config){
36886     config.region = "west";
36887     config.cursor = "w-resize";
36888     
36889     Roo.bootstrap.layout.Split.call(this, config);
36890     if(this.split){
36891         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36892         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36893         this.split.el.addClass("roo-layout-split-h");
36894     }
36895     
36896 };
36897 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36898     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36899     
36900     onRender: function(ctr, pos)
36901     {
36902         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36903         var size = this.config.initialSize || this.config.width;
36904         if(typeof size != "undefined"){
36905             this.el.setWidth(size);
36906         }
36907     },
36908     
36909     getBox : function(){
36910         if(this.collapsed){
36911             return this.collapsedEl.getBox();
36912         }
36913         var box = this.el.getBox();
36914         if(this.split){
36915             box.width += this.split.el.getWidth();
36916         }
36917         return box;
36918     },
36919     
36920     updateBox : function(box){
36921         if(this.split && !this.collapsed){
36922             var sw = this.split.el.getWidth();
36923             box.width -= sw;
36924             this.split.el.setLeft(box.x+box.width);
36925             this.split.el.setTop(box.y);
36926             this.split.el.setHeight(box.height);
36927         }
36928         if(this.collapsed){
36929             this.updateBody(null, box.height);
36930         }
36931         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36932     }
36933 });
36934 Roo.namespace("Roo.bootstrap.panel");/*
36935  * Based on:
36936  * Ext JS Library 1.1.1
36937  * Copyright(c) 2006-2007, Ext JS, LLC.
36938  *
36939  * Originally Released Under LGPL - original licence link has changed is not relivant.
36940  *
36941  * Fork - LGPL
36942  * <script type="text/javascript">
36943  */
36944 /**
36945  * @class Roo.ContentPanel
36946  * @extends Roo.util.Observable
36947  * A basic ContentPanel element.
36948  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36949  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36950  * @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
36951  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36952  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36953  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36954  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36955  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36956  * @cfg {String} title          The title for this panel
36957  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36958  * @cfg {String} url            Calls {@link #setUrl} with this value
36959  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36960  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36961  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36962  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36963  * @cfg {Boolean} badges render the badges
36964
36965  * @constructor
36966  * Create a new ContentPanel.
36967  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36968  * @param {String/Object} config A string to set only the title or a config object
36969  * @param {String} content (optional) Set the HTML content for this panel
36970  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36971  */
36972 Roo.bootstrap.panel.Content = function( config){
36973     
36974     this.tpl = config.tpl || false;
36975     
36976     var el = config.el;
36977     var content = config.content;
36978
36979     if(config.autoCreate){ // xtype is available if this is called from factory
36980         el = Roo.id();
36981     }
36982     this.el = Roo.get(el);
36983     if(!this.el && config && config.autoCreate){
36984         if(typeof config.autoCreate == "object"){
36985             if(!config.autoCreate.id){
36986                 config.autoCreate.id = config.id||el;
36987             }
36988             this.el = Roo.DomHelper.append(document.body,
36989                         config.autoCreate, true);
36990         }else{
36991             var elcfg =  {   tag: "div",
36992                             cls: "roo-layout-inactive-content",
36993                             id: config.id||el
36994                             };
36995             if (config.html) {
36996                 elcfg.html = config.html;
36997                 
36998             }
36999                         
37000             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37001         }
37002     } 
37003     this.closable = false;
37004     this.loaded = false;
37005     this.active = false;
37006    
37007       
37008     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37009         
37010         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37011         
37012         this.wrapEl = this.el; //this.el.wrap();
37013         var ti = [];
37014         if (config.toolbar.items) {
37015             ti = config.toolbar.items ;
37016             delete config.toolbar.items ;
37017         }
37018         
37019         var nitems = [];
37020         this.toolbar.render(this.wrapEl, 'before');
37021         for(var i =0;i < ti.length;i++) {
37022           //  Roo.log(['add child', items[i]]);
37023             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37024         }
37025         this.toolbar.items = nitems;
37026         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37027         delete config.toolbar;
37028         
37029     }
37030     /*
37031     // xtype created footer. - not sure if will work as we normally have to render first..
37032     if (this.footer && !this.footer.el && this.footer.xtype) {
37033         if (!this.wrapEl) {
37034             this.wrapEl = this.el.wrap();
37035         }
37036     
37037         this.footer.container = this.wrapEl.createChild();
37038          
37039         this.footer = Roo.factory(this.footer, Roo);
37040         
37041     }
37042     */
37043     
37044      if(typeof config == "string"){
37045         this.title = config;
37046     }else{
37047         Roo.apply(this, config);
37048     }
37049     
37050     if(this.resizeEl){
37051         this.resizeEl = Roo.get(this.resizeEl, true);
37052     }else{
37053         this.resizeEl = this.el;
37054     }
37055     // handle view.xtype
37056     
37057  
37058     
37059     
37060     this.addEvents({
37061         /**
37062          * @event activate
37063          * Fires when this panel is activated. 
37064          * @param {Roo.ContentPanel} this
37065          */
37066         "activate" : true,
37067         /**
37068          * @event deactivate
37069          * Fires when this panel is activated. 
37070          * @param {Roo.ContentPanel} this
37071          */
37072         "deactivate" : true,
37073
37074         /**
37075          * @event resize
37076          * Fires when this panel is resized if fitToFrame is true.
37077          * @param {Roo.ContentPanel} this
37078          * @param {Number} width The width after any component adjustments
37079          * @param {Number} height The height after any component adjustments
37080          */
37081         "resize" : true,
37082         
37083          /**
37084          * @event render
37085          * Fires when this tab is created
37086          * @param {Roo.ContentPanel} this
37087          */
37088         "render" : true
37089         
37090         
37091         
37092     });
37093     
37094
37095     
37096     
37097     if(this.autoScroll){
37098         this.resizeEl.setStyle("overflow", "auto");
37099     } else {
37100         // fix randome scrolling
37101         //this.el.on('scroll', function() {
37102         //    Roo.log('fix random scolling');
37103         //    this.scrollTo('top',0); 
37104         //});
37105     }
37106     content = content || this.content;
37107     if(content){
37108         this.setContent(content);
37109     }
37110     if(config && config.url){
37111         this.setUrl(this.url, this.params, this.loadOnce);
37112     }
37113     
37114     
37115     
37116     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37117     
37118     if (this.view && typeof(this.view.xtype) != 'undefined') {
37119         this.view.el = this.el.appendChild(document.createElement("div"));
37120         this.view = Roo.factory(this.view); 
37121         this.view.render  &&  this.view.render(false, '');  
37122     }
37123     
37124     
37125     this.fireEvent('render', this);
37126 };
37127
37128 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37129     
37130     tabTip : '',
37131     
37132     setRegion : function(region){
37133         this.region = region;
37134         this.setActiveClass(region && !this.background);
37135     },
37136     
37137     
37138     setActiveClass: function(state)
37139     {
37140         if(state){
37141            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37142            this.el.setStyle('position','relative');
37143         }else{
37144            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37145            this.el.setStyle('position', 'absolute');
37146         } 
37147     },
37148     
37149     /**
37150      * Returns the toolbar for this Panel if one was configured. 
37151      * @return {Roo.Toolbar} 
37152      */
37153     getToolbar : function(){
37154         return this.toolbar;
37155     },
37156     
37157     setActiveState : function(active)
37158     {
37159         this.active = active;
37160         this.setActiveClass(active);
37161         if(!active){
37162             if(this.fireEvent("deactivate", this) === false){
37163                 return false;
37164             }
37165             return true;
37166         }
37167         this.fireEvent("activate", this);
37168         return true;
37169     },
37170     /**
37171      * Updates this panel's element
37172      * @param {String} content The new content
37173      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37174     */
37175     setContent : function(content, loadScripts){
37176         this.el.update(content, loadScripts);
37177     },
37178
37179     ignoreResize : function(w, h){
37180         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37181             return true;
37182         }else{
37183             this.lastSize = {width: w, height: h};
37184             return false;
37185         }
37186     },
37187     /**
37188      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37189      * @return {Roo.UpdateManager} The UpdateManager
37190      */
37191     getUpdateManager : function(){
37192         return this.el.getUpdateManager();
37193     },
37194      /**
37195      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37196      * @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:
37197 <pre><code>
37198 panel.load({
37199     url: "your-url.php",
37200     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37201     callback: yourFunction,
37202     scope: yourObject, //(optional scope)
37203     discardUrl: false,
37204     nocache: false,
37205     text: "Loading...",
37206     timeout: 30,
37207     scripts: false
37208 });
37209 </code></pre>
37210      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37211      * 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.
37212      * @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}
37213      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37214      * @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.
37215      * @return {Roo.ContentPanel} this
37216      */
37217     load : function(){
37218         var um = this.el.getUpdateManager();
37219         um.update.apply(um, arguments);
37220         return this;
37221     },
37222
37223
37224     /**
37225      * 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.
37226      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37227      * @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)
37228      * @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)
37229      * @return {Roo.UpdateManager} The UpdateManager
37230      */
37231     setUrl : function(url, params, loadOnce){
37232         if(this.refreshDelegate){
37233             this.removeListener("activate", this.refreshDelegate);
37234         }
37235         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37236         this.on("activate", this.refreshDelegate);
37237         return this.el.getUpdateManager();
37238     },
37239     
37240     _handleRefresh : function(url, params, loadOnce){
37241         if(!loadOnce || !this.loaded){
37242             var updater = this.el.getUpdateManager();
37243             updater.update(url, params, this._setLoaded.createDelegate(this));
37244         }
37245     },
37246     
37247     _setLoaded : function(){
37248         this.loaded = true;
37249     }, 
37250     
37251     /**
37252      * Returns this panel's id
37253      * @return {String} 
37254      */
37255     getId : function(){
37256         return this.el.id;
37257     },
37258     
37259     /** 
37260      * Returns this panel's element - used by regiosn to add.
37261      * @return {Roo.Element} 
37262      */
37263     getEl : function(){
37264         return this.wrapEl || this.el;
37265     },
37266     
37267    
37268     
37269     adjustForComponents : function(width, height)
37270     {
37271         //Roo.log('adjustForComponents ');
37272         if(this.resizeEl != this.el){
37273             width -= this.el.getFrameWidth('lr');
37274             height -= this.el.getFrameWidth('tb');
37275         }
37276         if(this.toolbar){
37277             var te = this.toolbar.getEl();
37278             te.setWidth(width);
37279             height -= te.getHeight();
37280         }
37281         if(this.footer){
37282             var te = this.footer.getEl();
37283             te.setWidth(width);
37284             height -= te.getHeight();
37285         }
37286         
37287         
37288         if(this.adjustments){
37289             width += this.adjustments[0];
37290             height += this.adjustments[1];
37291         }
37292         return {"width": width, "height": height};
37293     },
37294     
37295     setSize : function(width, height){
37296         if(this.fitToFrame && !this.ignoreResize(width, height)){
37297             if(this.fitContainer && this.resizeEl != this.el){
37298                 this.el.setSize(width, height);
37299             }
37300             var size = this.adjustForComponents(width, height);
37301             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37302             this.fireEvent('resize', this, size.width, size.height);
37303         }
37304     },
37305     
37306     /**
37307      * Returns this panel's title
37308      * @return {String} 
37309      */
37310     getTitle : function(){
37311         
37312         if (typeof(this.title) != 'object') {
37313             return this.title;
37314         }
37315         
37316         var t = '';
37317         for (var k in this.title) {
37318             if (!this.title.hasOwnProperty(k)) {
37319                 continue;
37320             }
37321             
37322             if (k.indexOf('-') >= 0) {
37323                 var s = k.split('-');
37324                 for (var i = 0; i<s.length; i++) {
37325                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37326                 }
37327             } else {
37328                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37329             }
37330         }
37331         return t;
37332     },
37333     
37334     /**
37335      * Set this panel's title
37336      * @param {String} title
37337      */
37338     setTitle : function(title){
37339         this.title = title;
37340         if(this.region){
37341             this.region.updatePanelTitle(this, title);
37342         }
37343     },
37344     
37345     /**
37346      * Returns true is this panel was configured to be closable
37347      * @return {Boolean} 
37348      */
37349     isClosable : function(){
37350         return this.closable;
37351     },
37352     
37353     beforeSlide : function(){
37354         this.el.clip();
37355         this.resizeEl.clip();
37356     },
37357     
37358     afterSlide : function(){
37359         this.el.unclip();
37360         this.resizeEl.unclip();
37361     },
37362     
37363     /**
37364      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37365      *   Will fail silently if the {@link #setUrl} method has not been called.
37366      *   This does not activate the panel, just updates its content.
37367      */
37368     refresh : function(){
37369         if(this.refreshDelegate){
37370            this.loaded = false;
37371            this.refreshDelegate();
37372         }
37373     },
37374     
37375     /**
37376      * Destroys this panel
37377      */
37378     destroy : function(){
37379         this.el.removeAllListeners();
37380         var tempEl = document.createElement("span");
37381         tempEl.appendChild(this.el.dom);
37382         tempEl.innerHTML = "";
37383         this.el.remove();
37384         this.el = null;
37385     },
37386     
37387     /**
37388      * form - if the content panel contains a form - this is a reference to it.
37389      * @type {Roo.form.Form}
37390      */
37391     form : false,
37392     /**
37393      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37394      *    This contains a reference to it.
37395      * @type {Roo.View}
37396      */
37397     view : false,
37398     
37399       /**
37400      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37401      * <pre><code>
37402
37403 layout.addxtype({
37404        xtype : 'Form',
37405        items: [ .... ]
37406    }
37407 );
37408
37409 </code></pre>
37410      * @param {Object} cfg Xtype definition of item to add.
37411      */
37412     
37413     
37414     getChildContainer: function () {
37415         return this.getEl();
37416     }
37417     
37418     
37419     /*
37420         var  ret = new Roo.factory(cfg);
37421         return ret;
37422         
37423         
37424         // add form..
37425         if (cfg.xtype.match(/^Form$/)) {
37426             
37427             var el;
37428             //if (this.footer) {
37429             //    el = this.footer.container.insertSibling(false, 'before');
37430             //} else {
37431                 el = this.el.createChild();
37432             //}
37433
37434             this.form = new  Roo.form.Form(cfg);
37435             
37436             
37437             if ( this.form.allItems.length) {
37438                 this.form.render(el.dom);
37439             }
37440             return this.form;
37441         }
37442         // should only have one of theses..
37443         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37444             // views.. should not be just added - used named prop 'view''
37445             
37446             cfg.el = this.el.appendChild(document.createElement("div"));
37447             // factory?
37448             
37449             var ret = new Roo.factory(cfg);
37450              
37451              ret.render && ret.render(false, ''); // render blank..
37452             this.view = ret;
37453             return ret;
37454         }
37455         return false;
37456     }
37457     \*/
37458 });
37459  
37460 /**
37461  * @class Roo.bootstrap.panel.Grid
37462  * @extends Roo.bootstrap.panel.Content
37463  * @constructor
37464  * Create a new GridPanel.
37465  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37466  * @param {Object} config A the config object
37467   
37468  */
37469
37470
37471
37472 Roo.bootstrap.panel.Grid = function(config)
37473 {
37474     
37475       
37476     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37477         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37478
37479     config.el = this.wrapper;
37480     //this.el = this.wrapper;
37481     
37482       if (config.container) {
37483         // ctor'ed from a Border/panel.grid
37484         
37485         
37486         this.wrapper.setStyle("overflow", "hidden");
37487         this.wrapper.addClass('roo-grid-container');
37488
37489     }
37490     
37491     
37492     if(config.toolbar){
37493         var tool_el = this.wrapper.createChild();    
37494         this.toolbar = Roo.factory(config.toolbar);
37495         var ti = [];
37496         if (config.toolbar.items) {
37497             ti = config.toolbar.items ;
37498             delete config.toolbar.items ;
37499         }
37500         
37501         var nitems = [];
37502         this.toolbar.render(tool_el);
37503         for(var i =0;i < ti.length;i++) {
37504           //  Roo.log(['add child', items[i]]);
37505             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37506         }
37507         this.toolbar.items = nitems;
37508         
37509         delete config.toolbar;
37510     }
37511     
37512     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37513     config.grid.scrollBody = true;;
37514     config.grid.monitorWindowResize = false; // turn off autosizing
37515     config.grid.autoHeight = false;
37516     config.grid.autoWidth = false;
37517     
37518     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37519     
37520     if (config.background) {
37521         // render grid on panel activation (if panel background)
37522         this.on('activate', function(gp) {
37523             if (!gp.grid.rendered) {
37524                 gp.grid.render(this.wrapper);
37525                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37526             }
37527         });
37528             
37529     } else {
37530         this.grid.render(this.wrapper);
37531         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37532
37533     }
37534     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37535     // ??? needed ??? config.el = this.wrapper;
37536     
37537     
37538     
37539   
37540     // xtype created footer. - not sure if will work as we normally have to render first..
37541     if (this.footer && !this.footer.el && this.footer.xtype) {
37542         
37543         var ctr = this.grid.getView().getFooterPanel(true);
37544         this.footer.dataSource = this.grid.dataSource;
37545         this.footer = Roo.factory(this.footer, Roo);
37546         this.footer.render(ctr);
37547         
37548     }
37549     
37550     
37551     
37552     
37553      
37554 };
37555
37556 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37557     getId : function(){
37558         return this.grid.id;
37559     },
37560     
37561     /**
37562      * Returns the grid for this panel
37563      * @return {Roo.bootstrap.Table} 
37564      */
37565     getGrid : function(){
37566         return this.grid;    
37567     },
37568     
37569     setSize : function(width, height){
37570         if(!this.ignoreResize(width, height)){
37571             var grid = this.grid;
37572             var size = this.adjustForComponents(width, height);
37573             var gridel = grid.getGridEl();
37574             gridel.setSize(size.width, size.height);
37575             /*
37576             var thd = grid.getGridEl().select('thead',true).first();
37577             var tbd = grid.getGridEl().select('tbody', true).first();
37578             if (tbd) {
37579                 tbd.setSize(width, height - thd.getHeight());
37580             }
37581             */
37582             grid.autoSize();
37583         }
37584     },
37585      
37586     
37587     
37588     beforeSlide : function(){
37589         this.grid.getView().scroller.clip();
37590     },
37591     
37592     afterSlide : function(){
37593         this.grid.getView().scroller.unclip();
37594     },
37595     
37596     destroy : function(){
37597         this.grid.destroy();
37598         delete this.grid;
37599         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37600     }
37601 });
37602
37603 /**
37604  * @class Roo.bootstrap.panel.Nest
37605  * @extends Roo.bootstrap.panel.Content
37606  * @constructor
37607  * Create a new Panel, that can contain a layout.Border.
37608  * 
37609  * 
37610  * @param {Roo.BorderLayout} layout The layout for this panel
37611  * @param {String/Object} config A string to set only the title or a config object
37612  */
37613 Roo.bootstrap.panel.Nest = function(config)
37614 {
37615     // construct with only one argument..
37616     /* FIXME - implement nicer consturctors
37617     if (layout.layout) {
37618         config = layout;
37619         layout = config.layout;
37620         delete config.layout;
37621     }
37622     if (layout.xtype && !layout.getEl) {
37623         // then layout needs constructing..
37624         layout = Roo.factory(layout, Roo);
37625     }
37626     */
37627     
37628     config.el =  config.layout.getEl();
37629     
37630     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37631     
37632     config.layout.monitorWindowResize = false; // turn off autosizing
37633     this.layout = config.layout;
37634     this.layout.getEl().addClass("roo-layout-nested-layout");
37635     
37636     
37637     
37638     
37639 };
37640
37641 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37642
37643     setSize : function(width, height){
37644         if(!this.ignoreResize(width, height)){
37645             var size = this.adjustForComponents(width, height);
37646             var el = this.layout.getEl();
37647             if (size.height < 1) {
37648                 el.setWidth(size.width);   
37649             } else {
37650                 el.setSize(size.width, size.height);
37651             }
37652             var touch = el.dom.offsetWidth;
37653             this.layout.layout();
37654             // ie requires a double layout on the first pass
37655             if(Roo.isIE && !this.initialized){
37656                 this.initialized = true;
37657                 this.layout.layout();
37658             }
37659         }
37660     },
37661     
37662     // activate all subpanels if not currently active..
37663     
37664     setActiveState : function(active){
37665         this.active = active;
37666         this.setActiveClass(active);
37667         
37668         if(!active){
37669             this.fireEvent("deactivate", this);
37670             return;
37671         }
37672         
37673         this.fireEvent("activate", this);
37674         // not sure if this should happen before or after..
37675         if (!this.layout) {
37676             return; // should not happen..
37677         }
37678         var reg = false;
37679         for (var r in this.layout.regions) {
37680             reg = this.layout.getRegion(r);
37681             if (reg.getActivePanel()) {
37682                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37683                 reg.setActivePanel(reg.getActivePanel());
37684                 continue;
37685             }
37686             if (!reg.panels.length) {
37687                 continue;
37688             }
37689             reg.showPanel(reg.getPanel(0));
37690         }
37691         
37692         
37693         
37694         
37695     },
37696     
37697     /**
37698      * Returns the nested BorderLayout for this panel
37699      * @return {Roo.BorderLayout} 
37700      */
37701     getLayout : function(){
37702         return this.layout;
37703     },
37704     
37705      /**
37706      * Adds a xtype elements to the layout of the nested panel
37707      * <pre><code>
37708
37709 panel.addxtype({
37710        xtype : 'ContentPanel',
37711        region: 'west',
37712        items: [ .... ]
37713    }
37714 );
37715
37716 panel.addxtype({
37717         xtype : 'NestedLayoutPanel',
37718         region: 'west',
37719         layout: {
37720            center: { },
37721            west: { }   
37722         },
37723         items : [ ... list of content panels or nested layout panels.. ]
37724    }
37725 );
37726 </code></pre>
37727      * @param {Object} cfg Xtype definition of item to add.
37728      */
37729     addxtype : function(cfg) {
37730         return this.layout.addxtype(cfg);
37731     
37732     }
37733 });        /*
37734  * Based on:
37735  * Ext JS Library 1.1.1
37736  * Copyright(c) 2006-2007, Ext JS, LLC.
37737  *
37738  * Originally Released Under LGPL - original licence link has changed is not relivant.
37739  *
37740  * Fork - LGPL
37741  * <script type="text/javascript">
37742  */
37743 /**
37744  * @class Roo.TabPanel
37745  * @extends Roo.util.Observable
37746  * A lightweight tab container.
37747  * <br><br>
37748  * Usage:
37749  * <pre><code>
37750 // basic tabs 1, built from existing content
37751 var tabs = new Roo.TabPanel("tabs1");
37752 tabs.addTab("script", "View Script");
37753 tabs.addTab("markup", "View Markup");
37754 tabs.activate("script");
37755
37756 // more advanced tabs, built from javascript
37757 var jtabs = new Roo.TabPanel("jtabs");
37758 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37759
37760 // set up the UpdateManager
37761 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37762 var updater = tab2.getUpdateManager();
37763 updater.setDefaultUrl("ajax1.htm");
37764 tab2.on('activate', updater.refresh, updater, true);
37765
37766 // Use setUrl for Ajax loading
37767 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37768 tab3.setUrl("ajax2.htm", null, true);
37769
37770 // Disabled tab
37771 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37772 tab4.disable();
37773
37774 jtabs.activate("jtabs-1");
37775  * </code></pre>
37776  * @constructor
37777  * Create a new TabPanel.
37778  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37779  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37780  */
37781 Roo.bootstrap.panel.Tabs = function(config){
37782     /**
37783     * The container element for this TabPanel.
37784     * @type Roo.Element
37785     */
37786     this.el = Roo.get(config.el);
37787     delete config.el;
37788     if(config){
37789         if(typeof config == "boolean"){
37790             this.tabPosition = config ? "bottom" : "top";
37791         }else{
37792             Roo.apply(this, config);
37793         }
37794     }
37795     
37796     if(this.tabPosition == "bottom"){
37797         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37798         this.el.addClass("roo-tabs-bottom");
37799     }
37800     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37801     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37802     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37803     if(Roo.isIE){
37804         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37805     }
37806     if(this.tabPosition != "bottom"){
37807         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37808          * @type Roo.Element
37809          */
37810         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37811         this.el.addClass("roo-tabs-top");
37812     }
37813     this.items = [];
37814
37815     this.bodyEl.setStyle("position", "relative");
37816
37817     this.active = null;
37818     this.activateDelegate = this.activate.createDelegate(this);
37819
37820     this.addEvents({
37821         /**
37822          * @event tabchange
37823          * Fires when the active tab changes
37824          * @param {Roo.TabPanel} this
37825          * @param {Roo.TabPanelItem} activePanel The new active tab
37826          */
37827         "tabchange": true,
37828         /**
37829          * @event beforetabchange
37830          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37831          * @param {Roo.TabPanel} this
37832          * @param {Object} e Set cancel to true on this object to cancel the tab change
37833          * @param {Roo.TabPanelItem} tab The tab being changed to
37834          */
37835         "beforetabchange" : true
37836     });
37837
37838     Roo.EventManager.onWindowResize(this.onResize, this);
37839     this.cpad = this.el.getPadding("lr");
37840     this.hiddenCount = 0;
37841
37842
37843     // toolbar on the tabbar support...
37844     if (this.toolbar) {
37845         alert("no toolbar support yet");
37846         this.toolbar  = false;
37847         /*
37848         var tcfg = this.toolbar;
37849         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37850         this.toolbar = new Roo.Toolbar(tcfg);
37851         if (Roo.isSafari) {
37852             var tbl = tcfg.container.child('table', true);
37853             tbl.setAttribute('width', '100%');
37854         }
37855         */
37856         
37857     }
37858    
37859
37860
37861     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37862 };
37863
37864 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37865     /*
37866      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37867      */
37868     tabPosition : "top",
37869     /*
37870      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37871      */
37872     currentTabWidth : 0,
37873     /*
37874      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37875      */
37876     minTabWidth : 40,
37877     /*
37878      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37879      */
37880     maxTabWidth : 250,
37881     /*
37882      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37883      */
37884     preferredTabWidth : 175,
37885     /*
37886      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37887      */
37888     resizeTabs : false,
37889     /*
37890      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37891      */
37892     monitorResize : true,
37893     /*
37894      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37895      */
37896     toolbar : false,
37897
37898     /**
37899      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37900      * @param {String} id The id of the div to use <b>or create</b>
37901      * @param {String} text The text for the tab
37902      * @param {String} content (optional) Content to put in the TabPanelItem body
37903      * @param {Boolean} closable (optional) True to create a close icon on the tab
37904      * @return {Roo.TabPanelItem} The created TabPanelItem
37905      */
37906     addTab : function(id, text, content, closable, tpl)
37907     {
37908         var item = new Roo.bootstrap.panel.TabItem({
37909             panel: this,
37910             id : id,
37911             text : text,
37912             closable : closable,
37913             tpl : tpl
37914         });
37915         this.addTabItem(item);
37916         if(content){
37917             item.setContent(content);
37918         }
37919         return item;
37920     },
37921
37922     /**
37923      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37924      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37925      * @return {Roo.TabPanelItem}
37926      */
37927     getTab : function(id){
37928         return this.items[id];
37929     },
37930
37931     /**
37932      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37933      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37934      */
37935     hideTab : function(id){
37936         var t = this.items[id];
37937         if(!t.isHidden()){
37938            t.setHidden(true);
37939            this.hiddenCount++;
37940            this.autoSizeTabs();
37941         }
37942     },
37943
37944     /**
37945      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37946      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37947      */
37948     unhideTab : function(id){
37949         var t = this.items[id];
37950         if(t.isHidden()){
37951            t.setHidden(false);
37952            this.hiddenCount--;
37953            this.autoSizeTabs();
37954         }
37955     },
37956
37957     /**
37958      * Adds an existing {@link Roo.TabPanelItem}.
37959      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37960      */
37961     addTabItem : function(item){
37962         this.items[item.id] = item;
37963         this.items.push(item);
37964       //  if(this.resizeTabs){
37965     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37966   //         this.autoSizeTabs();
37967 //        }else{
37968 //            item.autoSize();
37969        // }
37970     },
37971
37972     /**
37973      * Removes a {@link Roo.TabPanelItem}.
37974      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37975      */
37976     removeTab : function(id){
37977         var items = this.items;
37978         var tab = items[id];
37979         if(!tab) { return; }
37980         var index = items.indexOf(tab);
37981         if(this.active == tab && items.length > 1){
37982             var newTab = this.getNextAvailable(index);
37983             if(newTab) {
37984                 newTab.activate();
37985             }
37986         }
37987         this.stripEl.dom.removeChild(tab.pnode.dom);
37988         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37989             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37990         }
37991         items.splice(index, 1);
37992         delete this.items[tab.id];
37993         tab.fireEvent("close", tab);
37994         tab.purgeListeners();
37995         this.autoSizeTabs();
37996     },
37997
37998     getNextAvailable : function(start){
37999         var items = this.items;
38000         var index = start;
38001         // look for a next tab that will slide over to
38002         // replace the one being removed
38003         while(index < items.length){
38004             var item = items[++index];
38005             if(item && !item.isHidden()){
38006                 return item;
38007             }
38008         }
38009         // if one isn't found select the previous tab (on the left)
38010         index = start;
38011         while(index >= 0){
38012             var item = items[--index];
38013             if(item && !item.isHidden()){
38014                 return item;
38015             }
38016         }
38017         return null;
38018     },
38019
38020     /**
38021      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38022      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38023      */
38024     disableTab : function(id){
38025         var tab = this.items[id];
38026         if(tab && this.active != tab){
38027             tab.disable();
38028         }
38029     },
38030
38031     /**
38032      * Enables a {@link Roo.TabPanelItem} that is disabled.
38033      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38034      */
38035     enableTab : function(id){
38036         var tab = this.items[id];
38037         tab.enable();
38038     },
38039
38040     /**
38041      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38042      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38043      * @return {Roo.TabPanelItem} The TabPanelItem.
38044      */
38045     activate : function(id){
38046         var tab = this.items[id];
38047         if(!tab){
38048             return null;
38049         }
38050         if(tab == this.active || tab.disabled){
38051             return tab;
38052         }
38053         var e = {};
38054         this.fireEvent("beforetabchange", this, e, tab);
38055         if(e.cancel !== true && !tab.disabled){
38056             if(this.active){
38057                 this.active.hide();
38058             }
38059             this.active = this.items[id];
38060             this.active.show();
38061             this.fireEvent("tabchange", this, this.active);
38062         }
38063         return tab;
38064     },
38065
38066     /**
38067      * Gets the active {@link Roo.TabPanelItem}.
38068      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38069      */
38070     getActiveTab : function(){
38071         return this.active;
38072     },
38073
38074     /**
38075      * Updates the tab body element to fit the height of the container element
38076      * for overflow scrolling
38077      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38078      */
38079     syncHeight : function(targetHeight){
38080         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38081         var bm = this.bodyEl.getMargins();
38082         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38083         this.bodyEl.setHeight(newHeight);
38084         return newHeight;
38085     },
38086
38087     onResize : function(){
38088         if(this.monitorResize){
38089             this.autoSizeTabs();
38090         }
38091     },
38092
38093     /**
38094      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38095      */
38096     beginUpdate : function(){
38097         this.updating = true;
38098     },
38099
38100     /**
38101      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38102      */
38103     endUpdate : function(){
38104         this.updating = false;
38105         this.autoSizeTabs();
38106     },
38107
38108     /**
38109      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38110      */
38111     autoSizeTabs : function(){
38112         var count = this.items.length;
38113         var vcount = count - this.hiddenCount;
38114         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38115             return;
38116         }
38117         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38118         var availWidth = Math.floor(w / vcount);
38119         var b = this.stripBody;
38120         if(b.getWidth() > w){
38121             var tabs = this.items;
38122             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38123             if(availWidth < this.minTabWidth){
38124                 /*if(!this.sleft){    // incomplete scrolling code
38125                     this.createScrollButtons();
38126                 }
38127                 this.showScroll();
38128                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38129             }
38130         }else{
38131             if(this.currentTabWidth < this.preferredTabWidth){
38132                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38133             }
38134         }
38135     },
38136
38137     /**
38138      * Returns the number of tabs in this TabPanel.
38139      * @return {Number}
38140      */
38141      getCount : function(){
38142          return this.items.length;
38143      },
38144
38145     /**
38146      * Resizes all the tabs to the passed width
38147      * @param {Number} The new width
38148      */
38149     setTabWidth : function(width){
38150         this.currentTabWidth = width;
38151         for(var i = 0, len = this.items.length; i < len; i++) {
38152                 if(!this.items[i].isHidden()) {
38153                 this.items[i].setWidth(width);
38154             }
38155         }
38156     },
38157
38158     /**
38159      * Destroys this TabPanel
38160      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38161      */
38162     destroy : function(removeEl){
38163         Roo.EventManager.removeResizeListener(this.onResize, this);
38164         for(var i = 0, len = this.items.length; i < len; i++){
38165             this.items[i].purgeListeners();
38166         }
38167         if(removeEl === true){
38168             this.el.update("");
38169             this.el.remove();
38170         }
38171     },
38172     
38173     createStrip : function(container)
38174     {
38175         var strip = document.createElement("nav");
38176         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38177         container.appendChild(strip);
38178         return strip;
38179     },
38180     
38181     createStripList : function(strip)
38182     {
38183         // div wrapper for retard IE
38184         // returns the "tr" element.
38185         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38186         //'<div class="x-tabs-strip-wrap">'+
38187           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38188           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38189         return strip.firstChild; //.firstChild.firstChild.firstChild;
38190     },
38191     createBody : function(container)
38192     {
38193         var body = document.createElement("div");
38194         Roo.id(body, "tab-body");
38195         //Roo.fly(body).addClass("x-tabs-body");
38196         Roo.fly(body).addClass("tab-content");
38197         container.appendChild(body);
38198         return body;
38199     },
38200     createItemBody :function(bodyEl, id){
38201         var body = Roo.getDom(id);
38202         if(!body){
38203             body = document.createElement("div");
38204             body.id = id;
38205         }
38206         //Roo.fly(body).addClass("x-tabs-item-body");
38207         Roo.fly(body).addClass("tab-pane");
38208          bodyEl.insertBefore(body, bodyEl.firstChild);
38209         return body;
38210     },
38211     /** @private */
38212     createStripElements :  function(stripEl, text, closable, tpl)
38213     {
38214         var td = document.createElement("li"); // was td..
38215         
38216         
38217         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38218         
38219         
38220         stripEl.appendChild(td);
38221         /*if(closable){
38222             td.className = "x-tabs-closable";
38223             if(!this.closeTpl){
38224                 this.closeTpl = new Roo.Template(
38225                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38226                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38227                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38228                 );
38229             }
38230             var el = this.closeTpl.overwrite(td, {"text": text});
38231             var close = el.getElementsByTagName("div")[0];
38232             var inner = el.getElementsByTagName("em")[0];
38233             return {"el": el, "close": close, "inner": inner};
38234         } else {
38235         */
38236         // not sure what this is..
38237 //            if(!this.tabTpl){
38238                 //this.tabTpl = new Roo.Template(
38239                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38240                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38241                 //);
38242 //                this.tabTpl = new Roo.Template(
38243 //                   '<a href="#">' +
38244 //                   '<span unselectable="on"' +
38245 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38246 //                            ' >{text}</span></a>'
38247 //                );
38248 //                
38249 //            }
38250
38251
38252             var template = tpl || this.tabTpl || false;
38253             
38254             if(!template){
38255                 
38256                 template = new Roo.Template(
38257                    '<a href="#">' +
38258                    '<span unselectable="on"' +
38259                             (this.disableTooltips ? '' : ' title="{text}"') +
38260                             ' >{text}</span></a>'
38261                 );
38262             }
38263             
38264             switch (typeof(template)) {
38265                 case 'object' :
38266                     break;
38267                 case 'string' :
38268                     template = new Roo.Template(template);
38269                     break;
38270                 default :
38271                     break;
38272             }
38273             
38274             var el = template.overwrite(td, {"text": text});
38275             
38276             var inner = el.getElementsByTagName("span")[0];
38277             
38278             return {"el": el, "inner": inner};
38279             
38280     }
38281         
38282     
38283 });
38284
38285 /**
38286  * @class Roo.TabPanelItem
38287  * @extends Roo.util.Observable
38288  * Represents an individual item (tab plus body) in a TabPanel.
38289  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38290  * @param {String} id The id of this TabPanelItem
38291  * @param {String} text The text for the tab of this TabPanelItem
38292  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38293  */
38294 Roo.bootstrap.panel.TabItem = function(config){
38295     /**
38296      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38297      * @type Roo.TabPanel
38298      */
38299     this.tabPanel = config.panel;
38300     /**
38301      * The id for this TabPanelItem
38302      * @type String
38303      */
38304     this.id = config.id;
38305     /** @private */
38306     this.disabled = false;
38307     /** @private */
38308     this.text = config.text;
38309     /** @private */
38310     this.loaded = false;
38311     this.closable = config.closable;
38312
38313     /**
38314      * The body element for this TabPanelItem.
38315      * @type Roo.Element
38316      */
38317     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38318     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38319     this.bodyEl.setStyle("display", "block");
38320     this.bodyEl.setStyle("zoom", "1");
38321     //this.hideAction();
38322
38323     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38324     /** @private */
38325     this.el = Roo.get(els.el);
38326     this.inner = Roo.get(els.inner, true);
38327     this.textEl = Roo.get(this.el.dom.firstChild, true);
38328     this.pnode = Roo.get(els.el.parentNode, true);
38329 //    this.el.on("mousedown", this.onTabMouseDown, this);
38330     this.el.on("click", this.onTabClick, this);
38331     /** @private */
38332     if(config.closable){
38333         var c = Roo.get(els.close, true);
38334         c.dom.title = this.closeText;
38335         c.addClassOnOver("close-over");
38336         c.on("click", this.closeClick, this);
38337      }
38338
38339     this.addEvents({
38340          /**
38341          * @event activate
38342          * Fires when this tab becomes the active tab.
38343          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38344          * @param {Roo.TabPanelItem} this
38345          */
38346         "activate": true,
38347         /**
38348          * @event beforeclose
38349          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38350          * @param {Roo.TabPanelItem} this
38351          * @param {Object} e Set cancel to true on this object to cancel the close.
38352          */
38353         "beforeclose": true,
38354         /**
38355          * @event close
38356          * Fires when this tab is closed.
38357          * @param {Roo.TabPanelItem} this
38358          */
38359          "close": true,
38360         /**
38361          * @event deactivate
38362          * Fires when this tab is no longer the active tab.
38363          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38364          * @param {Roo.TabPanelItem} this
38365          */
38366          "deactivate" : true
38367     });
38368     this.hidden = false;
38369
38370     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38371 };
38372
38373 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38374            {
38375     purgeListeners : function(){
38376        Roo.util.Observable.prototype.purgeListeners.call(this);
38377        this.el.removeAllListeners();
38378     },
38379     /**
38380      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38381      */
38382     show : function(){
38383         this.pnode.addClass("active");
38384         this.showAction();
38385         if(Roo.isOpera){
38386             this.tabPanel.stripWrap.repaint();
38387         }
38388         this.fireEvent("activate", this.tabPanel, this);
38389     },
38390
38391     /**
38392      * Returns true if this tab is the active tab.
38393      * @return {Boolean}
38394      */
38395     isActive : function(){
38396         return this.tabPanel.getActiveTab() == this;
38397     },
38398
38399     /**
38400      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38401      */
38402     hide : function(){
38403         this.pnode.removeClass("active");
38404         this.hideAction();
38405         this.fireEvent("deactivate", this.tabPanel, this);
38406     },
38407
38408     hideAction : function(){
38409         this.bodyEl.hide();
38410         this.bodyEl.setStyle("position", "absolute");
38411         this.bodyEl.setLeft("-20000px");
38412         this.bodyEl.setTop("-20000px");
38413     },
38414
38415     showAction : function(){
38416         this.bodyEl.setStyle("position", "relative");
38417         this.bodyEl.setTop("");
38418         this.bodyEl.setLeft("");
38419         this.bodyEl.show();
38420     },
38421
38422     /**
38423      * Set the tooltip for the tab.
38424      * @param {String} tooltip The tab's tooltip
38425      */
38426     setTooltip : function(text){
38427         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38428             this.textEl.dom.qtip = text;
38429             this.textEl.dom.removeAttribute('title');
38430         }else{
38431             this.textEl.dom.title = text;
38432         }
38433     },
38434
38435     onTabClick : function(e){
38436         e.preventDefault();
38437         this.tabPanel.activate(this.id);
38438     },
38439
38440     onTabMouseDown : function(e){
38441         e.preventDefault();
38442         this.tabPanel.activate(this.id);
38443     },
38444 /*
38445     getWidth : function(){
38446         return this.inner.getWidth();
38447     },
38448
38449     setWidth : function(width){
38450         var iwidth = width - this.pnode.getPadding("lr");
38451         this.inner.setWidth(iwidth);
38452         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38453         this.pnode.setWidth(width);
38454     },
38455 */
38456     /**
38457      * Show or hide the tab
38458      * @param {Boolean} hidden True to hide or false to show.
38459      */
38460     setHidden : function(hidden){
38461         this.hidden = hidden;
38462         this.pnode.setStyle("display", hidden ? "none" : "");
38463     },
38464
38465     /**
38466      * Returns true if this tab is "hidden"
38467      * @return {Boolean}
38468      */
38469     isHidden : function(){
38470         return this.hidden;
38471     },
38472
38473     /**
38474      * Returns the text for this tab
38475      * @return {String}
38476      */
38477     getText : function(){
38478         return this.text;
38479     },
38480     /*
38481     autoSize : function(){
38482         //this.el.beginMeasure();
38483         this.textEl.setWidth(1);
38484         /*
38485          *  #2804 [new] Tabs in Roojs
38486          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38487          */
38488         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38489         //this.el.endMeasure();
38490     //},
38491
38492     /**
38493      * Sets the text for the tab (Note: this also sets the tooltip text)
38494      * @param {String} text The tab's text and tooltip
38495      */
38496     setText : function(text){
38497         this.text = text;
38498         this.textEl.update(text);
38499         this.setTooltip(text);
38500         //if(!this.tabPanel.resizeTabs){
38501         //    this.autoSize();
38502         //}
38503     },
38504     /**
38505      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38506      */
38507     activate : function(){
38508         this.tabPanel.activate(this.id);
38509     },
38510
38511     /**
38512      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38513      */
38514     disable : function(){
38515         if(this.tabPanel.active != this){
38516             this.disabled = true;
38517             this.pnode.addClass("disabled");
38518         }
38519     },
38520
38521     /**
38522      * Enables this TabPanelItem if it was previously disabled.
38523      */
38524     enable : function(){
38525         this.disabled = false;
38526         this.pnode.removeClass("disabled");
38527     },
38528
38529     /**
38530      * Sets the content for this TabPanelItem.
38531      * @param {String} content The content
38532      * @param {Boolean} loadScripts true to look for and load scripts
38533      */
38534     setContent : function(content, loadScripts){
38535         this.bodyEl.update(content, loadScripts);
38536     },
38537
38538     /**
38539      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38540      * @return {Roo.UpdateManager} The UpdateManager
38541      */
38542     getUpdateManager : function(){
38543         return this.bodyEl.getUpdateManager();
38544     },
38545
38546     /**
38547      * Set a URL to be used to load the content for this TabPanelItem.
38548      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38549      * @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)
38550      * @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)
38551      * @return {Roo.UpdateManager} The UpdateManager
38552      */
38553     setUrl : function(url, params, loadOnce){
38554         if(this.refreshDelegate){
38555             this.un('activate', this.refreshDelegate);
38556         }
38557         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38558         this.on("activate", this.refreshDelegate);
38559         return this.bodyEl.getUpdateManager();
38560     },
38561
38562     /** @private */
38563     _handleRefresh : function(url, params, loadOnce){
38564         if(!loadOnce || !this.loaded){
38565             var updater = this.bodyEl.getUpdateManager();
38566             updater.update(url, params, this._setLoaded.createDelegate(this));
38567         }
38568     },
38569
38570     /**
38571      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38572      *   Will fail silently if the setUrl method has not been called.
38573      *   This does not activate the panel, just updates its content.
38574      */
38575     refresh : function(){
38576         if(this.refreshDelegate){
38577            this.loaded = false;
38578            this.refreshDelegate();
38579         }
38580     },
38581
38582     /** @private */
38583     _setLoaded : function(){
38584         this.loaded = true;
38585     },
38586
38587     /** @private */
38588     closeClick : function(e){
38589         var o = {};
38590         e.stopEvent();
38591         this.fireEvent("beforeclose", this, o);
38592         if(o.cancel !== true){
38593             this.tabPanel.removeTab(this.id);
38594         }
38595     },
38596     /**
38597      * The text displayed in the tooltip for the close icon.
38598      * @type String
38599      */
38600     closeText : "Close this tab"
38601 });
38602 /**
38603 *    This script refer to:
38604 *    Title: International Telephone Input
38605 *    Author: Jack O'Connor
38606 *    Code version:  v12.1.12
38607 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38608 **/
38609
38610 Roo.bootstrap.PhoneInputData = function() {
38611     var d = [
38612       [
38613         "Afghanistan (‫افغانستان‬‎)",
38614         "af",
38615         "93"
38616       ],
38617       [
38618         "Albania (Shqipëri)",
38619         "al",
38620         "355"
38621       ],
38622       [
38623         "Algeria (‫الجزائر‬‎)",
38624         "dz",
38625         "213"
38626       ],
38627       [
38628         "American Samoa",
38629         "as",
38630         "1684"
38631       ],
38632       [
38633         "Andorra",
38634         "ad",
38635         "376"
38636       ],
38637       [
38638         "Angola",
38639         "ao",
38640         "244"
38641       ],
38642       [
38643         "Anguilla",
38644         "ai",
38645         "1264"
38646       ],
38647       [
38648         "Antigua and Barbuda",
38649         "ag",
38650         "1268"
38651       ],
38652       [
38653         "Argentina",
38654         "ar",
38655         "54"
38656       ],
38657       [
38658         "Armenia (Հայաստան)",
38659         "am",
38660         "374"
38661       ],
38662       [
38663         "Aruba",
38664         "aw",
38665         "297"
38666       ],
38667       [
38668         "Australia",
38669         "au",
38670         "61",
38671         0
38672       ],
38673       [
38674         "Austria (Österreich)",
38675         "at",
38676         "43"
38677       ],
38678       [
38679         "Azerbaijan (Azərbaycan)",
38680         "az",
38681         "994"
38682       ],
38683       [
38684         "Bahamas",
38685         "bs",
38686         "1242"
38687       ],
38688       [
38689         "Bahrain (‫البحرين‬‎)",
38690         "bh",
38691         "973"
38692       ],
38693       [
38694         "Bangladesh (বাংলাদেশ)",
38695         "bd",
38696         "880"
38697       ],
38698       [
38699         "Barbados",
38700         "bb",
38701         "1246"
38702       ],
38703       [
38704         "Belarus (Беларусь)",
38705         "by",
38706         "375"
38707       ],
38708       [
38709         "Belgium (België)",
38710         "be",
38711         "32"
38712       ],
38713       [
38714         "Belize",
38715         "bz",
38716         "501"
38717       ],
38718       [
38719         "Benin (Bénin)",
38720         "bj",
38721         "229"
38722       ],
38723       [
38724         "Bermuda",
38725         "bm",
38726         "1441"
38727       ],
38728       [
38729         "Bhutan (འབྲུག)",
38730         "bt",
38731         "975"
38732       ],
38733       [
38734         "Bolivia",
38735         "bo",
38736         "591"
38737       ],
38738       [
38739         "Bosnia and Herzegovina (Босна и Херцеговина)",
38740         "ba",
38741         "387"
38742       ],
38743       [
38744         "Botswana",
38745         "bw",
38746         "267"
38747       ],
38748       [
38749         "Brazil (Brasil)",
38750         "br",
38751         "55"
38752       ],
38753       [
38754         "British Indian Ocean Territory",
38755         "io",
38756         "246"
38757       ],
38758       [
38759         "British Virgin Islands",
38760         "vg",
38761         "1284"
38762       ],
38763       [
38764         "Brunei",
38765         "bn",
38766         "673"
38767       ],
38768       [
38769         "Bulgaria (България)",
38770         "bg",
38771         "359"
38772       ],
38773       [
38774         "Burkina Faso",
38775         "bf",
38776         "226"
38777       ],
38778       [
38779         "Burundi (Uburundi)",
38780         "bi",
38781         "257"
38782       ],
38783       [
38784         "Cambodia (កម្ពុជា)",
38785         "kh",
38786         "855"
38787       ],
38788       [
38789         "Cameroon (Cameroun)",
38790         "cm",
38791         "237"
38792       ],
38793       [
38794         "Canada",
38795         "ca",
38796         "1",
38797         1,
38798         ["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"]
38799       ],
38800       [
38801         "Cape Verde (Kabu Verdi)",
38802         "cv",
38803         "238"
38804       ],
38805       [
38806         "Caribbean Netherlands",
38807         "bq",
38808         "599",
38809         1
38810       ],
38811       [
38812         "Cayman Islands",
38813         "ky",
38814         "1345"
38815       ],
38816       [
38817         "Central African Republic (République centrafricaine)",
38818         "cf",
38819         "236"
38820       ],
38821       [
38822         "Chad (Tchad)",
38823         "td",
38824         "235"
38825       ],
38826       [
38827         "Chile",
38828         "cl",
38829         "56"
38830       ],
38831       [
38832         "China (中国)",
38833         "cn",
38834         "86"
38835       ],
38836       [
38837         "Christmas Island",
38838         "cx",
38839         "61",
38840         2
38841       ],
38842       [
38843         "Cocos (Keeling) Islands",
38844         "cc",
38845         "61",
38846         1
38847       ],
38848       [
38849         "Colombia",
38850         "co",
38851         "57"
38852       ],
38853       [
38854         "Comoros (‫جزر القمر‬‎)",
38855         "km",
38856         "269"
38857       ],
38858       [
38859         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38860         "cd",
38861         "243"
38862       ],
38863       [
38864         "Congo (Republic) (Congo-Brazzaville)",
38865         "cg",
38866         "242"
38867       ],
38868       [
38869         "Cook Islands",
38870         "ck",
38871         "682"
38872       ],
38873       [
38874         "Costa Rica",
38875         "cr",
38876         "506"
38877       ],
38878       [
38879         "Côte d’Ivoire",
38880         "ci",
38881         "225"
38882       ],
38883       [
38884         "Croatia (Hrvatska)",
38885         "hr",
38886         "385"
38887       ],
38888       [
38889         "Cuba",
38890         "cu",
38891         "53"
38892       ],
38893       [
38894         "Curaçao",
38895         "cw",
38896         "599",
38897         0
38898       ],
38899       [
38900         "Cyprus (Κύπρος)",
38901         "cy",
38902         "357"
38903       ],
38904       [
38905         "Czech Republic (Česká republika)",
38906         "cz",
38907         "420"
38908       ],
38909       [
38910         "Denmark (Danmark)",
38911         "dk",
38912         "45"
38913       ],
38914       [
38915         "Djibouti",
38916         "dj",
38917         "253"
38918       ],
38919       [
38920         "Dominica",
38921         "dm",
38922         "1767"
38923       ],
38924       [
38925         "Dominican Republic (República Dominicana)",
38926         "do",
38927         "1",
38928         2,
38929         ["809", "829", "849"]
38930       ],
38931       [
38932         "Ecuador",
38933         "ec",
38934         "593"
38935       ],
38936       [
38937         "Egypt (‫مصر‬‎)",
38938         "eg",
38939         "20"
38940       ],
38941       [
38942         "El Salvador",
38943         "sv",
38944         "503"
38945       ],
38946       [
38947         "Equatorial Guinea (Guinea Ecuatorial)",
38948         "gq",
38949         "240"
38950       ],
38951       [
38952         "Eritrea",
38953         "er",
38954         "291"
38955       ],
38956       [
38957         "Estonia (Eesti)",
38958         "ee",
38959         "372"
38960       ],
38961       [
38962         "Ethiopia",
38963         "et",
38964         "251"
38965       ],
38966       [
38967         "Falkland Islands (Islas Malvinas)",
38968         "fk",
38969         "500"
38970       ],
38971       [
38972         "Faroe Islands (Føroyar)",
38973         "fo",
38974         "298"
38975       ],
38976       [
38977         "Fiji",
38978         "fj",
38979         "679"
38980       ],
38981       [
38982         "Finland (Suomi)",
38983         "fi",
38984         "358",
38985         0
38986       ],
38987       [
38988         "France",
38989         "fr",
38990         "33"
38991       ],
38992       [
38993         "French Guiana (Guyane française)",
38994         "gf",
38995         "594"
38996       ],
38997       [
38998         "French Polynesia (Polynésie française)",
38999         "pf",
39000         "689"
39001       ],
39002       [
39003         "Gabon",
39004         "ga",
39005         "241"
39006       ],
39007       [
39008         "Gambia",
39009         "gm",
39010         "220"
39011       ],
39012       [
39013         "Georgia (საქართველო)",
39014         "ge",
39015         "995"
39016       ],
39017       [
39018         "Germany (Deutschland)",
39019         "de",
39020         "49"
39021       ],
39022       [
39023         "Ghana (Gaana)",
39024         "gh",
39025         "233"
39026       ],
39027       [
39028         "Gibraltar",
39029         "gi",
39030         "350"
39031       ],
39032       [
39033         "Greece (Ελλάδα)",
39034         "gr",
39035         "30"
39036       ],
39037       [
39038         "Greenland (Kalaallit Nunaat)",
39039         "gl",
39040         "299"
39041       ],
39042       [
39043         "Grenada",
39044         "gd",
39045         "1473"
39046       ],
39047       [
39048         "Guadeloupe",
39049         "gp",
39050         "590",
39051         0
39052       ],
39053       [
39054         "Guam",
39055         "gu",
39056         "1671"
39057       ],
39058       [
39059         "Guatemala",
39060         "gt",
39061         "502"
39062       ],
39063       [
39064         "Guernsey",
39065         "gg",
39066         "44",
39067         1
39068       ],
39069       [
39070         "Guinea (Guinée)",
39071         "gn",
39072         "224"
39073       ],
39074       [
39075         "Guinea-Bissau (Guiné Bissau)",
39076         "gw",
39077         "245"
39078       ],
39079       [
39080         "Guyana",
39081         "gy",
39082         "592"
39083       ],
39084       [
39085         "Haiti",
39086         "ht",
39087         "509"
39088       ],
39089       [
39090         "Honduras",
39091         "hn",
39092         "504"
39093       ],
39094       [
39095         "Hong Kong (香港)",
39096         "hk",
39097         "852"
39098       ],
39099       [
39100         "Hungary (Magyarország)",
39101         "hu",
39102         "36"
39103       ],
39104       [
39105         "Iceland (Ísland)",
39106         "is",
39107         "354"
39108       ],
39109       [
39110         "India (भारत)",
39111         "in",
39112         "91"
39113       ],
39114       [
39115         "Indonesia",
39116         "id",
39117         "62"
39118       ],
39119       [
39120         "Iran (‫ایران‬‎)",
39121         "ir",
39122         "98"
39123       ],
39124       [
39125         "Iraq (‫العراق‬‎)",
39126         "iq",
39127         "964"
39128       ],
39129       [
39130         "Ireland",
39131         "ie",
39132         "353"
39133       ],
39134       [
39135         "Isle of Man",
39136         "im",
39137         "44",
39138         2
39139       ],
39140       [
39141         "Israel (‫ישראל‬‎)",
39142         "il",
39143         "972"
39144       ],
39145       [
39146         "Italy (Italia)",
39147         "it",
39148         "39",
39149         0
39150       ],
39151       [
39152         "Jamaica",
39153         "jm",
39154         "1876"
39155       ],
39156       [
39157         "Japan (日本)",
39158         "jp",
39159         "81"
39160       ],
39161       [
39162         "Jersey",
39163         "je",
39164         "44",
39165         3
39166       ],
39167       [
39168         "Jordan (‫الأردن‬‎)",
39169         "jo",
39170         "962"
39171       ],
39172       [
39173         "Kazakhstan (Казахстан)",
39174         "kz",
39175         "7",
39176         1
39177       ],
39178       [
39179         "Kenya",
39180         "ke",
39181         "254"
39182       ],
39183       [
39184         "Kiribati",
39185         "ki",
39186         "686"
39187       ],
39188       [
39189         "Kosovo",
39190         "xk",
39191         "383"
39192       ],
39193       [
39194         "Kuwait (‫الكويت‬‎)",
39195         "kw",
39196         "965"
39197       ],
39198       [
39199         "Kyrgyzstan (Кыргызстан)",
39200         "kg",
39201         "996"
39202       ],
39203       [
39204         "Laos (ລາວ)",
39205         "la",
39206         "856"
39207       ],
39208       [
39209         "Latvia (Latvija)",
39210         "lv",
39211         "371"
39212       ],
39213       [
39214         "Lebanon (‫لبنان‬‎)",
39215         "lb",
39216         "961"
39217       ],
39218       [
39219         "Lesotho",
39220         "ls",
39221         "266"
39222       ],
39223       [
39224         "Liberia",
39225         "lr",
39226         "231"
39227       ],
39228       [
39229         "Libya (‫ليبيا‬‎)",
39230         "ly",
39231         "218"
39232       ],
39233       [
39234         "Liechtenstein",
39235         "li",
39236         "423"
39237       ],
39238       [
39239         "Lithuania (Lietuva)",
39240         "lt",
39241         "370"
39242       ],
39243       [
39244         "Luxembourg",
39245         "lu",
39246         "352"
39247       ],
39248       [
39249         "Macau (澳門)",
39250         "mo",
39251         "853"
39252       ],
39253       [
39254         "Macedonia (FYROM) (Македонија)",
39255         "mk",
39256         "389"
39257       ],
39258       [
39259         "Madagascar (Madagasikara)",
39260         "mg",
39261         "261"
39262       ],
39263       [
39264         "Malawi",
39265         "mw",
39266         "265"
39267       ],
39268       [
39269         "Malaysia",
39270         "my",
39271         "60"
39272       ],
39273       [
39274         "Maldives",
39275         "mv",
39276         "960"
39277       ],
39278       [
39279         "Mali",
39280         "ml",
39281         "223"
39282       ],
39283       [
39284         "Malta",
39285         "mt",
39286         "356"
39287       ],
39288       [
39289         "Marshall Islands",
39290         "mh",
39291         "692"
39292       ],
39293       [
39294         "Martinique",
39295         "mq",
39296         "596"
39297       ],
39298       [
39299         "Mauritania (‫موريتانيا‬‎)",
39300         "mr",
39301         "222"
39302       ],
39303       [
39304         "Mauritius (Moris)",
39305         "mu",
39306         "230"
39307       ],
39308       [
39309         "Mayotte",
39310         "yt",
39311         "262",
39312         1
39313       ],
39314       [
39315         "Mexico (México)",
39316         "mx",
39317         "52"
39318       ],
39319       [
39320         "Micronesia",
39321         "fm",
39322         "691"
39323       ],
39324       [
39325         "Moldova (Republica Moldova)",
39326         "md",
39327         "373"
39328       ],
39329       [
39330         "Monaco",
39331         "mc",
39332         "377"
39333       ],
39334       [
39335         "Mongolia (Монгол)",
39336         "mn",
39337         "976"
39338       ],
39339       [
39340         "Montenegro (Crna Gora)",
39341         "me",
39342         "382"
39343       ],
39344       [
39345         "Montserrat",
39346         "ms",
39347         "1664"
39348       ],
39349       [
39350         "Morocco (‫المغرب‬‎)",
39351         "ma",
39352         "212",
39353         0
39354       ],
39355       [
39356         "Mozambique (Moçambique)",
39357         "mz",
39358         "258"
39359       ],
39360       [
39361         "Myanmar (Burma) (မြန်မာ)",
39362         "mm",
39363         "95"
39364       ],
39365       [
39366         "Namibia (Namibië)",
39367         "na",
39368         "264"
39369       ],
39370       [
39371         "Nauru",
39372         "nr",
39373         "674"
39374       ],
39375       [
39376         "Nepal (नेपाल)",
39377         "np",
39378         "977"
39379       ],
39380       [
39381         "Netherlands (Nederland)",
39382         "nl",
39383         "31"
39384       ],
39385       [
39386         "New Caledonia (Nouvelle-Calédonie)",
39387         "nc",
39388         "687"
39389       ],
39390       [
39391         "New Zealand",
39392         "nz",
39393         "64"
39394       ],
39395       [
39396         "Nicaragua",
39397         "ni",
39398         "505"
39399       ],
39400       [
39401         "Niger (Nijar)",
39402         "ne",
39403         "227"
39404       ],
39405       [
39406         "Nigeria",
39407         "ng",
39408         "234"
39409       ],
39410       [
39411         "Niue",
39412         "nu",
39413         "683"
39414       ],
39415       [
39416         "Norfolk Island",
39417         "nf",
39418         "672"
39419       ],
39420       [
39421         "North Korea (조선 민주주의 인민 공화국)",
39422         "kp",
39423         "850"
39424       ],
39425       [
39426         "Northern Mariana Islands",
39427         "mp",
39428         "1670"
39429       ],
39430       [
39431         "Norway (Norge)",
39432         "no",
39433         "47",
39434         0
39435       ],
39436       [
39437         "Oman (‫عُمان‬‎)",
39438         "om",
39439         "968"
39440       ],
39441       [
39442         "Pakistan (‫پاکستان‬‎)",
39443         "pk",
39444         "92"
39445       ],
39446       [
39447         "Palau",
39448         "pw",
39449         "680"
39450       ],
39451       [
39452         "Palestine (‫فلسطين‬‎)",
39453         "ps",
39454         "970"
39455       ],
39456       [
39457         "Panama (Panamá)",
39458         "pa",
39459         "507"
39460       ],
39461       [
39462         "Papua New Guinea",
39463         "pg",
39464         "675"
39465       ],
39466       [
39467         "Paraguay",
39468         "py",
39469         "595"
39470       ],
39471       [
39472         "Peru (Perú)",
39473         "pe",
39474         "51"
39475       ],
39476       [
39477         "Philippines",
39478         "ph",
39479         "63"
39480       ],
39481       [
39482         "Poland (Polska)",
39483         "pl",
39484         "48"
39485       ],
39486       [
39487         "Portugal",
39488         "pt",
39489         "351"
39490       ],
39491       [
39492         "Puerto Rico",
39493         "pr",
39494         "1",
39495         3,
39496         ["787", "939"]
39497       ],
39498       [
39499         "Qatar (‫قطر‬‎)",
39500         "qa",
39501         "974"
39502       ],
39503       [
39504         "Réunion (La Réunion)",
39505         "re",
39506         "262",
39507         0
39508       ],
39509       [
39510         "Romania (România)",
39511         "ro",
39512         "40"
39513       ],
39514       [
39515         "Russia (Россия)",
39516         "ru",
39517         "7",
39518         0
39519       ],
39520       [
39521         "Rwanda",
39522         "rw",
39523         "250"
39524       ],
39525       [
39526         "Saint Barthélemy",
39527         "bl",
39528         "590",
39529         1
39530       ],
39531       [
39532         "Saint Helena",
39533         "sh",
39534         "290"
39535       ],
39536       [
39537         "Saint Kitts and Nevis",
39538         "kn",
39539         "1869"
39540       ],
39541       [
39542         "Saint Lucia",
39543         "lc",
39544         "1758"
39545       ],
39546       [
39547         "Saint Martin (Saint-Martin (partie française))",
39548         "mf",
39549         "590",
39550         2
39551       ],
39552       [
39553         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39554         "pm",
39555         "508"
39556       ],
39557       [
39558         "Saint Vincent and the Grenadines",
39559         "vc",
39560         "1784"
39561       ],
39562       [
39563         "Samoa",
39564         "ws",
39565         "685"
39566       ],
39567       [
39568         "San Marino",
39569         "sm",
39570         "378"
39571       ],
39572       [
39573         "São Tomé and Príncipe (São Tomé e Príncipe)",
39574         "st",
39575         "239"
39576       ],
39577       [
39578         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39579         "sa",
39580         "966"
39581       ],
39582       [
39583         "Senegal (Sénégal)",
39584         "sn",
39585         "221"
39586       ],
39587       [
39588         "Serbia (Србија)",
39589         "rs",
39590         "381"
39591       ],
39592       [
39593         "Seychelles",
39594         "sc",
39595         "248"
39596       ],
39597       [
39598         "Sierra Leone",
39599         "sl",
39600         "232"
39601       ],
39602       [
39603         "Singapore",
39604         "sg",
39605         "65"
39606       ],
39607       [
39608         "Sint Maarten",
39609         "sx",
39610         "1721"
39611       ],
39612       [
39613         "Slovakia (Slovensko)",
39614         "sk",
39615         "421"
39616       ],
39617       [
39618         "Slovenia (Slovenija)",
39619         "si",
39620         "386"
39621       ],
39622       [
39623         "Solomon Islands",
39624         "sb",
39625         "677"
39626       ],
39627       [
39628         "Somalia (Soomaaliya)",
39629         "so",
39630         "252"
39631       ],
39632       [
39633         "South Africa",
39634         "za",
39635         "27"
39636       ],
39637       [
39638         "South Korea (대한민국)",
39639         "kr",
39640         "82"
39641       ],
39642       [
39643         "South Sudan (‫جنوب السودان‬‎)",
39644         "ss",
39645         "211"
39646       ],
39647       [
39648         "Spain (España)",
39649         "es",
39650         "34"
39651       ],
39652       [
39653         "Sri Lanka (ශ්‍රී ලංකාව)",
39654         "lk",
39655         "94"
39656       ],
39657       [
39658         "Sudan (‫السودان‬‎)",
39659         "sd",
39660         "249"
39661       ],
39662       [
39663         "Suriname",
39664         "sr",
39665         "597"
39666       ],
39667       [
39668         "Svalbard and Jan Mayen",
39669         "sj",
39670         "47",
39671         1
39672       ],
39673       [
39674         "Swaziland",
39675         "sz",
39676         "268"
39677       ],
39678       [
39679         "Sweden (Sverige)",
39680         "se",
39681         "46"
39682       ],
39683       [
39684         "Switzerland (Schweiz)",
39685         "ch",
39686         "41"
39687       ],
39688       [
39689         "Syria (‫سوريا‬‎)",
39690         "sy",
39691         "963"
39692       ],
39693       [
39694         "Taiwan (台灣)",
39695         "tw",
39696         "886"
39697       ],
39698       [
39699         "Tajikistan",
39700         "tj",
39701         "992"
39702       ],
39703       [
39704         "Tanzania",
39705         "tz",
39706         "255"
39707       ],
39708       [
39709         "Thailand (ไทย)",
39710         "th",
39711         "66"
39712       ],
39713       [
39714         "Timor-Leste",
39715         "tl",
39716         "670"
39717       ],
39718       [
39719         "Togo",
39720         "tg",
39721         "228"
39722       ],
39723       [
39724         "Tokelau",
39725         "tk",
39726         "690"
39727       ],
39728       [
39729         "Tonga",
39730         "to",
39731         "676"
39732       ],
39733       [
39734         "Trinidad and Tobago",
39735         "tt",
39736         "1868"
39737       ],
39738       [
39739         "Tunisia (‫تونس‬‎)",
39740         "tn",
39741         "216"
39742       ],
39743       [
39744         "Turkey (Türkiye)",
39745         "tr",
39746         "90"
39747       ],
39748       [
39749         "Turkmenistan",
39750         "tm",
39751         "993"
39752       ],
39753       [
39754         "Turks and Caicos Islands",
39755         "tc",
39756         "1649"
39757       ],
39758       [
39759         "Tuvalu",
39760         "tv",
39761         "688"
39762       ],
39763       [
39764         "U.S. Virgin Islands",
39765         "vi",
39766         "1340"
39767       ],
39768       [
39769         "Uganda",
39770         "ug",
39771         "256"
39772       ],
39773       [
39774         "Ukraine (Україна)",
39775         "ua",
39776         "380"
39777       ],
39778       [
39779         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39780         "ae",
39781         "971"
39782       ],
39783       [
39784         "United Kingdom",
39785         "gb",
39786         "44",
39787         0
39788       ],
39789       [
39790         "United States",
39791         "us",
39792         "1",
39793         0
39794       ],
39795       [
39796         "Uruguay",
39797         "uy",
39798         "598"
39799       ],
39800       [
39801         "Uzbekistan (Oʻzbekiston)",
39802         "uz",
39803         "998"
39804       ],
39805       [
39806         "Vanuatu",
39807         "vu",
39808         "678"
39809       ],
39810       [
39811         "Vatican City (Città del Vaticano)",
39812         "va",
39813         "39",
39814         1
39815       ],
39816       [
39817         "Venezuela",
39818         "ve",
39819         "58"
39820       ],
39821       [
39822         "Vietnam (Việt Nam)",
39823         "vn",
39824         "84"
39825       ],
39826       [
39827         "Wallis and Futuna (Wallis-et-Futuna)",
39828         "wf",
39829         "681"
39830       ],
39831       [
39832         "Western Sahara (‫الصحراء الغربية‬‎)",
39833         "eh",
39834         "212",
39835         1
39836       ],
39837       [
39838         "Yemen (‫اليمن‬‎)",
39839         "ye",
39840         "967"
39841       ],
39842       [
39843         "Zambia",
39844         "zm",
39845         "260"
39846       ],
39847       [
39848         "Zimbabwe",
39849         "zw",
39850         "263"
39851       ],
39852       [
39853         "Åland Islands",
39854         "ax",
39855         "358",
39856         1
39857       ]
39858   ];
39859   
39860   return d;
39861 }/**
39862 *    This script refer to:
39863 *    Title: International Telephone Input
39864 *    Author: Jack O'Connor
39865 *    Code version:  v12.1.12
39866 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39867 **/
39868
39869 /**
39870  * @class Roo.bootstrap.PhoneInput
39871  * @extends Roo.bootstrap.TriggerField
39872  * An input with International dial-code selection
39873  
39874  * @cfg {String} defaultDialCode default '+852'
39875  * @cfg {Array} preferedCountries default []
39876   
39877  * @constructor
39878  * Create a new PhoneInput.
39879  * @param {Object} config Configuration options
39880  */
39881
39882 Roo.bootstrap.PhoneInput = function(config) {
39883     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39884 };
39885
39886 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39887         
39888         listWidth: undefined,
39889         
39890         selectedClass: 'active',
39891         
39892         invalidClass : "has-warning",
39893         
39894         validClass: 'has-success',
39895         
39896         allowed: '0123456789',
39897         
39898         max_length: 15,
39899         
39900         /**
39901          * @cfg {String} defaultDialCode The default dial code when initializing the input
39902          */
39903         defaultDialCode: '+852',
39904         
39905         /**
39906          * @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
39907          */
39908         preferedCountries: false,
39909         
39910         getAutoCreate : function()
39911         {
39912             var data = Roo.bootstrap.PhoneInputData();
39913             var align = this.labelAlign || this.parentLabelAlign();
39914             var id = Roo.id();
39915             
39916             this.allCountries = [];
39917             this.dialCodeMapping = [];
39918             
39919             for (var i = 0; i < data.length; i++) {
39920               var c = data[i];
39921               this.allCountries[i] = {
39922                 name: c[0],
39923                 iso2: c[1],
39924                 dialCode: c[2],
39925                 priority: c[3] || 0,
39926                 areaCodes: c[4] || null
39927               };
39928               this.dialCodeMapping[c[2]] = {
39929                   name: c[0],
39930                   iso2: c[1],
39931                   priority: c[3] || 0,
39932                   areaCodes: c[4] || null
39933               };
39934             }
39935             
39936             var cfg = {
39937                 cls: 'form-group',
39938                 cn: []
39939             };
39940             
39941             var input =  {
39942                 tag: 'input',
39943                 id : id,
39944                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39945                 maxlength: this.max_length,
39946                 cls : 'form-control tel-input',
39947                 autocomplete: 'new-password'
39948             };
39949             
39950             var hiddenInput = {
39951                 tag: 'input',
39952                 type: 'hidden',
39953                 cls: 'hidden-tel-input'
39954             };
39955             
39956             if (this.name) {
39957                 hiddenInput.name = this.name;
39958             }
39959             
39960             if (this.disabled) {
39961                 input.disabled = true;
39962             }
39963             
39964             var flag_container = {
39965                 tag: 'div',
39966                 cls: 'flag-box',
39967                 cn: [
39968                     {
39969                         tag: 'div',
39970                         cls: 'flag'
39971                     },
39972                     {
39973                         tag: 'div',
39974                         cls: 'caret'
39975                     }
39976                 ]
39977             };
39978             
39979             var box = {
39980                 tag: 'div',
39981                 cls: this.hasFeedback ? 'has-feedback' : '',
39982                 cn: [
39983                     hiddenInput,
39984                     input,
39985                     {
39986                         tag: 'input',
39987                         cls: 'dial-code-holder',
39988                         disabled: true
39989                     }
39990                 ]
39991             };
39992             
39993             var container = {
39994                 cls: 'roo-select2-container input-group',
39995                 cn: [
39996                     flag_container,
39997                     box
39998                 ]
39999             };
40000             
40001             if (this.fieldLabel.length) {
40002                 var indicator = {
40003                     tag: 'i',
40004                     tooltip: 'This field is required'
40005                 };
40006                 
40007                 var label = {
40008                     tag: 'label',
40009                     'for':  id,
40010                     cls: 'control-label',
40011                     cn: []
40012                 };
40013                 
40014                 var label_text = {
40015                     tag: 'span',
40016                     html: this.fieldLabel
40017                 };
40018                 
40019                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40020                 label.cn = [
40021                     indicator,
40022                     label_text
40023                 ];
40024                 
40025                 if(this.indicatorpos == 'right') {
40026                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40027                     label.cn = [
40028                         label_text,
40029                         indicator
40030                     ];
40031                 }
40032                 
40033                 if(align == 'left') {
40034                     container = {
40035                         tag: 'div',
40036                         cn: [
40037                             container
40038                         ]
40039                     };
40040                     
40041                     if(this.labelWidth > 12){
40042                         label.style = "width: " + this.labelWidth + 'px';
40043                     }
40044                     if(this.labelWidth < 13 && this.labelmd == 0){
40045                         this.labelmd = this.labelWidth;
40046                     }
40047                     if(this.labellg > 0){
40048                         label.cls += ' col-lg-' + this.labellg;
40049                         input.cls += ' col-lg-' + (12 - this.labellg);
40050                     }
40051                     if(this.labelmd > 0){
40052                         label.cls += ' col-md-' + this.labelmd;
40053                         container.cls += ' col-md-' + (12 - this.labelmd);
40054                     }
40055                     if(this.labelsm > 0){
40056                         label.cls += ' col-sm-' + this.labelsm;
40057                         container.cls += ' col-sm-' + (12 - this.labelsm);
40058                     }
40059                     if(this.labelxs > 0){
40060                         label.cls += ' col-xs-' + this.labelxs;
40061                         container.cls += ' col-xs-' + (12 - this.labelxs);
40062                     }
40063                 }
40064             }
40065             
40066             cfg.cn = [
40067                 label,
40068                 container
40069             ];
40070             
40071             var settings = this;
40072             
40073             ['xs','sm','md','lg'].map(function(size){
40074                 if (settings[size]) {
40075                     cfg.cls += ' col-' + size + '-' + settings[size];
40076                 }
40077             });
40078             
40079             this.store = new Roo.data.Store({
40080                 proxy : new Roo.data.MemoryProxy({}),
40081                 reader : new Roo.data.JsonReader({
40082                     fields : [
40083                         {
40084                             'name' : 'name',
40085                             'type' : 'string'
40086                         },
40087                         {
40088                             'name' : 'iso2',
40089                             'type' : 'string'
40090                         },
40091                         {
40092                             'name' : 'dialCode',
40093                             'type' : 'string'
40094                         },
40095                         {
40096                             'name' : 'priority',
40097                             'type' : 'string'
40098                         },
40099                         {
40100                             'name' : 'areaCodes',
40101                             'type' : 'string'
40102                         }
40103                     ]
40104                 })
40105             });
40106             
40107             if(!this.preferedCountries) {
40108                 this.preferedCountries = [
40109                     'hk',
40110                     'gb',
40111                     'us'
40112                 ];
40113             }
40114             
40115             var p = this.preferedCountries.reverse();
40116             
40117             if(p) {
40118                 for (var i = 0; i < p.length; i++) {
40119                     for (var j = 0; j < this.allCountries.length; j++) {
40120                         if(this.allCountries[j].iso2 == p[i]) {
40121                             var t = this.allCountries[j];
40122                             this.allCountries.splice(j,1);
40123                             this.allCountries.unshift(t);
40124                         }
40125                     } 
40126                 }
40127             }
40128             
40129             this.store.proxy.data = {
40130                 success: true,
40131                 data: this.allCountries
40132             };
40133             
40134             return cfg;
40135         },
40136         
40137         initEvents : function()
40138         {
40139             this.createList();
40140             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40141             
40142             this.indicator = this.indicatorEl();
40143             this.flag = this.flagEl();
40144             this.dialCodeHolder = this.dialCodeHolderEl();
40145             
40146             this.trigger = this.el.select('div.flag-box',true).first();
40147             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40148             
40149             var _this = this;
40150             
40151             (function(){
40152                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40153                 _this.list.setWidth(lw);
40154             }).defer(100);
40155             
40156             this.list.on('mouseover', this.onViewOver, this);
40157             this.list.on('mousemove', this.onViewMove, this);
40158             this.inputEl().on("keyup", this.onKeyUp, this);
40159             this.inputEl().on("keypress", this.onKeyPress, this);
40160             
40161             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40162
40163             this.view = new Roo.View(this.list, this.tpl, {
40164                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40165             });
40166             
40167             this.view.on('click', this.onViewClick, this);
40168             this.setValue(this.defaultDialCode);
40169         },
40170         
40171         onTriggerClick : function(e)
40172         {
40173             Roo.log('trigger click');
40174             if(this.disabled){
40175                 return;
40176             }
40177             
40178             if(this.isExpanded()){
40179                 this.collapse();
40180                 this.hasFocus = false;
40181             }else {
40182                 this.store.load({});
40183                 this.hasFocus = true;
40184                 this.expand();
40185             }
40186         },
40187         
40188         isExpanded : function()
40189         {
40190             return this.list.isVisible();
40191         },
40192         
40193         collapse : function()
40194         {
40195             if(!this.isExpanded()){
40196                 return;
40197             }
40198             this.list.hide();
40199             Roo.get(document).un('mousedown', this.collapseIf, this);
40200             Roo.get(document).un('mousewheel', this.collapseIf, this);
40201             this.fireEvent('collapse', this);
40202             this.validate();
40203         },
40204         
40205         expand : function()
40206         {
40207             Roo.log('expand');
40208
40209             if(this.isExpanded() || !this.hasFocus){
40210                 return;
40211             }
40212             
40213             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40214             this.list.setWidth(lw);
40215             
40216             this.list.show();
40217             this.restrictHeight();
40218             
40219             Roo.get(document).on('mousedown', this.collapseIf, this);
40220             Roo.get(document).on('mousewheel', this.collapseIf, this);
40221             
40222             this.fireEvent('expand', this);
40223         },
40224         
40225         restrictHeight : function()
40226         {
40227             this.list.alignTo(this.inputEl(), this.listAlign);
40228             this.list.alignTo(this.inputEl(), this.listAlign);
40229         },
40230         
40231         onViewOver : function(e, t)
40232         {
40233             if(this.inKeyMode){
40234                 return;
40235             }
40236             var item = this.view.findItemFromChild(t);
40237             
40238             if(item){
40239                 var index = this.view.indexOf(item);
40240                 this.select(index, false);
40241             }
40242         },
40243
40244         // private
40245         onViewClick : function(view, doFocus, el, e)
40246         {
40247             var index = this.view.getSelectedIndexes()[0];
40248             
40249             var r = this.store.getAt(index);
40250             
40251             if(r){
40252                 this.onSelect(r, index);
40253             }
40254             if(doFocus !== false && !this.blockFocus){
40255                 this.inputEl().focus();
40256             }
40257         },
40258         
40259         onViewMove : function(e, t)
40260         {
40261             this.inKeyMode = false;
40262         },
40263         
40264         select : function(index, scrollIntoView)
40265         {
40266             this.selectedIndex = index;
40267             this.view.select(index);
40268             if(scrollIntoView !== false){
40269                 var el = this.view.getNode(index);
40270                 if(el){
40271                     this.list.scrollChildIntoView(el, false);
40272                 }
40273             }
40274         },
40275         
40276         createList : function()
40277         {
40278             this.list = Roo.get(document.body).createChild({
40279                 tag: 'ul',
40280                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40281                 style: 'display:none'
40282             });
40283             
40284             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40285         },
40286         
40287         collapseIf : function(e)
40288         {
40289             var in_combo  = e.within(this.el);
40290             var in_list =  e.within(this.list);
40291             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40292             
40293             if (in_combo || in_list || is_list) {
40294                 return;
40295             }
40296             this.collapse();
40297         },
40298         
40299         onSelect : function(record, index)
40300         {
40301             if(this.fireEvent('beforeselect', this, record, index) !== false){
40302                 
40303                 this.setFlagClass(record.data.iso2);
40304                 this.setDialCode(record.data.dialCode);
40305                 this.hasFocus = false;
40306                 this.collapse();
40307                 this.fireEvent('select', this, record, index);
40308             }
40309         },
40310         
40311         flagEl : function()
40312         {
40313             var flag = this.el.select('div.flag',true).first();
40314             if(!flag){
40315                 return false;
40316             }
40317             return flag;
40318         },
40319         
40320         dialCodeHolderEl : function()
40321         {
40322             var d = this.el.select('input.dial-code-holder',true).first();
40323             if(!d){
40324                 return false;
40325             }
40326             return d;
40327         },
40328         
40329         setDialCode : function(v)
40330         {
40331             this.dialCodeHolder.dom.value = '+'+v;
40332         },
40333         
40334         setFlagClass : function(n)
40335         {
40336             this.flag.dom.className = 'flag '+n;
40337         },
40338         
40339         getValue : function()
40340         {
40341             var v = this.inputEl().getValue();
40342             if(this.dialCodeHolder) {
40343                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40344             }
40345             return v;
40346         },
40347         
40348         setValue : function(v)
40349         {
40350             var d = this.getDialCode(v);
40351             
40352             //invalid dial code
40353             if(v.length == 0 || !d || d.length == 0) {
40354                 if(this.rendered){
40355                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40356                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40357                 }
40358                 return;
40359             }
40360             
40361             //valid dial code
40362             this.setFlagClass(this.dialCodeMapping[d].iso2);
40363             this.setDialCode(d);
40364             this.inputEl().dom.value = v.replace('+'+d,'');
40365             this.hiddenEl().dom.value = this.getValue();
40366             
40367             this.validate();
40368         },
40369         
40370         getDialCode : function(v)
40371         {
40372             v = v ||  '';
40373             
40374             if (v.length == 0) {
40375                 return this.dialCodeHolder.dom.value;
40376             }
40377             
40378             var dialCode = "";
40379             if (v.charAt(0) != "+") {
40380                 return false;
40381             }
40382             var numericChars = "";
40383             for (var i = 1; i < v.length; i++) {
40384               var c = v.charAt(i);
40385               if (!isNaN(c)) {
40386                 numericChars += c;
40387                 if (this.dialCodeMapping[numericChars]) {
40388                   dialCode = v.substr(1, i);
40389                 }
40390                 if (numericChars.length == 4) {
40391                   break;
40392                 }
40393               }
40394             }
40395             return dialCode;
40396         },
40397         
40398         reset : function()
40399         {
40400             this.setValue(this.defaultDialCode);
40401             this.validate();
40402         },
40403         
40404         hiddenEl : function()
40405         {
40406             return this.el.select('input.hidden-tel-input',true).first();
40407         },
40408         
40409         // after setting val
40410         onKeyUp : function(e){
40411             this.setValue(this.getValue());
40412         },
40413         
40414         onKeyPress : function(e){
40415             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40416                 e.stopEvent();
40417             }
40418         }
40419         
40420 });
40421 /**
40422  * @class Roo.bootstrap.MoneyField
40423  * @extends Roo.bootstrap.ComboBox
40424  * Bootstrap MoneyField class
40425  * 
40426  * @constructor
40427  * Create a new MoneyField.
40428  * @param {Object} config Configuration options
40429  */
40430
40431 Roo.bootstrap.MoneyField = function(config) {
40432     
40433     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40434     
40435 };
40436
40437 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40438     
40439     /**
40440      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40441      */
40442     allowDecimals : true,
40443     /**
40444      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40445      */
40446     decimalSeparator : ".",
40447     /**
40448      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40449      */
40450     decimalPrecision : 0,
40451     /**
40452      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40453      */
40454     allowNegative : true,
40455     /**
40456      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40457      */
40458     allowZero: true,
40459     /**
40460      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40461      */
40462     minValue : Number.NEGATIVE_INFINITY,
40463     /**
40464      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40465      */
40466     maxValue : Number.MAX_VALUE,
40467     /**
40468      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40469      */
40470     minText : "The minimum value for this field is {0}",
40471     /**
40472      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40473      */
40474     maxText : "The maximum value for this field is {0}",
40475     /**
40476      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40477      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40478      */
40479     nanText : "{0} is not a valid number",
40480     /**
40481      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40482      */
40483     castInt : true,
40484     /**
40485      * @cfg {String} defaults currency of the MoneyField
40486      * value should be in lkey
40487      */
40488     defaultCurrency : false,
40489     /**
40490      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40491      */
40492     thousandsDelimiter : false,
40493     /**
40494      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40495      */
40496     max_length: false,
40497     
40498     inputlg : 9,
40499     inputmd : 9,
40500     inputsm : 9,
40501     inputxs : 6,
40502     
40503     store : false,
40504     
40505     getAutoCreate : function()
40506     {
40507         var align = this.labelAlign || this.parentLabelAlign();
40508         
40509         var id = Roo.id();
40510
40511         var cfg = {
40512             cls: 'form-group',
40513             cn: []
40514         };
40515
40516         var input =  {
40517             tag: 'input',
40518             id : id,
40519             cls : 'form-control roo-money-amount-input',
40520             autocomplete: 'new-password'
40521         };
40522         
40523         var hiddenInput = {
40524             tag: 'input',
40525             type: 'hidden',
40526             id: Roo.id(),
40527             cls: 'hidden-number-input'
40528         };
40529         
40530         if(this.max_length) {
40531             input.maxlength = this.max_length; 
40532         }
40533         
40534         if (this.name) {
40535             hiddenInput.name = this.name;
40536         }
40537
40538         if (this.disabled) {
40539             input.disabled = true;
40540         }
40541
40542         var clg = 12 - this.inputlg;
40543         var cmd = 12 - this.inputmd;
40544         var csm = 12 - this.inputsm;
40545         var cxs = 12 - this.inputxs;
40546         
40547         var container = {
40548             tag : 'div',
40549             cls : 'row roo-money-field',
40550             cn : [
40551                 {
40552                     tag : 'div',
40553                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40554                     cn : [
40555                         {
40556                             tag : 'div',
40557                             cls: 'roo-select2-container input-group',
40558                             cn: [
40559                                 {
40560                                     tag : 'input',
40561                                     cls : 'form-control roo-money-currency-input',
40562                                     autocomplete: 'new-password',
40563                                     readOnly : 1,
40564                                     name : this.currencyName
40565                                 },
40566                                 {
40567                                     tag :'span',
40568                                     cls : 'input-group-addon',
40569                                     cn : [
40570                                         {
40571                                             tag: 'span',
40572                                             cls: 'caret'
40573                                         }
40574                                     ]
40575                                 }
40576                             ]
40577                         }
40578                     ]
40579                 },
40580                 {
40581                     tag : 'div',
40582                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40583                     cn : [
40584                         {
40585                             tag: 'div',
40586                             cls: this.hasFeedback ? 'has-feedback' : '',
40587                             cn: [
40588                                 input
40589                             ]
40590                         }
40591                     ]
40592                 }
40593             ]
40594             
40595         };
40596         
40597         if (this.fieldLabel.length) {
40598             var indicator = {
40599                 tag: 'i',
40600                 tooltip: 'This field is required'
40601             };
40602
40603             var label = {
40604                 tag: 'label',
40605                 'for':  id,
40606                 cls: 'control-label',
40607                 cn: []
40608             };
40609
40610             var label_text = {
40611                 tag: 'span',
40612                 html: this.fieldLabel
40613             };
40614
40615             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40616             label.cn = [
40617                 indicator,
40618                 label_text
40619             ];
40620
40621             if(this.indicatorpos == 'right') {
40622                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40623                 label.cn = [
40624                     label_text,
40625                     indicator
40626                 ];
40627             }
40628
40629             if(align == 'left') {
40630                 container = {
40631                     tag: 'div',
40632                     cn: [
40633                         container
40634                     ]
40635                 };
40636
40637                 if(this.labelWidth > 12){
40638                     label.style = "width: " + this.labelWidth + 'px';
40639                 }
40640                 if(this.labelWidth < 13 && this.labelmd == 0){
40641                     this.labelmd = this.labelWidth;
40642                 }
40643                 if(this.labellg > 0){
40644                     label.cls += ' col-lg-' + this.labellg;
40645                     input.cls += ' col-lg-' + (12 - this.labellg);
40646                 }
40647                 if(this.labelmd > 0){
40648                     label.cls += ' col-md-' + this.labelmd;
40649                     container.cls += ' col-md-' + (12 - this.labelmd);
40650                 }
40651                 if(this.labelsm > 0){
40652                     label.cls += ' col-sm-' + this.labelsm;
40653                     container.cls += ' col-sm-' + (12 - this.labelsm);
40654                 }
40655                 if(this.labelxs > 0){
40656                     label.cls += ' col-xs-' + this.labelxs;
40657                     container.cls += ' col-xs-' + (12 - this.labelxs);
40658                 }
40659             }
40660         }
40661
40662         cfg.cn = [
40663             label,
40664             container,
40665             hiddenInput
40666         ];
40667         
40668         var settings = this;
40669
40670         ['xs','sm','md','lg'].map(function(size){
40671             if (settings[size]) {
40672                 cfg.cls += ' col-' + size + '-' + settings[size];
40673             }
40674         });
40675         
40676         return cfg;
40677     },
40678     
40679     initEvents : function()
40680     {
40681         this.indicator = this.indicatorEl();
40682         
40683         this.initCurrencyEvent();
40684         
40685         this.initNumberEvent();
40686     },
40687     
40688     initCurrencyEvent : function()
40689     {
40690         if (!this.store) {
40691             throw "can not find store for combo";
40692         }
40693         
40694         this.store = Roo.factory(this.store, Roo.data);
40695         this.store.parent = this;
40696         
40697         this.createList();
40698         
40699         this.triggerEl = this.el.select('.input-group-addon', true).first();
40700         
40701         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40702         
40703         var _this = this;
40704         
40705         (function(){
40706             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40707             _this.list.setWidth(lw);
40708         }).defer(100);
40709         
40710         this.list.on('mouseover', this.onViewOver, this);
40711         this.list.on('mousemove', this.onViewMove, this);
40712         this.list.on('scroll', this.onViewScroll, this);
40713         
40714         if(!this.tpl){
40715             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40716         }
40717         
40718         this.view = new Roo.View(this.list, this.tpl, {
40719             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40720         });
40721         
40722         this.view.on('click', this.onViewClick, this);
40723         
40724         this.store.on('beforeload', this.onBeforeLoad, this);
40725         this.store.on('load', this.onLoad, this);
40726         this.store.on('loadexception', this.onLoadException, this);
40727         
40728         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40729             "up" : function(e){
40730                 this.inKeyMode = true;
40731                 this.selectPrev();
40732             },
40733
40734             "down" : function(e){
40735                 if(!this.isExpanded()){
40736                     this.onTriggerClick();
40737                 }else{
40738                     this.inKeyMode = true;
40739                     this.selectNext();
40740                 }
40741             },
40742
40743             "enter" : function(e){
40744                 this.collapse();
40745                 
40746                 if(this.fireEvent("specialkey", this, e)){
40747                     this.onViewClick(false);
40748                 }
40749                 
40750                 return true;
40751             },
40752
40753             "esc" : function(e){
40754                 this.collapse();
40755             },
40756
40757             "tab" : function(e){
40758                 this.collapse();
40759                 
40760                 if(this.fireEvent("specialkey", this, e)){
40761                     this.onViewClick(false);
40762                 }
40763                 
40764                 return true;
40765             },
40766
40767             scope : this,
40768
40769             doRelay : function(foo, bar, hname){
40770                 if(hname == 'down' || this.scope.isExpanded()){
40771                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40772                 }
40773                 return true;
40774             },
40775
40776             forceKeyDown: true
40777         });
40778         
40779         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40780         
40781     },
40782     
40783     initNumberEvent : function(e)
40784     {
40785         this.inputEl().on("keydown" , this.fireKey,  this);
40786         this.inputEl().on("focus", this.onFocus,  this);
40787         this.inputEl().on("blur", this.onBlur,  this);
40788         
40789         this.inputEl().relayEvent('keyup', this);
40790         
40791         if(this.indicator){
40792             this.indicator.addClass('invisible');
40793         }
40794  
40795         this.originalValue = this.getValue();
40796         
40797         if(this.validationEvent == 'keyup'){
40798             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40799             this.inputEl().on('keyup', this.filterValidation, this);
40800         }
40801         else if(this.validationEvent !== false){
40802             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40803         }
40804         
40805         if(this.selectOnFocus){
40806             this.on("focus", this.preFocus, this);
40807             
40808         }
40809         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40810             this.inputEl().on("keypress", this.filterKeys, this);
40811         } else {
40812             this.inputEl().relayEvent('keypress', this);
40813         }
40814         
40815         var allowed = "0123456789";
40816         
40817         if(this.allowDecimals){
40818             allowed += this.decimalSeparator;
40819         }
40820         
40821         if(this.allowNegative){
40822             allowed += "-";
40823         }
40824         
40825         if(this.thousandsDelimiter) {
40826             allowed += ",";
40827         }
40828         
40829         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40830         
40831         var keyPress = function(e){
40832             
40833             var k = e.getKey();
40834             
40835             var c = e.getCharCode();
40836             
40837             if(
40838                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40839                     allowed.indexOf(String.fromCharCode(c)) === -1
40840             ){
40841                 e.stopEvent();
40842                 return;
40843             }
40844             
40845             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40846                 return;
40847             }
40848             
40849             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40850                 e.stopEvent();
40851             }
40852         };
40853         
40854         this.inputEl().on("keypress", keyPress, this);
40855         
40856     },
40857     
40858     onTriggerClick : function(e)
40859     {   
40860         if(this.disabled){
40861             return;
40862         }
40863         
40864         this.page = 0;
40865         this.loadNext = false;
40866         
40867         if(this.isExpanded()){
40868             this.collapse();
40869             return;
40870         }
40871         
40872         this.hasFocus = true;
40873         
40874         if(this.triggerAction == 'all') {
40875             this.doQuery(this.allQuery, true);
40876             return;
40877         }
40878         
40879         this.doQuery(this.getRawValue());
40880     },
40881     
40882     getCurrency : function()
40883     {   
40884         var v = this.currencyEl().getValue();
40885         
40886         return v;
40887     },
40888     
40889     restrictHeight : function()
40890     {
40891         this.list.alignTo(this.currencyEl(), this.listAlign);
40892         this.list.alignTo(this.currencyEl(), this.listAlign);
40893     },
40894     
40895     onViewClick : function(view, doFocus, el, e)
40896     {
40897         var index = this.view.getSelectedIndexes()[0];
40898         
40899         var r = this.store.getAt(index);
40900         
40901         if(r){
40902             this.onSelect(r, index);
40903         }
40904     },
40905     
40906     onSelect : function(record, index){
40907         
40908         if(this.fireEvent('beforeselect', this, record, index) !== false){
40909         
40910             this.setFromCurrencyData(index > -1 ? record.data : false);
40911             
40912             this.collapse();
40913             
40914             this.fireEvent('select', this, record, index);
40915         }
40916     },
40917     
40918     setFromCurrencyData : function(o)
40919     {
40920         var currency = '';
40921         
40922         this.lastCurrency = o;
40923         
40924         if (this.currencyField) {
40925             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40926         } else {
40927             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40928         }
40929         
40930         this.lastSelectionText = currency;
40931         
40932         //setting default currency
40933         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40934             this.setCurrency(this.defaultCurrency);
40935             return;
40936         }
40937         
40938         this.setCurrency(currency);
40939     },
40940     
40941     setFromData : function(o)
40942     {
40943         var c = {};
40944         
40945         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40946         
40947         this.setFromCurrencyData(c);
40948         
40949         var value = '';
40950         
40951         if (this.name) {
40952             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40953         } else {
40954             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40955         }
40956         
40957         this.setValue(value);
40958         
40959     },
40960     
40961     setCurrency : function(v)
40962     {   
40963         this.currencyValue = v;
40964         
40965         if(this.rendered){
40966             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40967             this.validate();
40968         }
40969     },
40970     
40971     setValue : function(v)
40972     {
40973         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40974         
40975         this.value = v;
40976         
40977         if(this.rendered){
40978             
40979             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40980             
40981             this.inputEl().dom.value = (v == '') ? '' :
40982                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40983             
40984             if(!this.allowZero && v === '0') {
40985                 this.hiddenEl().dom.value = '';
40986                 this.inputEl().dom.value = '';
40987             }
40988             
40989             this.validate();
40990         }
40991     },
40992     
40993     getRawValue : function()
40994     {
40995         var v = this.inputEl().getValue();
40996         
40997         return v;
40998     },
40999     
41000     getValue : function()
41001     {
41002         return this.fixPrecision(this.parseValue(this.getRawValue()));
41003     },
41004     
41005     parseValue : function(value)
41006     {
41007         if(this.thousandsDelimiter) {
41008             value += "";
41009             r = new RegExp(",", "g");
41010             value = value.replace(r, "");
41011         }
41012         
41013         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41014         return isNaN(value) ? '' : value;
41015         
41016     },
41017     
41018     fixPrecision : function(value)
41019     {
41020         if(this.thousandsDelimiter) {
41021             value += "";
41022             r = new RegExp(",", "g");
41023             value = value.replace(r, "");
41024         }
41025         
41026         var nan = isNaN(value);
41027         
41028         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41029             return nan ? '' : value;
41030         }
41031         return parseFloat(value).toFixed(this.decimalPrecision);
41032     },
41033     
41034     decimalPrecisionFcn : function(v)
41035     {
41036         return Math.floor(v);
41037     },
41038     
41039     validateValue : function(value)
41040     {
41041         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41042             return false;
41043         }
41044         
41045         var num = this.parseValue(value);
41046         
41047         if(isNaN(num)){
41048             this.markInvalid(String.format(this.nanText, value));
41049             return false;
41050         }
41051         
41052         if(num < this.minValue){
41053             this.markInvalid(String.format(this.minText, this.minValue));
41054             return false;
41055         }
41056         
41057         if(num > this.maxValue){
41058             this.markInvalid(String.format(this.maxText, this.maxValue));
41059             return false;
41060         }
41061         
41062         return true;
41063     },
41064     
41065     validate : function()
41066     {
41067         if(this.disabled || this.allowBlank){
41068             this.markValid();
41069             return true;
41070         }
41071         
41072         var currency = this.getCurrency();
41073         
41074         if(this.validateValue(this.getRawValue()) && currency.length){
41075             this.markValid();
41076             return true;
41077         }
41078         
41079         this.markInvalid();
41080         return false;
41081     },
41082     
41083     getName: function()
41084     {
41085         return this.name;
41086     },
41087     
41088     beforeBlur : function()
41089     {
41090         if(!this.castInt){
41091             return;
41092         }
41093         
41094         var v = this.parseValue(this.getRawValue());
41095         
41096         if(v || v == 0){
41097             this.setValue(v);
41098         }
41099     },
41100     
41101     onBlur : function()
41102     {
41103         this.beforeBlur();
41104         
41105         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41106             //this.el.removeClass(this.focusClass);
41107         }
41108         
41109         this.hasFocus = false;
41110         
41111         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41112             this.validate();
41113         }
41114         
41115         var v = this.getValue();
41116         
41117         if(String(v) !== String(this.startValue)){
41118             this.fireEvent('change', this, v, this.startValue);
41119         }
41120         
41121         this.fireEvent("blur", this);
41122     },
41123     
41124     inputEl : function()
41125     {
41126         return this.el.select('.roo-money-amount-input', true).first();
41127     },
41128     
41129     currencyEl : function()
41130     {
41131         return this.el.select('.roo-money-currency-input', true).first();
41132     },
41133     
41134     hiddenEl : function()
41135     {
41136         return this.el.select('input.hidden-number-input',true).first();
41137     }
41138     
41139 });